diff --git a/CHANGELOG b/CHANGELOG index 24a735fe15..a71c0da70e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Clean private context keyword in RPC +* Add cache section in configuration * Use dualmethod on ModelStorage.save * New API for on_change: instance changes * Add restore_history_before on ModelSQL diff --git a/doc/topics/configuration.rst b/doc/topics/configuration.rst index a85aa33a12..519414a2d7 100644 --- a/doc/topics/configuration.rst +++ b/doc/topics/configuration.rst @@ -135,6 +135,34 @@ table for translations. Default: `en_US` +cache +----- + +Defines size of various cache. + +model +~~~~~ + +The number of different model kept in the cache per transaction. + +Default: `200` + +record +~~~~~~ + +The number of record loaded kept in the cache of the list. +It can be changed locally using the `_record_cache_size` key in +:attr:`Transaction.context`. + +Default: `2000` + +field +~~~~~ + +The number of field to load with an `eager` :attr:`Field.loading`. + +Default: `100` + ssl --- diff --git a/trytond/backend/database.py b/trytond/backend/database.py index dae790850d..6e1599093e 100644 --- a/trytond/backend/database.py +++ b/trytond/backend/database.py @@ -1,6 +1,6 @@ #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 trytond.const import MODEL_CACHE_SIZE +from trytond.config import config DatabaseIntegrityError = None DatabaseOperationalError = None @@ -117,7 +117,8 @@ class CursorInterface(object): context = Transaction().context keys = tuple(((key, context[key]) for key in sorted(self.cache_keys) if key in context)) - return self.cache.setdefault((user, keys), LRUDict(MODEL_CACHE_SIZE)) + return self.cache.setdefault((user, keys), + LRUDict(config.getint('cache', 'model'))) def execute(self, sql, params=None): ''' diff --git a/trytond/config.py b/trytond/config.py index 99e3057874..e1df144733 100644 --- a/trytond/config.py +++ b/trytond/config.py @@ -51,6 +51,10 @@ class TrytonConfigParser(ConfigParser.RawConfigParser): self.set('database', 'list', 'True') self.set('database', 'retry', 5) self.set('database', 'language', 'en_US') + self.add_section('cache') + self.set('cache', 'model', 200) + self.set('cache', 'record', 2000) + self.set('cache', 'field', 100) self.add_section('ssl') self.add_section('email') self.set('email', 'uri', 'smtp://localhost:25') diff --git a/trytond/const.py b/trytond/const.py index 13995b3db7..81f2bdf72a 100644 --- a/trytond/const.py +++ b/trytond/const.py @@ -1,5 +1,6 @@ #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 .config import config OPERATORS = ( 'child_of', @@ -17,7 +18,3 @@ OPERATORS = ( '<', '>', ) - -MODEL_CACHE_SIZE = 200 -RECORD_CACHE_SIZE = 2000 -BROWSE_FIELD_TRESHOLD = 100 diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 73327d2916..256cec2050 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -27,7 +27,7 @@ from ..pyson import PYSONEncoder from ..transaction import Transaction from ..pool import Pool from ..cache import Cache -from ..const import RECORD_CACHE_SIZE +from ..config import config __all__ = ['Translation', 'TranslationSetStart', 'TranslationSetSucceed', 'TranslationSet', @@ -795,7 +795,7 @@ class Translation(ModelSQL, ModelView): raise ValueError('Unknow translation type: %s' % translation.type) key2ids.setdefault(key, []).append(translation.id) - if len(module_translations) <= RECORD_CACHE_SIZE: + if len(module_translations) <= config.getint('cache', 'record'): id2translation[translation.id] = translation def override_translation(ressource_id, new_translation): @@ -829,12 +829,14 @@ class Translation(ModelSQL, ModelView): # Make a first loop to retreive translation ids in the right order to # get better read locality and a full usage of the cache. translation_ids = [] - if len(module_translations) <= RECORD_CACHE_SIZE: + if len(module_translations) <= config.getint('cache', 'record'): processes = (True,) else: processes = (False, True) for processing in processes: - if processing and len(module_translations) > RECORD_CACHE_SIZE: + if (processing + and len(module_translations) > config.getint('cache', + 'record')): id2translation = dict((t.id, t) for t in cls.browse(translation_ids)) for entry in pofile: diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index add50d1b9a..4adf1aace9 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -15,12 +15,15 @@ from trytond.model import ModelStorage, ModelView from trytond.model import fields from trytond import backend from trytond.tools import reduce_ids, grouped_slice -from trytond.const import OPERATORS, RECORD_CACHE_SIZE +from trytond.const import OPERATORS from trytond.transaction import Transaction from trytond.pool import Pool from trytond.cache import LRUDict from trytond.exceptions import ConcurrencyException from trytond.rpc import RPC + +from .modelstorage import cache_size + _RE_UNIQUE = re.compile('UNIQUE\s*\((.*)\)', re.I) _RE_CHECK = re.compile('CHECK\s*\((.*)\)', re.I) @@ -501,7 +504,7 @@ class ModelSQL(ModelStorage): cls.__insert_history(new_ids) records = cls.browse(new_ids) - for sub_records in grouped_slice(records, RECORD_CACHE_SIZE): + for sub_records in grouped_slice(records, cache_size()): cls._validate(sub_records) field_names = cls._fields.keys() @@ -836,7 +839,7 @@ class ModelSQL(ModelStorage): field.set(cls, fname, *fargs) cls.__insert_history(all_ids) - for sub_records in grouped_slice(all_records, RECORD_CACHE_SIZE): + for sub_records in grouped_slice(all_records, cache_size()): cls._validate(sub_records, field_names=all_field_names) cls.trigger_write(trigger_eligibles) @@ -1053,7 +1056,7 @@ class ModelSQL(ModelStorage): rows = cursor.dictfetchmany(cursor.IN_MAX) cache = cursor.get_cache() if cls.__name__ not in cache: - cache[cls.__name__] = LRUDict(RECORD_CACHE_SIZE) + cache[cls.__name__] = LRUDict(cache_size()) delete_records = transaction.delete_records.setdefault(cls.__name__, set()) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index cea94a487f..afb51e7c04 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -20,7 +20,8 @@ from trytond.model import Model from trytond.model import fields from trytond.tools import reduce_domain, memoize from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON -from trytond.const import OPERATORS, RECORD_CACHE_SIZE, BROWSE_FIELD_TRESHOLD +from trytond.const import OPERATORS +from trytond.config import config from trytond.transaction import Transaction from trytond.pool import Pool from trytond.cache import LRUDict, freeze @@ -32,6 +33,11 @@ from .descriptors import dualmethod __all__ = ['ModelStorage'] +def cache_size(): + return Transaction().context.get('_record_cache_size', + config.getint('cache', 'record')) + + class ModelStorage(Model): """ Define a model with storage capability in Tryton. @@ -434,7 +440,7 @@ class ModelStorage(Model): Return a list of instance for the ids ''' ids = map(int, ids) - local_cache = LRUDict(RECORD_CACHE_SIZE) + local_cache = LRUDict(cache_size()) return [cls(int(x), _ids=ids, _local_cache=local_cache) for x in ids] @staticmethod @@ -1151,7 +1157,7 @@ class ModelStorage(Model): if _local_cache is not None: self._local_cache = _local_cache else: - self._local_cache = LRUDict(RECORD_CACHE_SIZE) + self._local_cache = LRUDict(cache_size()) self._local_cache.counter = Transaction().counter super(ModelStorage, self).__init__(id, **kwargs) @@ -1160,7 +1166,7 @@ class ModelStorage(Model): def _cache(self): cache = self._cursor_cache if self.__name__ not in cache: - cache[self.__name__] = LRUDict(RECORD_CACHE_SIZE) + cache[self.__name__] = LRUDict(cache_size()) return cache[self.__name__] def __getattr__(self, name): @@ -1203,7 +1209,7 @@ class ModelStorage(Model): to_remove = set(x for x, y in fread_accesses.iteritems() if not y and x != name) - threshold = BROWSE_FIELD_TRESHOLD + threshold = config.getint('cache', 'field') def not_cached(item): fname, field = item @@ -1284,7 +1290,7 @@ class ModelStorage(Model): with Transaction().set_context(**ctx): key = (Model, freeze(ctx)) local_cache = model2cache.setdefault(key, - LRUDict(RECORD_CACHE_SIZE)) + LRUDict(cache_size())) ids = model2ids.setdefault(key, []) if field._type in ('many2one', 'one2one', 'reference'): ids.append(value) diff --git a/trytond/rpc.py b/trytond/rpc.py index 4fc1f7af5d..98ef8abea2 100644 --- a/trytond/rpc.py +++ b/trytond/rpc.py @@ -32,9 +32,12 @@ class RPC(object): else: context = args.pop() timestamp = None - if '_timestamp' in context: - timestamp = context['_timestamp'] - del context['_timestamp'] + for key in context.keys(): + if key == '_timestamp': + timestamp = context[key] + # Remove all private keyword + if key.startswith('_'): + del context[key] if self.check_access: context['_check_access'] = True if self.instantiate is not None: