details:   https://code.tryton.org/tryton/commit/7df2a71c0551
branch:    default
user:      Cédric Krier <[email protected]>
date:      Tue Nov 25 15:05:02 2025 +0100
description:
        Use psycopg_pool resize to clear unused database connections
diffstat:

 trytond/trytond/backend/postgresql/database.py |  70 ++++++++++++++-----------
 1 files changed, 40 insertions(+), 30 deletions(-)

diffs (90 lines):

diff -r 7d95fc685dde -r 7df2a71c0551 
trytond/trytond/backend/postgresql/database.py
--- a/trytond/trytond/backend/postgresql/database.py    Sun Mar 22 19:53:32 
2026 +0100
+++ b/trytond/trytond/backend/postgresql/database.py    Tue Nov 25 15:05:02 
2025 +0100
@@ -179,6 +179,7 @@
 
     index_translators = []
     _lock = RLock()
+    _clean_last = None
     _databases = defaultdict(dict)
     _connpool = None
     _list_cache = {}
@@ -207,39 +208,48 @@
         }
 
     def __new__(cls, name=_default_name):
-        with cls._lock:
-            now = datetime.now()
-            databases = cls._databases[os.getpid()]
-            timeout = config.getint('database', 'timeout')
+        now = datetime.now()
+        if cls._clean_last is None:
+            cls._clean_last = now
+
+        databases = cls._databases[os.getpid()]
+
+        minconn = config.getint('database', 'minconn', default=1)
+        maxconn = config.getint('database', 'maxconn', default=64)
+        timeout = config.getint('database', 'timeout')
+        last_clean = (now - cls._clean_last).total_seconds()
+        if last_clean > timeout:
             for database in list(databases.values()):
                 if ((now - database._last_use).total_seconds() > timeout
                         and database.name != name):
-                    database.close()
-            if name in databases:
-                inst = databases[name]
-            else:
-                inst = DatabaseInterface.__new__(cls, name=name)
-                minconn = config.getint('database', 'minconn', default=1)
-                maxconn = config.getint('database', 'maxconn', default=64)
-                kwargs = cls._connection_params(name)
-                kwargs['cursor_factory'] = LoggingCursor
-                conninfo = kwargs.pop('conninfo')
-                try:
-                    inst._connpool = ConnectionPool(
-                        conninfo,
-                        kwargs=kwargs,
-                        open=True,
-                        check=ConnectionPool.check_connection,
-                        min_size=minconn, max_size=maxconn)
-                except Exception:
-                    logger.error(
-                        'connection to "%s" failed', name, exc_info=True)
-                    raise
-                else:
-                    logger.info('connection to "%s" succeeded', name)
-                databases[name] = inst
-            inst._last_use = datetime.now()
-            return inst
+                    database._connpool.resize(0, 0)
+
+        if not (inst := databases.get(name)):
+            with cls._lock:
+                if not (inst := databases.get(name)):
+                    inst = DatabaseInterface.__new__(cls, name=name)
+                    kwargs = cls._connection_params(name)
+                    kwargs['cursor_factory'] = LoggingCursor
+                    conninfo = kwargs.pop('conninfo')
+                    try:
+                        inst._connpool = ConnectionPool(
+                            conninfo,
+                            kwargs=kwargs,
+                            open=True,
+                            check=ConnectionPool.check_connection,
+                            min_size=minconn, max_size=maxconn)
+                    except Exception:
+                        logger.error(
+                            'connection to "%s" failed', name, exc_info=True)
+                        raise
+                    else:
+                        logger.info('connection to "%s" succeeded', name)
+                    databases[name] = inst
+        elif (inst._connpool.min_size != minconn
+                or inst._connpool.max_size != maxconn):
+            inst._connpool.resize(minconn, maxconn)
+        inst._last_use = now
+        return inst
 
     def __init__(self, name=_default_name):
         super().__init__(name)

Reply via email to