From a07c905f4b2e8f6fff45b57fdcc1028849ac92aa Mon Sep 17 00:00:00 2001 From: Julien Maupetit Date: Fri, 19 Jul 2024 16:48:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(api)=20fix=20not=20thread-safe=20d?= =?UTF-8?q?atabase=20session=20singleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The singleton pattern is not thread-safe leading to database session inconsistency during server heavy load (expired sessions, etc.) See raised errors in SQLAlchemy docuementation: https://docs.sqlalchemy.org/en/20/errors.html#error-bhk3 --- src/api/CHANGELOG.md | 1 + src/api/qualicharge/db.py | 21 ++------------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/api/CHANGELOG.md b/src/api/CHANGELOG.md index ca50dc8c..41fc4342 100644 --- a/src/api/CHANGELOG.md +++ b/src/api/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to ### Fixed - Add relevant data examples for Swagger +- Improve database session management ## [0.10.0] - 2024-07-01 diff --git a/src/api/qualicharge/db.py b/src/api/qualicharge/db.py index c0193c21..ebed63c3 100644 --- a/src/api/qualicharge/db.py +++ b/src/api/qualicharge/db.py @@ -41,20 +41,6 @@ def get_engine(self, url: PostgresDsn, echo: bool = False) -> SAEngine: return self._engine -class Session(metaclass=Singleton): - """Database session singleton.""" - - _session: Optional[SMSession] = None - - def get_session(self, engine: SAEngine) -> SMSession: - """Get active session or create a new one.""" - if self._session is None: - logger.debug("Create new session") - self._session = SMSession(bind=engine) - logger.debug("Getting database session %s", self._session) - return self._session - - class SAQueryCounter: """Context manager to count SQLALchemy queries. @@ -88,12 +74,9 @@ def get_engine() -> SAEngine: def get_session() -> Generator[SMSession, None, None]: """Get database session.""" - session = Session().get_session(get_engine()) - logger.debug("Getting session %s", session) - try: + with SMSession(bind=get_engine()) as session: + logger.debug("Getting session %s", session) yield session - finally: - session.close() def is_alive() -> bool: