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
This commit is contained in:
Cédric Krier
2021-01-20 00:52:07 +01:00
parent c6fc31ecee
commit c587d74dc4
5 changed files with 46 additions and 8 deletions
+1
View File
@@ -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
+30 -1
View File
@@ -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]
+4 -1
View File
@@ -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'
+1
View File
@@ -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')
+10 -6
View File
@@ -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()