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
This commit is contained in:
Cédric Krier
2018-07-10 17:03:49 +02:00
parent 9681897041
commit 78b2811fff
3 changed files with 31 additions and 14 deletions
+1
View File
@@ -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
+7
View File
@@ -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
-----
+23 -14
View File
@@ -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: