diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 0fa4ea56fb..5e05dcafeb 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Allow creating indexes concurrently * Allow skipping index creation * Add assert methods to test tools * Set ELLIPSIS option flag on doctest diff --git a/trytond/doc/ref/backend.rst b/trytond/doc/ref/backend.rst index 303c99c5ab..f9f2816d1b 100644 --- a/trytond/doc/ref/backend.rst +++ b/trytond/doc/ref/backend.rst @@ -304,11 +304,14 @@ TableHandler Drop the named ``ident`` constraint. ``table`` can be used to alter another table. -.. method:: TableHandler.set_indexes(indexes) +.. method:: TableHandler.set_indexes(indexes[, concurrently]) Create the :class:`indexes ` if possible and drop others having ``idx_`` as prefix or ``_index`` as suffix. + If ``concurrently`` is set indexes are created concurrently if possible. + It defaults to ``False``. + .. method:: TableHandler.drop_column(column_name) Drop the named column. diff --git a/trytond/trytond/backend/postgresql/table.py b/trytond/trytond/backend/postgresql/table.py index b11dad6bec..2a677f606c 100644 --- a/trytond/trytond/backend/postgresql/table.py +++ b/trytond/trytond/backend/postgresql/table.py @@ -494,7 +494,7 @@ class TableHandler(TableHandlerInterface): Identifier(self.table_name), Identifier(ident))) self._update_definitions(constraints=True) - def set_indexes(self, indexes): + def set_indexes(self, indexes, concurrently=False): cursor = Transaction().connection.cursor() old = set(self._indexes) for index in indexes: @@ -504,7 +504,18 @@ class TableHandler(TableHandlerInterface): name = '_'.join([self.table_name, name]) name = 'idx_' + self.convert_name(name, reserved=len('idx_')) cursor.execute( - SQL('CREATE INDEX IF NOT EXISTS {} ON {} USING {}').format( + 'SELECT idx.indisvalid ' + 'FROM pg_index idx ' + 'JOIN pg_class cls ON cls.oid = idx.indexrelid ' + 'WHERE cls.relname = %s', + (name,)) + if (idx_valid := cursor.fetchone()) and not idx_valid[0]: + cursor.execute( + SQL("DROP INDEX {}").format(Identifier(name))) + cursor.execute( + SQL('CREATE INDEX {} IF NOT EXISTS {} ON {} USING {}') + .format( + SQL('CONCURRENTLY' if concurrently else ''), Identifier(name), Identifier(self.table_name), query), diff --git a/trytond/trytond/backend/sqlite/table.py b/trytond/trytond/backend/sqlite/table.py index b2af7da8c7..8558c1361a 100644 --- a/trytond/trytond/backend/sqlite/table.py +++ b/trytond/trytond/backend/sqlite/table.py @@ -278,7 +278,7 @@ class TableHandler(TableHandlerInterface): def drop_constraint(self, ident, table=None): warnings.warn('Unable to drop constraint with SQLite backend') - def set_indexes(self, indexes): + def set_indexes(self, indexes, concurrently=False): cursor = Transaction().connection.cursor() old = set(self._indexes) for index in indexes: diff --git a/trytond/trytond/backend/table.py b/trytond/trytond/backend/table.py index d862ca35aa..fd252f3d5d 100644 --- a/trytond/trytond/backend/table.py +++ b/trytond/trytond/backend/table.py @@ -107,7 +107,7 @@ class TableHandlerInterface(object): name = hashlib.sha256(name).hexdigest()[:length - 1] return name - def set_indexes(self, indexes): + def set_indexes(self, indexes, concurrently=False): raise NotImplementedError def index_translator_for(self, index): diff --git a/trytond/trytond/model/modelsql.py b/trytond/trytond/model/modelsql.py index 70d2acfb05..b4ec568dbe 100644 --- a/trytond/trytond/model/modelsql.py +++ b/trytond/trytond/model/modelsql.py @@ -516,7 +516,7 @@ class ModelSQL(ModelStorage): [h_table.write_date], [None])) @classmethod - def _update_sql_indexes(cls): + def _update_sql_indexes(cls, concurrently=False): def no_subset(index): for j in cls._sql_indexes: if j != index and index < j: @@ -525,11 +525,11 @@ class ModelSQL(ModelStorage): if not callable(cls.table_query): table_h = cls.__table_handler__() indexes = filter(no_subset, cls._sql_indexes) - table_h.set_indexes(indexes) + table_h.set_indexes(indexes, concurrently=concurrently) if cls._history: history_th = cls.__table_handler__(history=True) indexes = filter(no_subset, cls._history_sql_indexes) - history_th.set_indexes(indexes) + history_th.set_indexes(indexes, concurrently=concurrently) @classmethod def _update_history_table(cls): diff --git a/trytond/trytond/modules/__init__.py b/trytond/trytond/modules/__init__.py index d27ddac3e5..3fe48f0d61 100644 --- a/trytond/trytond/modules/__init__.py +++ b/trytond/trytond/modules/__init__.py @@ -246,13 +246,16 @@ def load_module_graph(graph, pool, update=None, lang=None, indexes=True): pool.setup_mixin() + def create_indexes(concurrently): + for model_name in models_with_indexes: + model = pool.get(model_name) + if model._sql_indexes: + logger.info('index:update %s', model_name) + model._update_sql_indexes(concurrently=concurrently) + if update: if indexes: - for model_name in models_with_indexes: - model = pool.get(model_name) - if model._sql_indexes: - logger.info('index:create %s', model_name) - model._update_sql_indexes() + create_indexes(concurrently=False) transaction.commit() else: logger.info('index:skipping indexes creation') @@ -262,6 +265,9 @@ def load_module_graph(graph, pool, update=None, lang=None, indexes=True): logger.info('history:update %s', model.__name__) model._update_history_table() transaction.commit() + elif indexes: + with transaction.new_transaction(autocommit=True): + create_indexes(concurrently=True) # Vacuum : while modules_todo: