From 96bc3d4fffde8cd084e795002797266d880350e6 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Wed, 26 Aug 2020 10:38:50 +0000 Subject: [PATCH] [FIX] website: invalide cache page with classic ormcache invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid to manually invalid the lru cache and probably miss some place (assets, website menu, qweb view, website, company, ...) we use the classic cache. Courtesy of @rco-odoo for the help and review closes odoo/odoo#56744 X-original-commit: 98e12901c97a67dc87f21647473754cd6f384d4a Signed-off-by: Jérémy Kersten (jke) --- addons/website/models/ir_http.py | 20 ++++---------------- addons/website/models/website_page.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index 53762ac85c3..ebda40a665a 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -19,7 +19,6 @@ from odoo import registry, SUPERUSER_ID from odoo.http import request from odoo.tools.safe_eval import safe_eval from odoo.osv.expression import FALSE_DOMAIN -from odoo.tools.lru import LRU from odoo.addons.http_routing.models.ir_http import ModelConverter, _guess_mimetype from odoo.addons.portal.controllers.portal import _build_url_w_params @@ -70,7 +69,6 @@ class Http(models.AbstractModel): @classmethod def clear_caches(cls): super(Http, cls)._clear_routing_map() - cls.clear_cached_pages() return super(Http, cls).clear_caches() @classmethod @@ -243,10 +241,6 @@ class Http(models.AbstractModel): installed = request.registry._init_modules | set(odoo.conf.server_wide_modules) return mods + [mod for mod in installed if mod.startswith('website')] - @classmethod - def clear_cached_pages(cls): - cls._cached_pages = LRU(1024) - @classmethod def _serve_page(cls): req_page = request.httprequest.path @@ -268,22 +262,17 @@ class Http(models.AbstractModel): and request.httprequest.method == "GET" and request.env.user._is_public() # only cache for unlogged user and 'nocache' not in request.params # allow bypass cache / debug + and not request.session.debug and len(cache_key) and cache_key[-1] is not None # nocache via expr ): need_to_cache = True - if not hasattr(cls, '_cached_pages'): - cls.clear_cached_pages() # init LRU - - # use cached version try: - r = cls._cached_pages[cache_key] + r = page._get_cache_response(cache_key) if r['time'] + page.cache_time > time.time(): response = werkzeug.Response(r['content'], mimetype=r['contenttype']) response._cached_template = r['template'] response._cached_page = page return response - else: - cls._cached_pages[cache_key] except KeyError: pass @@ -295,13 +284,12 @@ class Http(models.AbstractModel): if need_to_cache and response.status_code == 200: r = response.render() - cls._cached_pages[cache_key] = { + page._set_cache_response(cache_key, { 'content': r, 'contenttype': response.headers['Content-Type'], 'time': time.time(), 'template': getattr(response, 'qcontext', {}).get('response_template') - } - + }) return response return False diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py index 96eed5a06ee..d5da26d8cfa 100644 --- a/addons/website/models/website_page.py +++ b/addons/website/models/website_page.py @@ -201,6 +201,7 @@ class Page(models.Model): def write(self, vals): if 'url' in vals and not vals['url'].startswith('/'): vals['url'] = '/' + vals['url'] + self.clear_caches() # write on page == write on view that invalid cache return super(Page, self).write(vals) def get_website_meta(self): @@ -215,3 +216,20 @@ class Page(models.Model): if self.cache_key_expr: # e.g. (request.session.geoip.get('country_code'),) cache_key += safe_eval(self.cache_key_expr, {'request': req}) return cache_key + + def _get_cache_response(self, cache_key): + """ Return the cached response corresponding to ``self`` and ``cache_key``. + Raise a KeyError if the item is not in cache. + """ + # HACK: we use the same LRU as ormcache to take advantage from its + # distributed invalidation, but we don't explicitly use ormcache + return self.pool._Registry__cache[('website.page', _cached_response, self.id, cache_key)] + + def _set_cache_response(self, cache_key, response): + """ Put in cache the given response. """ + self.pool._Registry__cache[('website.page', _cached_response, self.id, cache_key)] = response + + +# this is just a dummy function to be used as ormcache key +def _cached_response(): + pass