From c587d74dc4d59afac53e09393216f4013deddcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 20 Jan 2021 00:52:07 +0100 Subject: [PATCH] Refresh pool of other processes When a process change the list of activated modules, the others must refresh their pool. We do that by just stopping the pool for that database, the next request will load the new pool. issue8988 review302861002 --- CHANGELOG | 1 + trytond/cache.py | 31 ++++++++++++++++++++++++++++++- trytond/ir/module.py | 5 ++++- trytond/modules/__init__.py | 1 + trytond/pool.py | 16 ++++++++++------ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0ac3237dce..80bf81c732 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Refresh pool of other processes * Add clear_all method to Cache * Support Genshi element directives in HTML editor * Add firstline tool diff --git a/trytond/cache.py b/trytond/cache.py index 96107a97fd..6475a590fc 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -16,6 +16,7 @@ from sql.functions import CurrentTimestamp, Function from trytond import backend from trytond.config import config +from trytond.pool import Pool from trytond.transaction import Transaction from trytond.tools import resolve, grouped_slice @@ -43,6 +44,15 @@ def freeze(o): return o +def _get_modules(cursor): + ir_module = Table('ir_module') + cursor.execute(*ir_module.select( + ir_module.name, + where=ir_module.state.in_( + ['activated', 'to upgrade', 'to remove']))) + return {m for m, in cursor} + + class BaseCache(object): _instances = {} @@ -195,6 +205,7 @@ class MemoryCache(BaseCache): timestamps = {} for timestamp, name in cursor.fetchall(): timestamps[name] = timestamp + modules = _get_modules(cursor) finally: database.put_connection(connection) for name, timestamp in timestamps.items(): @@ -205,6 +216,7 @@ class MemoryCache(BaseCache): inst_timestamp = inst._timestamp.get(dbname) if not inst_timestamp or timestamp > inst_timestamp: inst._clear(dbname, timestamp) + Pool(dbname).refresh(modules) cls._clean_last = datetime.now() def sync_since(self, value): @@ -291,6 +303,21 @@ class MemoryCache(BaseCache): inst._database_cache.pop(dbname, None) inst._transaction_lower.pop(dbname, None) + @classmethod + def refresh_pool(cls, transaction): + database = transaction.database + dbname = database.name + if not _clear_timeout and database.has_channel(): + database = backend.Database(dbname) + conn = database.get_connection() + try: + cursor = conn.cursor() + cursor.execute( + 'NOTIFY "%s", %%s' % cls._channel, ('refresh pool',)) + conn.commit() + finally: + database.put_connection(conn) + @classmethod def _listen(cls, dbname): database = backend.Database(dbname) @@ -313,7 +340,9 @@ class MemoryCache(BaseCache): conn.poll() while conn.notifies: notification = conn.notifies.pop() - if notification.payload: + if notification.payload == 'refresh pool': + Pool(dbname).refresh(_get_modules(cursor)) + elif notification.payload: reset = json.loads(notification.payload) for name in reset: inst = cls._instances[name] diff --git a/trytond/ir/module.py b/trytond/ir/module.py index b359a7e2a0..4d28b6da2c 100644 --- a/trytond/ir/module.py +++ b/trytond/ir/module.py @@ -5,6 +5,7 @@ from functools import wraps from sql.operators import NotIn +from trytond.cache import Cache from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.model import ModelView, ModelSQL, fields, Unique, sequence_ordered @@ -557,7 +558,8 @@ class ModuleActivateUpgrade(Wizard): pool = Pool() Module = pool.get('ir.module') Lang = pool.get('ir.lang') - with Transaction().new_transaction(): + transaction = Transaction() + with transaction.new_transaction(): modules = Module.search([ ('state', 'in', ['to upgrade', 'to remove', 'to activate']), ]) @@ -568,6 +570,7 @@ class ModuleActivateUpgrade(Wizard): lang = [x.code for x in langs] if update: pool.init(update=update, lang=lang) + Cache.refresh_pool(transaction) return 'done' diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index 6fad3d12d5..01524a0773 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -302,6 +302,7 @@ def load_module_graph(graph, pool, update=None, lang=None): # Ensure cache is clear for other instances Cache.clear_all() + Cache.refresh_pool(transaction) logger.info('all modules loaded') diff --git a/trytond/pool.py b/trytond/pool.py index 913096ed8a..b85020eb0c 100644 --- a/trytond/pool.py +++ b/trytond/pool.py @@ -113,12 +113,12 @@ class Pool(object): with cls._lock: if database_name in cls._instances: del cls._instances[database_name] - lock = cls._locks.get(database_name) - if not lock: - return - with lock: - if database_name in cls._pool: - del cls._pool[database_name] + lock = cls._locks.get(database_name) + if not lock: + return + with lock: + if database_name in cls._pool: + del cls._pool[database_name] @classmethod def database_list(cls): @@ -271,6 +271,10 @@ class Pool(object): cls.__name__, (mixin, cls), {'__slots__': ()}) self.add(cls, type=type_) + def refresh(self, modules): + if set(self._modules) != modules: + self.stop(self.database_name) + def isregisteredby(obj, module, type_='model'): pool = Pool()