# HG changeset patch
# Parent f4eede049f46ec816d9c272161a16005e75b68eb
db pool: try harder to `rollback` unused db connections, which is especially important for the PostgreSQL backend where failing to do so can leave `idle in transaction` connections behind.

Should fix #8443.


diff --git a/trac/db/pool.py b/trac/db/pool.py
--- a/trac/db/pool.py
+++ b/trac/db/pool.py
@@ -132,20 +132,30 @@ class ConnectionPoolBackend(object):
     def _return_cnx(self, cnx, key, tid):
         self._available.acquire()
         try:
-            assert (tid, key) in self._active
-            cnx, num = self._active[(tid, key)]
-            if num == 1:
-                del self._active[(tid, key)]
-                self._available.notify() 
-                if cnx.poolable and try_rollback(cnx):
-                    self._pool.append(cnx)
-                    self._pool_key.append(key)
-                    self._pool_time.append(time.time())
-            else:
-                self._active[(tid, key)] = (cnx, num - 1)
+            tk = (tid, key)
+            if tk in self._active:
+                cnx, num = self._active[tk]
+                if num == 1:
+                    self._deactivate(tk)
+                else:
+                    self._active[(tid, key)] = (cnx, num - 1)
+             # else, might be gone already due to `shutdown(tid)`
         finally:
             self._available.release()
 
+    def _deactivate(self, tid_key):
+        """Remove (tid, key) entry from the `_active` connections
+        and from there put the `cnx` in the pool if it is poolable.
+        
+        Note: _available lock must be held.
+        """
+        cnx, num = self._active.pop(tid_key)
+        if try_rollback(cnx) and cnx.poolable:
+            self._pool.append(cnx)
+            self._pool_key.append(tid_key[1])
+            self._pool_time.append(time.time())
+        self._available.notify()
+
     def shutdown(self, tid=None):
         """Close pooled connections not used in a while"""
         delay = 120
@@ -154,10 +164,15 @@ class ConnectionPoolBackend(object):
         when = time.time() - delay
         self._available.acquire()
         try:
+            # forceful clean-up of active connections for this thread (#8443)
+            for tk in [tk for tk in self._active.keys() if tk[0] == tid]:
+                self._deactivate(tk)
+            # claim unused connections
             while self._pool_time and self._pool_time[0] <= when:
                 self._pool.pop(0)
                 self._pool_key.pop(0)
                 self._pool_time.pop(0)
+                self._available.notify()
         finally:
             self._available.release()
 

