Skip to content

Commit 94a6ea4

Browse files
committed
Merge PR #3050 into 18.0
Signed-off-by sbidoul
2 parents d41c0bb + 7d2e3d8 commit 94a6ea4

13 files changed

+832
-0
lines changed

session_db/README.rst

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
====================
2+
Store sessions in DB
3+
====================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:7f625ad3c63b7ad41974310cc447302e517eb7fe62290a60ceec8f0325b80853
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
20+
:target: https://github.com/OCA/server-tools/tree/18.0/session_db
21+
:alt: OCA/server-tools
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-session_db
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
Store sessions in a database instead of the filesystem. This simplifies
32+
the configuration of horizontally scalable deployments, by avoiding the
33+
need for a distributed filesystem to store the Odoo sessions.
34+
35+
**Table of contents**
36+
37+
.. contents::
38+
:local:
39+
40+
Usage
41+
=====
42+
43+
Set this module in the server wide modules.
44+
45+
Set a ``SESSION_DB_URI`` environment variable as a full postgresql
46+
connection string, like ``postgres://user:passwd@server/db`` or ``db``.
47+
48+
It is recommended to use a dedicated database for this module, and
49+
possibly a dedicated postgres user for additional security.
50+
51+
Bug Tracker
52+
===========
53+
54+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
55+
In case of trouble, please check there if your issue has already been reported.
56+
If you spotted it first, help us to smash it by providing a detailed and welcomed
57+
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20session_db%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
58+
59+
Do not contact contributors directly about support or help with technical issues.
60+
61+
Credits
62+
=======
63+
64+
Authors
65+
-------
66+
67+
* Odoo SA
68+
* ACSONE SA/NV
69+
70+
Maintainers
71+
-----------
72+
73+
This module is maintained by the OCA.
74+
75+
.. image:: https://odoo-community.org/logo.png
76+
:alt: Odoo Community Association
77+
:target: https://odoo-community.org
78+
79+
OCA, or the Odoo Community Association, is a nonprofit organization whose
80+
mission is to support the collaborative development of Odoo features and
81+
promote its widespread use.
82+
83+
.. |maintainer-sbidoul| image:: https://github.com/sbidoul.png?size=40px
84+
:target: https://github.com/sbidoul
85+
:alt: sbidoul
86+
87+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
88+
89+
|maintainer-sbidoul|
90+
91+
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/session_db>`_ project on GitHub.
92+
93+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

session_db/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import pg_session_store

session_db/__manifest__.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "Store sessions in DB",
3+
"version": "18.0.1.0.0",
4+
"author": "Odoo SA,ACSONE SA/NV,Odoo Community Association (OCA)",
5+
"license": "LGPL-3",
6+
"website": "https://github.com/OCA/server-tools",
7+
"maintainers": ["sbidoul"],
8+
}

session_db/i18n/it.po

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
#
4+
msgid ""
5+
msgstr ""
6+
"Project-Id-Version: Odoo Server 17.0\n"
7+
"Report-Msgid-Bugs-To: \n"
8+
"Last-Translator: Automatically generated\n"
9+
"Language-Team: none\n"
10+
"Language: it\n"
11+
"MIME-Version: 1.0\n"
12+
"Content-Type: text/plain; charset=UTF-8\n"
13+
"Content-Transfer-Encoding: \n"
14+
"Plural-Forms: nplurals=2; plural=n != 1;\n"

session_db/i18n/session_db.pot

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
#
4+
msgid ""
5+
msgstr ""
6+
"Project-Id-Version: Odoo Server 17.0\n"
7+
"Report-Msgid-Bugs-To: \n"
8+
"Last-Translator: \n"
9+
"Language-Team: \n"
10+
"MIME-Version: 1.0\n"
11+
"Content-Type: text/plain; charset=UTF-8\n"
12+
"Content-Transfer-Encoding: \n"
13+
"Plural-Forms: \n"

session_db/pg_session_store.py

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Copyright (c) Odoo SA 2017
2+
# @author Nicolas Seinlet
3+
# Copyright (c) ACSONE SA 2022
4+
# @author Stéphane Bidoul
5+
import json
6+
import logging
7+
import os
8+
9+
import psycopg2
10+
11+
import odoo
12+
from odoo import http
13+
from odoo.tools._vendor import sessions
14+
from odoo.tools.func import lazy_property
15+
16+
_logger = logging.getLogger(__name__)
17+
18+
lock = None
19+
if odoo.evented:
20+
import gevent.lock
21+
22+
lock = gevent.lock.RLock()
23+
elif odoo.tools.config["workers"] == 0:
24+
import threading
25+
26+
lock = threading.RLock()
27+
28+
29+
def with_lock(func):
30+
def wrapper(*args, **kwargs):
31+
try:
32+
if lock is not None:
33+
lock.acquire()
34+
return func(*args, **kwargs)
35+
finally:
36+
if lock is not None:
37+
lock.release()
38+
39+
return wrapper
40+
41+
42+
def with_cursor(func):
43+
def wrapper(self, *args, **kwargs):
44+
tries = 0
45+
while True:
46+
tries += 1
47+
try:
48+
self._ensure_connection()
49+
return func(self, *args, **kwargs)
50+
except (psycopg2.InterfaceError, psycopg2.OperationalError):
51+
self._close_connection()
52+
if tries > 4:
53+
_logger.warning(
54+
"session_db operation try %s/5 failed, aborting", tries
55+
)
56+
raise
57+
_logger.info("session_db operation try %s/5 failed, retrying", tries)
58+
59+
return wrapper
60+
61+
62+
class PGSessionStore(sessions.SessionStore):
63+
def __init__(self, uri, session_class=None):
64+
super().__init__(session_class)
65+
self._uri = uri
66+
self._cr = None
67+
self._open_connection()
68+
self._setup_db()
69+
70+
def __del__(self):
71+
self._close_connection()
72+
73+
@with_lock
74+
def _ensure_connection(self):
75+
if self._cr is None:
76+
self._open_connection()
77+
78+
@with_lock
79+
def _open_connection(self):
80+
self._close_connection()
81+
cnx = odoo.sql_db.db_connect(self._uri, allow_uri=True)
82+
self._cr = cnx.cursor()
83+
self._cr._cnx.autocommit = True
84+
85+
@with_lock
86+
def _close_connection(self):
87+
"""Return cursor to the pool."""
88+
if self._cr is not None:
89+
try:
90+
self._cr.close()
91+
except Exception: # pylint: disable=except-pass
92+
pass
93+
self._cr = None
94+
95+
@with_lock
96+
@with_cursor
97+
def _setup_db(self):
98+
self._cr.execute(
99+
"""
100+
CREATE TABLE IF NOT EXISTS http_sessions (
101+
sid varchar PRIMARY KEY,
102+
write_date timestamp without time zone NOT NULL,
103+
payload text NOT NULL
104+
)
105+
"""
106+
)
107+
108+
@with_lock
109+
@with_cursor
110+
def save(self, session):
111+
payload = json.dumps(dict(session))
112+
self._cr.execute(
113+
"""
114+
INSERT INTO http_sessions(sid, write_date, payload)
115+
VALUES (%(sid)s, now() at time zone 'UTC', %(payload)s)
116+
ON CONFLICT (sid)
117+
DO UPDATE SET payload = %(payload)s,
118+
write_date = now() at time zone 'UTC'
119+
""",
120+
dict(sid=session.sid, payload=payload),
121+
)
122+
123+
@with_lock
124+
@with_cursor
125+
def delete(self, session):
126+
self._cr.execute("DELETE FROM http_sessions WHERE sid=%s", (session.sid,))
127+
128+
@with_lock
129+
@with_cursor
130+
def get(self, sid):
131+
self._cr.execute("SELECT payload FROM http_sessions WHERE sid=%s", (sid,))
132+
try:
133+
data = json.loads(self._cr.fetchone()[0])
134+
except Exception:
135+
return self.new()
136+
137+
return self.session_class(data, sid, False)
138+
139+
# This method is not part of the Session interface but is called nevertheless,
140+
# so let's get it from FilesystemSessionStore.
141+
rotate = http.FilesystemSessionStore.rotate
142+
143+
@with_lock
144+
@with_cursor
145+
def vacuum(self, max_lifetime=http.SESSION_LIFETIME):
146+
self._cr.execute(
147+
"DELETE FROM http_sessions "
148+
"WHERE now() at time zone 'UTC' - write_date > %s",
149+
(f"{max_lifetime} seconds",),
150+
)
151+
152+
153+
_original_session_store = http.root.__class__.session_store
154+
155+
156+
@lazy_property
157+
def session_store(self):
158+
session_db_uri = os.environ.get("SESSION_DB_URI")
159+
if session_db_uri:
160+
_logger.debug("HTTP sessions stored in: db")
161+
return PGSessionStore(session_db_uri, session_class=http.Session)
162+
return _original_session_store.__get__(self, self.__class__)
163+
164+
165+
# Monkey patch of standard methods
166+
_logger.debug("Monkey patching session store")
167+
http.root.__class__.session_store = session_store
168+
# Reset the lazy property cache
169+
vars(http.root).pop("session_store", None)

session_db/pyproject.toml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"

session_db/readme/DESCRIPTION.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Store sessions in a database instead of the filesystem. This simplifies
2+
the configuration of horizontally scalable deployments, by avoiding the
3+
need for a distributed filesystem to store the Odoo sessions.

session_db/readme/USAGE.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Set this module in the server wide modules.
2+
3+
Set a `SESSION_DB_URI` environment variable as a full postgresql
4+
connection string, like `postgres://user:passwd@server/db` or `db`.
5+
6+
It is recommended to use a dedicated database for this module, and
7+
possibly a dedicated postgres user for additional security.
9.23 KB
Loading

0 commit comments

Comments
 (0)