From 0d0cd7ee4547c20ea81f54bb6a050c47e9791ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 7 Mar 2023 15:43:53 +0100 Subject: [PATCH] Keep all transaction caches still referenced The LRUDict is replaced by a WeakValueDictionary to keep all used transaction caches such as they can be cleared by ModelStorage. But we still keep the latest cache used so they are not removed from the WeakValueDictionary too soon when such cache has been created to be pre-filled like in ModelSQL.search. Closes #12119 --- trytond/trytond/transaction.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/trytond/trytond/transaction.py b/trytond/trytond/transaction.py index 19e68f4671..be9f6816a3 100644 --- a/trytond/trytond/transaction.py +++ b/trytond/trytond/transaction.py @@ -2,9 +2,10 @@ # this repository contains the full copyright notices and license terms. import logging import time -from collections import defaultdict +from collections import defaultdict, deque from functools import wraps from threading import local +from weakref import WeakValueDictionary from sql import Flavor @@ -105,8 +106,6 @@ class Transaction(object): cache_keys = {'language', 'fuzzy_translation', '_datetime'} def __new__(cls, new=False): - from trytond.cache import LRUDict - from trytond.pool import Pool transactions = cls._local.transactions if new or not transactions: instance = super(Transaction, cls).__new__(cls) @@ -122,14 +121,8 @@ class Transaction(object): instance.check_warnings = None instance.timestamp = None instance.started_at = None - instance.cache = LRUDict( - _cache_transaction, - lambda: LRUDict( - _cache_model, - lambda name: LRUDict( - record_cache_size(instance), - Pool().get(name)._record), - default_factory_with_key=True)) + instance.cache = WeakValueDictionary() + instance._cache_deque = deque(maxlen=_cache_transaction) instance._atexit = [] transactions.append(instance) else: @@ -148,10 +141,21 @@ class Transaction(object): return self._local.tasks def get_cache(self): + from trytond.cache import LRUDict + from trytond.pool import Pool keys = tuple(((key, self.context[key]) for key in sorted(self.cache_keys) if key in self.context)) - return self.cache[(self.user, keys)] + cache = self.cache.setdefault( + (self.user, keys), LRUDict( + _cache_model, + lambda name: LRUDict( + record_cache_size(self), + Pool().get(name)._record), + default_factory_with_key=True)) + # Keep last used cache references to allow to pre-fill them + self._cache_deque.append(cache) + return cache def start(self, database_name, user, readonly=False, context=None, close=False, autocommit=False, **extras):