mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-08-01 19:00:09 +03:00
Add inital CachingConnectionPool class
This commit is contained in:
parent
f08019e356
commit
74238d52e7
160
lib/pool.py
160
lib/pool.py
|
@ -184,3 +184,163 @@ class ThreadedConnectionPool(AbstractConnectionPool):
|
||||||
self._closeall()
|
self._closeall()
|
||||||
finally:
|
finally:
|
||||||
self._lock.release()
|
self._lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
class CachingConnectionPool(AbstractConnectionPool):
|
||||||
|
"""A connection pool that works with the threading module and caches connections"""
|
||||||
|
|
||||||
|
# A dictionary to hold connection ID's and when they should be removed from the pool
|
||||||
|
# Keys are id(connection) and vlaues are expiration time
|
||||||
|
# Storing the expiration time on the connection itself might be preferable, if possible.
|
||||||
|
from collections import OrderedDict
|
||||||
|
_expirations = OrderedDict()
|
||||||
|
|
||||||
|
def __init__(self, minconn, maxconn, lifetime = 3600, *args, **kwargs):
|
||||||
|
"""Initialize the threading lock."""
|
||||||
|
import threading
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
AbstractConnectionPool.__init__(
|
||||||
|
self, minconn, maxconn, *args, **kwargs)
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
self._lifetime = lifetime
|
||||||
|
|
||||||
|
def _connect(self, key=None):
|
||||||
|
"""Create a new connection, assign it to 'key' if not None,
|
||||||
|
And assign an expiration time"""
|
||||||
|
conn = psycopg2.connect(*self._args, **self._kwargs)
|
||||||
|
if key is not None:
|
||||||
|
self._used[key] = conn
|
||||||
|
self._rused[id(conn)] = key
|
||||||
|
else:
|
||||||
|
self._pool.append(conn)
|
||||||
|
|
||||||
|
#Add expiration time
|
||||||
|
self._expirations[id(conn)] = datetime.now() + timedelta(seconds = self._lifetime)
|
||||||
|
return conn
|
||||||
|
|
||||||
|
# Override the _putconn function to put the connection back into the pool even if we are over minconn, and to run the _prune command.
|
||||||
|
def _putconn(self, conn, key=None, close=False):
|
||||||
|
"""Put away a connection."""
|
||||||
|
if self.closed:
|
||||||
|
raise PoolError("connection pool is closed")
|
||||||
|
if key is None:
|
||||||
|
key = self._rused.get(id(conn))
|
||||||
|
|
||||||
|
if not key:
|
||||||
|
raise PoolError("trying to put unkeyed connection")
|
||||||
|
|
||||||
|
if len(self._pool) < self.maxconn and not close:
|
||||||
|
# Return the connection into a consistent state before putting
|
||||||
|
# it back into the pool
|
||||||
|
if not conn.closed:
|
||||||
|
status = conn.get_transaction_status()
|
||||||
|
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
|
||||||
|
# server connection lost
|
||||||
|
conn.close()
|
||||||
|
try:
|
||||||
|
del self._expirations[id(conn)]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
elif status != _ext.TRANSACTION_STATUS_IDLE:
|
||||||
|
# connection in error or in transaction
|
||||||
|
conn.rollback()
|
||||||
|
self._pool.append(conn)
|
||||||
|
else:
|
||||||
|
# regular idle connection
|
||||||
|
self._pool.append(conn)
|
||||||
|
# If the connection is closed, we just discard it.
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
del self._expirations[id(conn)]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
conn.close()
|
||||||
|
#remove this connection from the expiration list
|
||||||
|
try:
|
||||||
|
del self._expirations[id(conn)]
|
||||||
|
except KeyError:
|
||||||
|
pass #not in the expiration list for some reason, can't remove it.
|
||||||
|
|
||||||
|
# here we check for the presence of key because it can happen that a
|
||||||
|
# thread tries to put back a connection after a call to close
|
||||||
|
if not self.closed or key in self._used:
|
||||||
|
del self._used[key]
|
||||||
|
del self._rused[id(conn)]
|
||||||
|
|
||||||
|
# remove any expired connections from the pool
|
||||||
|
self._prune()
|
||||||
|
|
||||||
|
|
||||||
|
def getconn(self, key=None):
|
||||||
|
"""Get a free connection and assign it to 'key' if not None."""
|
||||||
|
self._lock.acquire()
|
||||||
|
try:
|
||||||
|
return self._getconn(key)
|
||||||
|
finally:
|
||||||
|
self._lock.release()
|
||||||
|
|
||||||
|
def putconn(self, conn=None, key=None, close=False):
|
||||||
|
"""Put away an unused connection."""
|
||||||
|
self._lock.acquire()
|
||||||
|
try:
|
||||||
|
self._putconn(conn, key, close)
|
||||||
|
finally:
|
||||||
|
self._lock.release()
|
||||||
|
|
||||||
|
def closeall(self):
|
||||||
|
"""Close all connections (even the one currently in use.)"""
|
||||||
|
self._lock.acquire()
|
||||||
|
try:
|
||||||
|
self._closeall()
|
||||||
|
finally:
|
||||||
|
self._lock.release()
|
||||||
|
|
||||||
|
def _prune(self):
|
||||||
|
"""Remove any expired connections from the connection pool."""
|
||||||
|
junk_expirations = []
|
||||||
|
for obj_id, exp_time in self._expirations.items():
|
||||||
|
# _expirations is an ordered dict, so results should be in chronological order
|
||||||
|
if exp_time > datetime.now():
|
||||||
|
break;
|
||||||
|
|
||||||
|
del_idx = None
|
||||||
|
#find index of connection in _pool. May not be there if connection is in use
|
||||||
|
for index, conn in enumerate(self._pool):
|
||||||
|
if id(conn) == obj_id:
|
||||||
|
conn.close()
|
||||||
|
junk_expirations.append(obj_id)
|
||||||
|
del_idx = index
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# See if this connection is used. If not, we need to remove
|
||||||
|
# the reference to it.
|
||||||
|
for conn in self._used.values():
|
||||||
|
if id(conn) == obj_id:
|
||||||
|
break #found it, so just move on. Don't expire the
|
||||||
|
# connection till we are done with it.
|
||||||
|
else:
|
||||||
|
# This connection doesn't exist any more, so get rid
|
||||||
|
# of the reference to the expiration.
|
||||||
|
# Can't delete here because we'd be changing the item
|
||||||
|
# we are itterating over.
|
||||||
|
junk_expirations.append(obj_id)
|
||||||
|
|
||||||
|
# Delete connection from pool if expired
|
||||||
|
if del_idx is not None:
|
||||||
|
del self._pool[del_idx]
|
||||||
|
|
||||||
|
|
||||||
|
# Remove any junk expirations
|
||||||
|
for item in junk_expirations:
|
||||||
|
try:
|
||||||
|
del self._expirations[item]
|
||||||
|
except KeyError:
|
||||||
|
pass #expiration doesn't exist??
|
||||||
|
|
||||||
|
# Make sure we still have at least minconn connections
|
||||||
|
# Connections may be available or used
|
||||||
|
total_conns = len(self._pool) + len(self._used)
|
||||||
|
if total_conns < self.minconn:
|
||||||
|
for i in range(self.minconn - total_conns):
|
||||||
|
self._connect()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user