Ticket #9111: t9111-create-close-outside-cs.diff
| File t9111-create-close-outside-cs.diff, 5.3 KB (added by cboos, 23 months ago) |
|---|
-
trac/db/pool.py
# HG changeset patch # Parent 04aa8ff1c31795f341c2b9c571173fe7a0627bb3 pool: when taking or returning a Connection, don't do any potentially lenghty operation with the lock held. diff --git a/trac/db/pool.py b/trac/db/pool.py
a b class ConnectionPoolBackend(object): 80 80 key = unicode(kwargs) 81 81 start = time.time() 82 82 tid = threading._get_ident() 83 # Get a Connection, either directly or a deferred one 83 84 self._available.acquire() 84 85 try: 85 86 # First choice: Return the same cnx already used by the thread … … class ConnectionPoolBackend(object): 97 98 num = 1 98 99 if cnx: 99 100 self._active[(tid, key)] = (cnx, num) 100 return PooledConnection(self, cnx, key, tid, log)101 else:102 # if we didn't get a cnx after wait() something's fishy...103 timeout = time.time() - start104 raise TimeoutError(_('Unable to get database '105 'connection within %(time)d '106 'seconds', time=timeout))107 101 finally: 108 102 self._available.release() 109 103 104 deferred = num == 1 and isinstance(cnx, tuple) 105 if deferred: 106 # Potentially lenghty operations must be done without lock held 107 op, cnx = cnx 108 if op == 'ping': 109 try: 110 cnx.ping() 111 except: 112 cnx = None 113 elif op == 'close': 114 cnx.close() 115 if op in ('close', 'create'): 116 cnx = connector.get_connection(**kwargs) 117 118 if cnx: 119 if deferred: 120 # replace placeholder with real Connection 121 self._available.acquire() 122 try: 123 self._active[(tid, key)] = (cnx, num) 124 finally: 125 self._available.release() 126 return PooledConnection(self, cnx, key, tid, log) 127 128 if deferred and op == 'ping': 129 # cnx couldn't be reused, clear placeholder and retry 130 self._available.acquire() 131 try: 132 del self._active[(tid, key)] 133 finally: 134 self._available.release() 135 return self.get_cnx(connector, kwargs) 136 137 # if we didn't get a cnx after wait(), something's fishy... 138 timeout = time.time() - start 139 raise TimeoutError(_("Unable to get database connection within " 140 "%(time)d seconds", time=timeout)) 141 110 142 def _take_cnx(self, connector, kwargs, key, tid): 111 143 """Note: _available lock must be held when calling this method.""" 112 create = False113 144 # Second best option: Reuse a live pooled connection 114 145 if key in self._pool_key: 115 146 idx = self._pool_key.index(key) … … class ConnectionPoolBackend(object): 119 150 # If possible, verify that the pooled connection is 120 151 # still available and working. 121 152 if hasattr(cnx, 'ping'): 122 try: 123 cnx.ping() 124 except: 125 cnx = self._take_cnx(key, tid) 153 return ('ping', cnx) 126 154 return cnx 127 155 # Third best option: Create a new connection 128 156 elif len(self._active) + len(self._pool) < self._maxsize: 129 create = True157 return ('create', None) 130 158 # Forth best option: Replace a pooled connection with a new one 131 159 elif len(self._active) < self._maxsize: 132 160 # Remove the LRU connection in the pool 133 self._pool.pop(0) .close()161 self._pool.pop(0) 134 162 self._pool_key.pop(0) 135 163 self._pool_time.pop(0) 136 create = True 137 if create: 138 return connector.get_connection(**kwargs) 164 return ('close', None) 139 165 140 166 def _return_cnx(self, cnx, key, tid): 167 # Decrement active refcount, clear slot if 1 141 168 self._available.acquire() 142 169 try: 143 170 assert (tid, key) in self._active 144 171 cnx, num = self._active[(tid, key)] 145 172 if num == 1: 146 173 del self._active[(tid, key)] 147 self._available.notify()148 if cnx.poolable and try_rollback(cnx):149 self._pool.append(cnx)150 self._pool_key.append(key)151 self._pool_time.append(time.time())152 174 else: 153 175 self._active[(tid, key)] = (cnx, num - 1) 154 176 finally: 155 177 self._available.release() 178 if num == 1: 179 # Reset connection outside of critical section 180 if not try_rollback(cnx): # TODO inline this in 0.13 181 cnx = None 182 # Connection available, from reuse or from creation of a new one 183 self._available.acquire() 184 try: 185 if cnx and cnx.poolable: 186 self._pool.append(cnx) 187 self._pool_key.append(key) 188 self._pool_time.append(time.time()) 189 self._available.notify() 190 finally: 191 self._available.release() 156 192 157 193 def shutdown(self, tid=None): 158 194 """Close pooled connections not used in a while"""
