From 78b2811fffa7d6c3de5899686ea16931f33accdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 10 Jul 2018 17:03:49 +0200 Subject: [PATCH] Prevent Cache to start too much new transactions resets starts a transaction only if there is something to store. clean is run at best every 5 minutes by default. issue7533 review64261002 --- CHANGELOG | 1 + doc/topics/configuration.rst | 7 +++++++ trytond/cache.py | 37 ++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 24929b00d1..e86e29083d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add cache clean timeout configuration * Implicit conversion to boolean in PYSON statement * Add domain to dictionary schema * Fill main language in ir.configuration at database initialization diff --git a/doc/topics/configuration.rst b/doc/topics/configuration.rst index 9a94632015..17a681858d 100644 --- a/doc/topics/configuration.rst +++ b/doc/topics/configuration.rst @@ -172,6 +172,13 @@ The number of field to load with an `eager` :attr:`Field.loading`. Default: `100` +clean_timeout +~~~~~~~~~~~~~ + +The minimum number of seconds between two cleanings of the cache. + +Default: `300` + table ----- diff --git a/trytond/cache.py b/trytond/cache.py index a1bffe7878..17e1890cdd 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -2,6 +2,7 @@ # this repository contains the full copyright notices and license terms. from threading import Lock from collections import OrderedDict +from datetime import datetime from sql import Table from sql.functions import CurrentTimestamp @@ -11,6 +12,7 @@ from trytond.transaction import Transaction from trytond.tools import resolve __all__ = ['BaseCache', 'Cache', 'LRUDict'] +_clear_timeout = config.getint('cache', 'clean_timeout', default=5 * 60) def freeze(o): @@ -68,6 +70,7 @@ class MemoryCache(BaseCache): """ _resets = {} _resets_lock = Lock() + _clean_last = datetime.now() def __init__(self, name, size_limit=1024, context=True): super(MemoryCache, self).__init__(name, size_limit, context) @@ -103,8 +106,10 @@ class MemoryCache(BaseCache): with self._lock: self._cache[dbname] = LRUDict(self.size_limit) - @staticmethod - def clean(dbname): + @classmethod + def clean(cls, dbname): + if (datetime.now() - cls._clean_last).total_seconds() < _clear_timeout: + return with Transaction().new_transaction(_nocache=True) as transaction,\ transaction.connection.cursor() as cursor: table = Table('ir_cache') @@ -112,28 +117,31 @@ class MemoryCache(BaseCache): timestamps = {} for timestamp, name in cursor.fetchall(): timestamps[name] = timestamp - for inst in Cache._cache_instance: + for inst in cls._cache_instance: if inst._name in timestamps: with inst._lock: if (not inst._timestamp or timestamps[inst._name] > inst._timestamp): inst._timestamp = timestamps[inst._name] inst._cache[dbname] = LRUDict(inst.size_limit) + cls._clean_last = datetime.now() - @staticmethod - def reset(dbname, name): - with Cache._resets_lock: - Cache._resets.setdefault(dbname, set()) - Cache._resets[dbname].add(name) + @classmethod + def reset(cls, dbname, name): + with cls._resets_lock: + cls._resets.setdefault(dbname, set()) + cls._resets[dbname].add(name) - @staticmethod - def resets(dbname): + @classmethod + def resets(cls, dbname): table = Table('ir_cache') + resets = cls._resets.setdefault(dbname, set()) + if not resets: + return with Transaction().new_transaction(_nocache=True) as transaction,\ transaction.connection.cursor() as cursor,\ - Cache._resets_lock: - Cache._resets.setdefault(dbname, set()) - for name in Cache._resets[dbname]: + cls._resets_lock: + for name in resets: cursor.execute(*table.select(table.name, where=table.name == name, limit=1)) @@ -146,13 +154,14 @@ class MemoryCache(BaseCache): cursor.execute(*table.insert( [table.timestamp, table.name], [[CurrentTimestamp(), name]])) - Cache._resets[dbname].clear() + resets.clear() @classmethod def drop(cls, dbname): for inst in cls._cache_instance: inst._cache.pop(dbname, None) + if config.get('cache', 'class'): Cache = resolve(config.get('cache', 'class')) else: