From eb820df5355069f0e0f20b203da63ccd9c02b99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Dec 2012 16:15:24 +0100 Subject: [PATCH] Use globale cache also for Many2One and Reference fields review590007 --- trytond/ir/action.py | 41 ++++++++++++++--------- trytond/model/modelsql.py | 7 ++-- trytond/model/modelstorage.py | 63 ++++++++++++++++++++--------------- trytond/res/user.py | 6 ++++ 4 files changed, 72 insertions(+), 45 deletions(-) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 69fb47b52d..4e9689eb0d 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -522,10 +522,12 @@ class ActionReport(ModelSQL, ModelView): if cursor.nextid(cls._table): cursor.setnextid(cls._table, cursor.currid('ir_action')) report = super(ActionReport, cls).create(vals) - new_id = report.action.id - cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \ - 'WHERE id = %s', (report.action.id, report.id)) - cursor.update_auto_increment(cls._table, report.action.id) + cursor.execute('SELECT action FROM "' + cls._table + '" ' + 'WHERE id = %s', (report.id,)) + new_id, = cursor.fetchone() + cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' + 'WHERE id = %s', (new_id, report.id)) + cursor.update_auto_increment(cls._table, new_id) report = cls(new_id) cls.write([report], later) return report @@ -734,10 +736,12 @@ class ActionActWindow(ModelSQL, ModelView): if cursor.nextid(cls._table): cursor.setnextid(cls._table, cursor.currid('ir_action')) act_window = super(ActionActWindow, cls).create(vals) - new_id = act_window.action.id - cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \ - 'WHERE id = %s', (act_window.action.id, act_window.id)) - cursor.update_auto_increment(cls._table, act_window.action.id) + cursor.execute('SELECT action FROM "' + cls._table + '" ' + 'WHERE id = %s', (act_window.id,)) + new_id, = cursor.fetchone() + cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' + 'WHERE id = %s', (new_id, act_window.id)) + cursor.update_auto_increment(cls._table, new_id) act_window = cls(new_id) cls.write([act_window], later) return act_window @@ -874,10 +878,12 @@ class ActionWizard(ModelSQL, ModelView): if cursor.nextid(cls._table): cursor.setnextid(cls._table, cursor.currid('ir_action')) wizard = super(ActionWizard, cls).create(vals) - new_id = wizard.action.id - cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \ - 'WHERE id = %s', (wizard.action.id, wizard.id)) - cursor.update_auto_increment(cls._table, wizard.action.id) + cursor.execute('SELECT action FROM "' + cls._table + '" ' + 'WHERE id = %s', (wizard.id,)) + new_id, = cursor.fetchone() + cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' + 'WHERE id = %s', (new_id, wizard.id)) + cursor.update_auto_increment(cls._table, new_id) wizard = cls(new_id) cls.write([wizard], later) return wizard @@ -943,10 +949,13 @@ class ActionURL(ModelSQL, ModelView): if cursor.nextid(cls._table): cursor.setnextid(cls._table, cursor.currid('ir_action')) url = super(ActionURL, cls).create(vals) - new_id = url.action.id - cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \ - 'WHERE id = %s', (url.action.id, url.id)) - cursor.update_auto_increment(cls._table, url.action.id) + cursor.execute('SELECT action FROM "' + cls._table + '" ' + 'WHERE id = %s', (url.id,)) + new_id, = cursor.fetchone() + cls.write([url], {}) # simulate write to clear the cache + cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' + 'WHERE id = %s', (new_id, url.id)) + cursor.update_auto_increment(cls._table, new_id) url = cls(new_id) cls.write([url], later) return url diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index e871a26cb7..1e1cdc48c9 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -1209,21 +1209,22 @@ class ModelSQL(ModelStorage): rows = cursor.dictfetchmany(cursor.IN_MAX) cache = cursor.get_cache(Transaction().context) - cache.setdefault(cls.__name__, LRUDict(RECORD_CACHE_SIZE)) + if cls.__name__ not in cache: + cache[cls.__name__] = LRUDict(RECORD_CACHE_SIZE) delete_records = Transaction().delete_records.setdefault(cls.__name__, set()) keys = None for data in islice(rows, 0, cache.size_limit): if data['id'] in delete_records: continue - if not keys: + if keys is None: keys = data.keys() for k in keys[:]: if k in ('_timestamp', '_datetime'): keys.remove(k) continue field = cls._fields[k] - if field._type not in ('many2one',): + if not getattr(field, 'datetime_field', None): keys.remove(k) continue for k in keys: diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index f1096903de..61519ce96d 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1154,14 +1154,6 @@ class ModelStorage(Model): if self._local_cache.counter != counter: self._local_cache.clear() self._local_cache.counter = counter - try: - return self._local_cache[self.id][name] - except KeyError: - pass - try: - return self._cache[self.id][name] - except KeyError: - pass # fetch the definition of the field if name in self._fields: @@ -1172,6 +1164,16 @@ class ModelStorage(Model): raise AttributeError("'%s' Model has no attribute '%s'" % (self.__name__, name)) + try: + return self._local_cache[self.id][name] + except KeyError: + pass + try: + if field._type not in ('many2one', 'reference'): + return self._cache[self.id][name] + except KeyError: + pass + # build the list of fields we will fetch ffields = { name: field, @@ -1233,18 +1235,12 @@ class ModelStorage(Model): ffields[field.datetime_field] = datetime_field def filter_(id_): - try: - if name in self._local_cache[id_]: - return False - if name in self._cache[id_]: - return False - except KeyError: - pass - return True + return (name not in self._cache.get(id_, {}) + and name not in self._local_cache.get(id_, {})) index = self._ids.index(self.id) ids = chain(islice(self._ids, index, None), islice(self._ids, 0, max(index - 1, 0))) - ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX)) + ids = islice(ifilter(filter_, ids), self._cursor.IN_MAX) def instantiate(field, value, data): if field._type in ('many2one', 'one2one', 'reference'): @@ -1287,25 +1283,40 @@ class ModelStorage(Model): with contextlib.nested(Transaction().set_cursor(self._cursor), Transaction().set_user(self._user), Transaction().set_context(self._context)): + if self.id in self._cache and name in self._cache[self.id]: + # Use values from cache + ids = islice(chain(islice(self._ids, index, None), + islice(self._ids, 0, max(index - 1, 0))), + self._cursor.IN_MAX) + ffields = {name: ffields[name]} + read_data = [{'id': i, name: self._cache[i][name]} + for i in ids + if i in self._cache and name in self._cache[i]] + else: + read_data = self.read(list(ids), ffields.keys()) # create browse records for 'remote' models - for data in self.read(ids, ffields.keys()): + for data in read_data: for fname, field in ffields.iteritems(): + fvalue = data[fname] if field._type in ('many2one', 'one2one', 'one2many', 'many2many', 'reference'): - data[fname] = instantiate(field, data[fname], data) - elif not isinstance(field, fields.Function): - continue + fvalue = instantiate(field, data[fname], data) if data['id'] == self.id and fname == name: - value = data[fname] + value = fvalue + if (field._type not in ('many2one', 'one2one', 'one2many', + 'many2many', 'reference') + and not isinstance(field, fields.Function)): + continue if data['id'] not in self._local_cache: self._local_cache[data['id']] = {} - self._local_cache[data['id']][fname] = data[fname] - del data[fname] + self._local_cache[data['id']][fname] = fvalue + if (field._type not in ('many2one', 'reference') + or getattr(field, 'datetime_field', None) + or isinstance(field, fields.Function)): + del data[fname] if data['id'] not in self._cache: self._cache[data['id']] = {} self._cache[data['id']].update(data) - if data['id'] == self.id and name in data: - value = data[name] return value @property diff --git a/trytond/res/user.py b/trytond/res/user.py index f89e2ce90b..d30deaf96b 100644 --- a/trytond/res/user.py +++ b/trytond/res/user.py @@ -221,6 +221,12 @@ class User(ModelSQL, ModelView): def write(cls, users, vals): vals = cls._convert_vals(vals) super(User, cls).write(users, vals) + # Clean cursor cache as it could be filled by domain_get + for cache in Transaction().cursor.cache.itervalues(): + if cls.__name__ in cache: + for user in users: + if user.id in cache[cls.__name__]: + cache[cls.__name__][user.id].clear() # Restart the cache for domain_get method pool = Pool() pool.get('ir.rule')._domain_get_cache.clear()