From ed552f357eaac08ce692b1fb9cea5afe2bbd9c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Mar 2024 19:29:54 +0100 Subject: [PATCH] Add size constraint on RPC cache --- tryton/tryton/cache.py | 34 ++++++++++++++++++++++++++++++++++ tryton/tryton/common/common.py | 23 +---------------------- tryton/tryton/config.py | 1 + tryton/tryton/jsonrpc.py | 9 +++++++-- 4 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 tryton/tryton/cache.py diff --git a/tryton/tryton/cache.py b/tryton/tryton/cache.py new file mode 100644 index 0000000000..c68b6a4462 --- /dev/null +++ b/tryton/tryton/cache.py @@ -0,0 +1,34 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. + +from collections import OrderedDict + + +class CacheDict(OrderedDict): + + def __init__(self, *args, cache_len=10, default_factory=None, **kwargs): + assert cache_len > 0 + self.cache_len = cache_len + self.default_factory = default_factory + + super().__init__(*args, **kwargs) + + def __setitem__(self, key, value): + super().__setitem__(key, value) + self.move_to_end(key) + + while len(self) > self.cache_len: + oldkey = next(iter(self)) + self.__delitem__(oldkey) + + def __getitem__(self, key): + value = super().__getitem__(key) + self.move_to_end(key) + return value + + def __missing__(self, key): + if self.default_factory is None: + raise KeyError(key) + value = self.default_factory() + self[key] = value + return value diff --git a/tryton/tryton/common/common.py b/tryton/tryton/common/common.py index 48bf7af704..d2e72bc6f5 100644 --- a/tryton/tryton/common/common.py +++ b/tryton/tryton/common/common.py @@ -37,6 +37,7 @@ from string import Template from threading import Lock, Thread import tryton.rpc as rpc +from tryton.cache import CacheDict from tryton.config import CONFIG, PIXMAPS_DIR, SOUNDS_DIR, TRYTON_ICON try: @@ -59,28 +60,6 @@ _ = gettext.gettext logger = logging.getLogger(__name__) -class CacheDict(OrderedDict): - - def __init__(self, *args, cache_len=10, **kwargs): - assert cache_len > 0 - self.cache_len = cache_len - - super().__init__(*args, **kwargs) - - def __setitem__(self, key, value): - super().__setitem__(key, value) - self.move_to_end(key) - - while len(self) > self.cache_len: - oldkey = next(iter(self)) - self.__delitem__(oldkey) - - def __getitem__(self, key): - value = super().__getitem__(key) - self.move_to_end(key) - return value - - class IconFactory: batchnum = 10 diff --git a/tryton/tryton/config.py b/tryton/tryton/config.py index fb1909be18..99b901db67 100644 --- a/tryton/tryton/config.py +++ b/tryton/tryton/config.py @@ -81,6 +81,7 @@ class ConfigManager(object): 'login.db': demo_database, 'login.expanded': False, 'client.title': 'Tryton', + 'rpc.cache_size': 1024, 'client.modepda': False, 'client.toolbar': 'default', 'client.save_tree_width': True, diff --git a/tryton/tryton/jsonrpc.py b/tryton/tryton/jsonrpc.py index 1a9cfe196b..2f6ca2a761 100644 --- a/tryton/tryton/jsonrpc.py +++ b/tryton/tryton/jsonrpc.py @@ -12,12 +12,14 @@ import socket import ssl import threading import xmlrpc.client -from collections import defaultdict from contextlib import contextmanager from decimal import Decimal from functools import partial, reduce from urllib.parse import quote, urljoin +from .cache import CacheDict +from .config import CONFIG + __all__ = ["ResponseError", "Fault", "ProtocolError", "Transport", "ServerProxy", "ServerPool"] CONNECT_TIMEOUT = 5 @@ -401,7 +403,10 @@ class ServerPool(object): class _Cache: def __init__(self): - self.store = defaultdict(dict) + cache_size = CONFIG['rpc.cache_size'] + self.store = CacheDict( + cache_len=cache_size, + default_factory=lambda: CacheDict(cache_len=cache_size)) def cached(self, prefix): return prefix in self.store