# 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
|
b
|
class ConnectionPoolBackend(object): |
| 132 | 132 | def _return_cnx(self, cnx, key, tid): |
| 133 | 133 | self._available.acquire() |
| 134 | 134 | try: |
| 135 | | assert (tid, key) in self._active |
| 136 | | cnx, num = self._active[(tid, key)] |
| 137 | | if num == 1: |
| 138 | | del self._active[(tid, key)] |
| 139 | | self._available.notify() |
| 140 | | if cnx.poolable and try_rollback(cnx): |
| 141 | | self._pool.append(cnx) |
| 142 | | self._pool_key.append(key) |
| 143 | | self._pool_time.append(time.time()) |
| 144 | | else: |
| 145 | | self._active[(tid, key)] = (cnx, num - 1) |
| | 135 | tk = (tid, key) |
| | 136 | if tk in self._active: |
| | 137 | cnx, num = self._active[tk] |
| | 138 | if num == 1: |
| | 139 | self._deactivate(tk) |
| | 140 | else: |
| | 141 | self._active[(tid, key)] = (cnx, num - 1) |
| | 142 | # else, might be gone already due to `shutdown(tid)` |
| 146 | 143 | finally: |
| 147 | 144 | self._available.release() |
| 148 | 145 | |
| | 146 | def _deactivate(self, tid_key): |
| | 147 | """Remove (tid, key) entry from the `_active` connections |
| | 148 | and from there put the `cnx` in the pool if it is poolable. |
| | 149 | |
| | 150 | Note: _available lock must be held. |
| | 151 | """ |
| | 152 | cnx, num = self._active.pop(tid_key) |
| | 153 | if try_rollback(cnx) and cnx.poolable: |
| | 154 | self._pool.append(cnx) |
| | 155 | self._pool_key.append(tid_key[1]) |
| | 156 | self._pool_time.append(time.time()) |
| | 157 | self._available.notify() |
| | 158 | |
| 149 | 159 | def shutdown(self, tid=None): |
| 150 | 160 | """Close pooled connections not used in a while""" |
| 151 | 161 | delay = 120 |
| … |
… |
class ConnectionPoolBackend(object): |
| 154 | 164 | when = time.time() - delay |
| 155 | 165 | self._available.acquire() |
| 156 | 166 | try: |
| | 167 | # forceful clean-up of active connections for this thread (#8443) |
| | 168 | for tk in [tk for tk in self._active.keys() if tk[0] == tid]: |
| | 169 | self._deactivate(tk) |
| | 170 | # claim unused connections |
| 157 | 171 | while self._pool_time and self._pool_time[0] <= when: |
| 158 | 172 | self._pool.pop(0) |
| 159 | 173 | self._pool_key.pop(0) |
| 160 | 174 | self._pool_time.pop(0) |
| | 175 | self._available.notify() |
| 161 | 176 | finally: |
| 162 | 177 | self._available.release() |
| 163 | 178 | |