diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 383308061dea9bfb035207aa483d477bb2aad873..b2f9b268dea1c6ad9f71e10f182a1a947b301fb8 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -918,7 +918,7 @@ class Home(http.Controller): uid = request.env.user.id if request.env.user._is_system(): uid = request.session.uid = odoo.SUPERUSER_ID - request.env['res.users']._invalidate_session_cache() + request.env['res.users'].clear_caches() request.session.session_token = security.compute_session_token(request.session, request.env) return http.local_redirect(self._login_redirect(uid), keep_hash=True) diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index 6f7509ac1334d5b6cafb1e494c8d16b272103080..a54c9c2b21fd25c8adef17c384765111a38271c7 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -249,7 +249,6 @@ class Users(models.Model): _description = 'Users' _inherits = {'res.partner': 'partner_id'} _order = 'name, login' - __uid_cache = defaultdict(dict) # {dbname: {uid: password}} # User can write on a few of his own fields (but not his groups for example) SELF_WRITEABLE_FIELDS = ['signature', 'action_id', 'company_id', 'email', 'name', 'image_1920', 'lang', 'tz'] @@ -580,26 +579,24 @@ class Users(models.Model): # DLE P139: Calling invalidate_cache on a new, well you lost everything as you wont be able to take it back from the cache # `test_00_equipment_multicompany_user` self.env['ir.model.access'].call_cache_clearing_methods() - self.env['ir.rule'].clear_caches() - self.has_group.clear_cache(self) - if any(key.startswith('context_') or key in ('lang', 'tz') for key in values): - self.context_get.clear_cache(self) - if any(key in values for key in ['active'] + USER_PRIVATE_FIELDS): - db = self._cr.dbname - for id in self.ids: - self.__uid_cache[db].pop(id, None) - if any(key in values for key in self._get_session_token_fields()): - self._invalidate_session_cache() + + # per-method / per-model caches have been removed so the various + # clear_cache/clear_caches methods pretty much just end up calling + # Registry._clear_cache + invalidation_fields = { + 'groups_id', 'active', 'lang', 'tz', 'company_id', + *USER_PRIVATE_FIELDS, + *self._get_session_token_fields() + } + if (invalidation_fields & values.keys()) or any(key.startswith('context_') for key in values): + self.clear_caches() return res def unlink(self): if SUPERUSER_ID in self.ids: raise UserError(_('You can not remove the admin user as it is used internally for resources created by Odoo (updates, module installation, ...)')) - db = self._cr.dbname - for id in self.ids: - self.__uid_cache[db].pop(id, None) - self._invalidate_session_cache() + self.clear_caches() return super(Users, self).unlink() @api.model @@ -719,22 +716,20 @@ class Users(models.Model): return uid @classmethod + @tools.ormcache('uid', 'passwd') def check(cls, db, uid, passwd): """Verifies that the given (uid, password) is authorized for the database ``db`` and raise an exception if it is not.""" if not passwd: # empty passwords disallowed for obvious security reasons raise AccessDenied() - db = cls.pool.db_name - if cls.__uid_cache[db].get(uid) == passwd: - return + with contextlib.closing(cls.pool.cursor()) as cr: self = api.Environment(cr, uid, {})[cls._name] with self._assert_can_auth(): if not self.env.user.active: raise AccessDenied() self._check_credentials(passwd, {'interactive': False}) - cls.__uid_cache[db][uid] = passwd def _get_session_token_fields(self): return {'id', 'login', 'password', 'active'} @@ -748,7 +743,7 @@ class Users(models.Model): FROM res_users WHERE id=%%s""" % (session_fields), (self.id,)) if self.env.cr.rowcount != 1: - self._invalidate_session_cache() + self.clear_caches() return False data_fields = self.env.cr.fetchone() # generate hmac key @@ -759,10 +754,6 @@ class Users(models.Model): # keep in the cache the token return h.hexdigest() - def _invalidate_session_cache(self): - """ Clear the sessions cache """ - self._compute_session_token.clear_cache(self) - @api.model def change_password(self, old_passwd, new_passwd): """Change current user password. Old password must be provided explicitly diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index f660fccf52c8f83a8d0298646617d0161c11a08c..19b631fc4c3c8ee7f994cb7a46bdf293b309fdd1 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -114,6 +114,7 @@ class Registry(Mapping): self._fields_by_model = None self._ordinary_tables = None self._constraint_queue = deque() + self.__cache = LRU(8192) # modules fully loaded (maintained during init phase by `loading` module) self._init_modules = set() @@ -218,6 +219,7 @@ class Registry(Mapping): """ from .. import models + self.clear_caches() lazy_property.reset_all(self) # Instantiate registered classes (via the MetaModel automatic discovery @@ -234,6 +236,7 @@ class Registry(Mapping): """ Complete the setup of models. This must be called after loading modules and before using the ORM. """ + self.clear_caches() lazy_property.reset_all(self) self.registry_invalidated = True env = odoo.api.Environment(cr, SUPERUSER_ID, {}) @@ -504,15 +507,9 @@ class Registry(Mapping): for table in missing_tables: _logger.error("Model %s has no table.", table2model[table]) - @lazy_property - def cache(self): - """ A cache for model methods. """ - # this lazy_property is automatically reset by lazy_property.reset_all() - return LRU(8192) - def _clear_cache(self): """ Clear the cache and mark it as invalidated. """ - self.cache.clear() + self.__cache.clear() self.cache_invalidated = True def clear_caches(self): @@ -618,7 +615,7 @@ class Registry(Mapping): self.setup_models(cr) self.registry_invalidated = False if self.cache_invalidated: - self.cache.clear() + self.__cache.clear() self.cache_invalidated = False @contextmanager diff --git a/odoo/service/security.py b/odoo/service/security.py index 23d89b6c691c6d618bfb852ad799f0a29a9b6cd7..c247c6d9ac5ee83eb83f8285406bb2d85662670e 100644 --- a/odoo/service/security.py +++ b/odoo/service/security.py @@ -17,5 +17,4 @@ def check_session(session, env): expected = self._compute_session_token(session.sid) if expected and odoo.tools.misc.consteq(expected, session.session_token): return True - self._invalidate_session_cache() return False diff --git a/odoo/tools/cache.py b/odoo/tools/cache.py index 0e244f63a4b113fc9ce68615116dabeff5e2d003..46652736b932688fb984460ca978bae63db51b65 100644 --- a/odoo/tools/cache.py +++ b/odoo/tools/cache.py @@ -76,7 +76,7 @@ class ormcache(object): def lru(self, model): counter = STAT[(model.pool.db_name, model._name, self.method)] - return model.pool.cache, (model._name, self.method), counter + return model.pool._Registry__cache, (model._name, self.method), counter def lookup(self, method, *args, **kwargs): d, key0, counter = self.lru(args[0]) @@ -207,7 +207,7 @@ def log_ormcache_stats(sig=None, frame=None): for dbname, reg in sorted(Registry.registries.d.items()): # set logger prefix to dbname me.dbname = dbname - entries = Counter(k[:2] for k in reg.cache.d) + entries = Counter(k[:2] for k in reg._Registry__cache.d) # show entries sorted by model name, method name for key in sorted(entries, key=lambda key: (key[0], key[1].__name__)): model, method = key diff --git a/odoo/tools/func.py b/odoo/tools/func.py index deae2bdec045fdd19fadfccb9ff9543fe6786494..17b5f85ab246a19aa188df15bce15b84f9d23fec 100644 --- a/odoo/tools/func.py +++ b/odoo/tools/func.py @@ -16,6 +16,8 @@ class lazy_property(object): get it again. """ def __init__(self, fget): + assert not fget.__name__.startswith('__'),\ + "lazy_property does not support mangled names" self.fget = fget def __get__(self, obj, cls):