From 8023bff1b654ab0d27a6aebcdee4f7edb9dd3d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 10 Feb 2023 19:15:26 +0100 Subject: [PATCH 1/3] session_db: gevent and thread support There were concurrency issues in evented mode. So while I was at it, I added support for threaded mode too. --- session_db/pg_session_store.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/session_db/pg_session_store.py b/session_db/pg_session_store.py index e5974232d..189be9658 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -15,6 +15,29 @@ from odoo.tools.func import lazy_property _logger = logging.getLogger(__name__) +lock = None +if odoo.evented: + import gevent.lock + + lock = gevent.lock.RLock() +elif odoo.tools.config["workers"] == 0: + import threading + + lock = threading.RLock() + + +def with_lock(func): + def wrapper(*args, **kwargs): + try: + if lock is not None: + lock.acquire() + return func(*args, **kwargs) + finally: + if lock is not None: + lock.release() + + return wrapper + def with_cursor(func): def wrapper(self, *args, **kwargs): @@ -37,9 +60,6 @@ class PGSessionStore(sessions.SessionStore): super().__init__(session_class) self._uri = uri self._cr = None - # FIXME This class is NOT thread-safe. Only use in worker mode - if odoo.tools.config["workers"] == 0: - raise ValueError("session_db requires multiple workers") self._open_connection() self._setup_db() @@ -47,6 +67,7 @@ class PGSessionStore(sessions.SessionStore): if self._cr is not None: self._cr.close() + @with_lock def _open_connection(self): cnx = odoo.sql_db.db_connect(self._uri, allow_uri=True) try: @@ -59,6 +80,7 @@ class PGSessionStore(sessions.SessionStore): self._cr = cnx.cursor() self._cr._cnx.autocommit = True + @with_lock @with_cursor def _setup_db(self): self._cr.execute( @@ -71,6 +93,7 @@ class PGSessionStore(sessions.SessionStore): """ ) + @with_lock @with_cursor def save(self, session): payload = json.dumps(dict(session)) @@ -85,10 +108,12 @@ class PGSessionStore(sessions.SessionStore): dict(sid=session.sid, payload=payload), ) + @with_lock @with_cursor def delete(self, session): self._cr.execute("DELETE FROM http_sessions WHERE sid=%s", (session.sid,)) + @with_lock @with_cursor def get(self, sid): self._cr.execute("SELECT payload FROM http_sessions WHERE sid=%s", (sid,)) @@ -103,6 +128,7 @@ class PGSessionStore(sessions.SessionStore): # so let's get it from FilesystemSessionStore. rotate = http.FilesystemSessionStore.rotate + @with_lock @with_cursor def vacuum(self): self._cr.execute( From b13029ab08a121247e93dbe6165249fef0ec10f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 10 Feb 2023 19:21:10 +0100 Subject: [PATCH 2/3] session_db: cosmetics --- session_db/pg_session_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session_db/pg_session_store.py b/session_db/pg_session_store.py index 189be9658..1a6e3a2d4 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -69,7 +69,6 @@ class PGSessionStore(sessions.SessionStore): @with_lock def _open_connection(self): - cnx = odoo.sql_db.db_connect(self._uri, allow_uri=True) try: # return cursor to the pool if self._cr is not None: @@ -77,6 +76,7 @@ class PGSessionStore(sessions.SessionStore): self._cr = None except Exception: # pylint: disable=except-pass pass + cnx = odoo.sql_db.db_connect(self._uri, allow_uri=True) self._cr = cnx.cursor() self._cr._cnx.autocommit = True From 2277d974581225574cc139b4c14bc9e18182512a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 10 Feb 2023 19:57:41 +0100 Subject: [PATCH 3/3] session_db: improve cursor release --- session_db/pg_session_store.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/session_db/pg_session_store.py b/session_db/pg_session_store.py index 1a6e3a2d4..849e431de 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -69,13 +69,13 @@ class PGSessionStore(sessions.SessionStore): @with_lock def _open_connection(self): - try: - # return cursor to the pool - if self._cr is not None: + # return cursor to the pool + if self._cr is not None: + try: self._cr.close() - self._cr = None - except Exception: # pylint: disable=except-pass - pass + except Exception: # pylint: disable=except-pass + pass + self._cr = None cnx = odoo.sql_db.db_connect(self._uri, allow_uri=True) self._cr = cnx.cursor() self._cr._cnx.autocommit = True