diff --git a/CHANGELOG b/CHANGELOG index 13bd36d1d0..2a3028d62f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Manage Cache in Transaction * Allow to register multiple exit functions on Transaction * Return 429 status when too many login attempts * Add set_rpc on Field diff --git a/doc/ref/cache.rst b/doc/ref/cache.rst index 83ab6858a5..3cfeab1f92 100644 --- a/doc/ref/cache.rst +++ b/doc/ref/cache.rst @@ -13,6 +13,8 @@ be unique and it's used to identify the cache. We usually use be used to limit the number of values cached and the `context` parameter is used to indicate if the cache depends on the user context and is true by default. +The cache is cleaned on :class:`Transaction` starts and resets on +:class:`Transaction` commit or rollback. .. warning:: As there is no deepcopy of the values cached, they must never be mutated diff --git a/trytond/cache.py b/trytond/cache.py index a7845480a8..e760e8d59e 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -105,7 +105,7 @@ class MemoryCache(BaseCache): @staticmethod def clean(dbname): - with Transaction().new_transaction() as transaction,\ + with Transaction().new_transaction(_nocache=True) as transaction,\ transaction.connection.cursor() as cursor: table = Table('ir_cache') cursor.execute(*table.select(table.timestamp, table.name)) @@ -129,7 +129,7 @@ class MemoryCache(BaseCache): @staticmethod def resets(dbname): table = Table('ir_cache') - with Transaction().new_transaction() as transaction,\ + with Transaction().new_transaction(_nocache=True) as transaction,\ transaction.connection.cursor() as cursor,\ Cache._resets_lock: Cache._resets.setdefault(dbname, set()) diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py index 0dfa2f5cc4..3872d9f1d1 100644 --- a/trytond/ir/cron.py +++ b/trytond/ir/cron.py @@ -14,7 +14,6 @@ from ..transaction import Transaction from ..pool import Pool from .. import backend from ..config import config -from ..cache import Cache from ..sendmail import sendmail __all__ = [ @@ -181,7 +180,6 @@ class Cron(ModelSQL, ModelView): def run(cls, db_name): now = datetime.datetime.now() with Transaction().start(db_name, 0) as transaction: - Cache.clean(db_name) transaction.database.lock(transaction.connection, cls._table) crons = cls.search([ ('number_calls', '!=', 0), @@ -214,4 +212,3 @@ class Cron(ModelSQL, ModelView): except Exception: transaction.rollback() logger.error('Running cron %s', cron.id, exc_info=True) - Cache.resets(db_name) diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index 2a06a7c034..b25506fcae 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -17,7 +17,6 @@ from sql.functions import CurrentTimestamp import trytond.tools as tools from trytond.config import config from trytond.transaction import Transaction -from trytond.cache import Cache from trytond import backend import trytond.convert as convert @@ -446,7 +445,6 @@ def load_modules(database_name, pool, update=None, lang=None): Module.update_list() # Need to commit to unlock SQLite database transaction.commit() - Cache.resets(database_name) if not Transaction().connection: with Transaction().start(database_name, 0): diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py index 0fb1e5f1c1..59a254e8c7 100644 --- a/trytond/protocols/dispatcher.py +++ b/trytond/protocols/dispatcher.py @@ -13,7 +13,6 @@ from trytond import backend from trytond.config import config from trytond import __version__ from trytond.transaction import Transaction -from trytond.cache import Cache from trytond.exceptions import ( UserError, UserWarning, ConcurrencyException, LoginException, RateLimitException) @@ -57,9 +56,6 @@ def login(request, database_name, user, parameters, language=None): except RateLimitException: session = None code = 429 - with Transaction().start(database_name, 0): - Cache.clean(database_name) - Cache.resets(database_name) msg = 'successful login' if session else 'bad login or password' logger.info('%s \'%s\' from %s using %s on database \'%s\'', msg, user, request.remote_addr, request.scheme, database_name) @@ -156,7 +152,6 @@ def _dispatch(request, pool, *args, **kwargs): for count in range(config.getint('database', 'retry'), -1, -1): with Transaction().start(pool.database_name, user, readonly=rpc.readonly) as transaction: - Cache.clean(pool.database_name) try: c_args, c_kwargs, transaction.context, transaction.timestamp \ = rpc.convert(obj, *args, **kwargs) @@ -187,7 +182,6 @@ def _dispatch(request, pool, *args, **kwargs): raise # Need to commit to unlock SQLite database transaction.commit() - Cache.resets(pool.database_name) if request.authorization.type == 'session': try: with Transaction().start(pool.database_name, 0) as transaction: diff --git a/trytond/protocols/wrappers.py b/trytond/protocols/wrappers.py index ae20fe373f..cec57e73df 100644 --- a/trytond/protocols/wrappers.py +++ b/trytond/protocols/wrappers.py @@ -16,7 +16,6 @@ from trytond import security, backend from trytond.pool import Pool from trytond.transaction import Transaction from trytond.config import config -from trytond.cache import Cache logger = logging.getLogger(__name__) @@ -123,7 +122,6 @@ def with_transaction(readonly=None): pool.database_name, 0, readonly=readonly_ ) as transaction: try: - Cache.clean(pool.database_name) result = func(request, pool, *args, **kwargs) except DatabaseOperationalError: if count and not readonly_: @@ -136,7 +134,6 @@ def with_transaction(readonly=None): raise # Need to commit to unlock SQLite database transaction.commit() - Cache.resets(pool.database_name) return result return wrapper return decorator diff --git a/trytond/transaction.py b/trytond/transaction.py index ad1a1620ee..1e7e321afb 100644 --- a/trytond/transaction.py +++ b/trytond/transaction.py @@ -73,7 +73,7 @@ class Transaction(object): LRUDict(config.getint('cache', 'model'))) def start(self, database_name, user, readonly=False, context=None, - close=False, autocommit=False): + close=False, autocommit=False, _nocache=False): ''' Start transaction ''' @@ -100,6 +100,10 @@ class Transaction(object): self.timestamp = {} self.counter = 0 self._datamanagers = [] + self._nocache = _nocache + if not _nocache: + from trytond.cache import Cache + Cache.clean(database.name) return self def __enter__(self): @@ -170,11 +174,12 @@ class Transaction(object): self._local.transactions.append(transaction) return transaction - def new_transaction(self, autocommit=False, readonly=False): + def new_transaction(self, autocommit=False, readonly=False, + _nocache=False): transaction = Transaction(new=True) return transaction.start(self.database.name, self.user, context=self.context, close=self.close, readonly=readonly, - autocommit=autocommit) + autocommit=autocommit, _nocache=_nocache) def commit(self): try: @@ -197,6 +202,9 @@ class Transaction(object): logger.critical('A datamanager raised an exception in' ' tpc_finish, the data might be inconsistant', exc_info=True) + if not self._nocache: + from trytond.cache import Cache + Cache.resets(self.database.name) def rollback(self): for cache in self.cache.itervalues(): @@ -204,6 +212,9 @@ class Transaction(object): for datamanager in self._datamanagers: datamanager.tpc_abort(self) self.connection.rollback() + if not self._nocache: + from trytond.cache import Cache + Cache.resets(self.database.name) def join(self, datamanager): try: