Aestate
simple_pooled_db.py
Go to the documentation of this file.
1 """SimplePooledDB - a very simple DB-API 2 database connection pool.
2 
3 Implements a pool of threadsafe cached DB-API 2 connections
4 to a database which are transparently reused.
5 
6 This should result in a speedup for persistent applications
7 such as the "Webware for Python" AppServer.
8 
9 For more information on the DB-API 2, see:
10  https://www.python.org/dev/peps/pep-0249/
11 For more information on Webware for Python, see:
12  https://webwareforpython.github.io/w4py/
13 
14 Measures are taken to make the pool of connections threadsafe
15 regardless of whether the DB-API 2 module used is threadsafe
16 on the connection level (threadsafety > 1) or not. It must only
17 be threadsafe on the module level (threadsafety = 1). If the
18 DB-API 2 module is threadsafe, the connections will be shared
19 between threads (keep this in mind if you use transactions).
20 
21 Usage:
22 
23 The idea behind SimplePooledDB is that it's completely transparent.
24 After you have established your connection pool, stating the
25 DB-API 2 module to be used, the number of connections
26 to be cached in the pool and the connection parameters, e.g.
27 
28  import pgdb # import used DB-API 2 module
29  from dbutils.simple_pooled_db import PooledDB
30  dbpool = PooledDB(pgdb, 5, host=..., database=..., user=..., ...)
31 
32 you can demand database connections from that pool,
33 
34  db = dbpool.connection()
35 
36 and use them just as if they were ordinary DB-API 2 connections.
37 It's really just a proxy class.
38 
39 db.close() will return the connection to the pool, it will not
40 actually close it. This is so your existing code works nicely.
41 
42 Ideas for improvement:
43 
44 * Do not create the maximum number of connections on startup
45 already, but only a certain number and the rest on demand.
46 * Detect and transparently reset "bad" connections.
47 * Connections should have some sort of maximum usage limit
48 after which they should be automatically closed and reopened.
49 * Prefer or enforce thread-affinity for the connections,
50 allowing for both shareable and non-shareable connections.
51 
52 Please note that these and other ideas have been already
53 implemented in in PooledDB, a more sophisticated version
54 of SimplePooledDB. You might also consider using PersistentDB
55 instead for thread-affine persistent database connections.
56 SimplePooledDB may still serve as a very simple reference
57 and example implementation for developers.
58 
59 
60 Copyright, credits and license:
61 
62 * Contributed as MiscUtils/DBPool for Webware for Python
63  by Dan Green, December 2000
64 * Thread safety bug found by Tom Schwaller
65 * Fixes by Geoff Talvola (thread safety in _threadsafe_getConnection())
66 * Clean up by Chuck Esterbrook
67 * Fix unthreadsafe functions which were leaking, Jay Love
68 * Eli Green's webware-discuss comments were lifted for additional docs
69 * Clean-up and detailed commenting, rename and move to DBUtils
70  by Christoph Zwerschke in September 2005
71 
72 Licensed under the MIT license.
73 """
74 
75 from . import __version__
76 
77 
78 class PooledDBError(Exception):
79  """General PooledDB error."""
80 
81 
83  """DB-API module not supported by PooledDB."""
84 
85 
87  """A proxy class for pooled database connections.
88 
89  You don't normally deal with this class directly,
90  but use PooledDB to get new connections.
91  """
92 
93  def __init__(self, pool, con):
94  self._con = con
95  self._pool = pool
96 
97  def close(self):
98  """Close the pooled connection."""
99  # Instead of actually closing the connection,
100  # return it to the pool so it can be reused.
101  if self._con is not None:
102  self._pool.returnConnection(self._con)
103  self._con = None
104 
105  def __getattr__(self, name):
106  # All other members are the same.
107  return getattr(self._con, name)
108 
109  def __del__(self):
110  self.close()
111 
112 
113 class PooledDB:
114  """A very simple database connection pool.
115 
116  After you have created the connection pool,
117  you can get connections using getConnection().
118  """
119 
120  version = __version__
121 
122  def __init__(self, dbapi, maxconnections, *args, **kwargs):
123  """Set up the database connection pool.
124 
125  dbapi: the DB-API 2 compliant module you want to use
126  maxconnections: the number of connections cached in the pool
127  args, kwargs: the parameters that shall be used to establish
128  the database connections using connect()
129  """
130  try:
131  threadsafety = dbapi.threadsafety
132  except Exception:
133  threadsafety = None
134  if threadsafety == 0:
135  raise NotSupportedError(
136  "Database module does not support any level of threading.")
137  elif threadsafety == 1:
138  # If there is no connection level safety, build
139  # the pool using the synchronized queue class
140  # that implements all the required locking semantics.
141  try:
142  from Queue import Queue
143  except ImportError: # Python 3
144  from queue import Queue
145  self._queue = Queue(maxconnections) # create the queue
149  elif threadsafety in (2, 3):
150  # If there is connection level safety, implement the
151  # pool with an ordinary list used as a circular buffer.
152  # We only need a minimum of locking in this case.
153  from threading import Lock
154  self._lock = Lock() # create a lock object to be used later
155  self._nextConnection = 0 # index of the next connection to be used
156  self._connections = [] # the list of connections
160  else:
161  raise NotSupportedError(
162  "Database module threading support cannot be determined.")
163  # Establish all database connections (it would be better to
164  # only establish a part of them now, and the rest on demand).
165  for i in range(maxconnections):
166  self.addConnection(dbapi.connect(*args, **kwargs))
167 
168  # The following functions are used with DB-API 2 modules
169  # that do not have connection level threadsafety, like PyGreSQL.
170  # However, the module must be threadsafe at the module level.
171  # Note: threadsafe/unthreadsafe refers to the DB-API 2 module,
172  # not to this class which should be threadsafe in any case.
173 
175  """Get a connection from the pool."""
176  return PooledDBConnection(self, self._queue.get())
177 
179  """Add a connection to the pool."""
180  self._queue.put(con)
181 
183  """Return a connection to the pool.
184 
185  In this case, the connections need to be put
186  back into the queue after they have been used.
187  This is done automatically when the connection is closed
188  and should never be called explicitly outside of this module.
189  """
191 
192  # The following functions are used with DB-API 2 modules
193  # that are threadsafe at the connection level, like psycopg.
194  # Note: In this case, connections are shared between threads.
195  # This may lead to problems if you use transactions.
196 
198  """Get a connection from the pool."""
199  self._lock.acquire()
200  try:
201  next = self._nextConnection
202  con = PooledDBConnection(self, self._connections[next])
203  next += 1
204  if next >= len(self._connections):
205  next = 0
206  self._nextConnection = next
207  return con
208  finally:
209  self._lock.release()
210 
212  """Add a connection to the pool."""
213  self._connections.append(con)
214 
216  """Return a connection to the pool.
217 
218  In this case, the connections always stay in the pool,
219  so there is no need to do anything here.
220  """
221  pass
aestate.opera.DBPool.simple_pooled_db.PooledDB._unthreadsafe_get_connection
def _unthreadsafe_get_connection(self)
Definition: simple_pooled_db.py:174
aestate.opera.DBPool.simple_pooled_db.PooledDB.returnConnection
returnConnection
Definition: simple_pooled_db.py:148
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection.close
def close(self)
Definition: simple_pooled_db.py:97
aestate.opera.DBPool.simple_pooled_db.PooledDB._lock
_lock
Definition: simple_pooled_db.py:154
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection.__getattr__
def __getattr__(self, name)
Definition: simple_pooled_db.py:105
aestate.opera.DBPool.simple_pooled_db.PooledDB._connections
_connections
Definition: simple_pooled_db.py:156
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection.__del__
def __del__(self)
Definition: simple_pooled_db.py:109
aestate.opera.DBPool.simple_pooled_db.PooledDB._queue
_queue
Definition: simple_pooled_db.py:145
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection.__init__
def __init__(self, pool, con)
Definition: simple_pooled_db.py:93
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection._pool
_pool
Definition: simple_pooled_db.py:95
aestate.opera.DBPool.simple_pooled_db.PooledDB
Definition: simple_pooled_db.py:113
aestate.opera.DBPool.simple_pooled_db.PooledDB._threadsafe_return_connection
def _threadsafe_return_connection(self, con)
Definition: simple_pooled_db.py:215
aestate.opera.DBPool.simple_pooled_db.PooledDB.connection
connection
Definition: simple_pooled_db.py:146
aestate.opera.DBPool.simple_pooled_db.PooledDB._threadsafe_get_connection
def _threadsafe_get_connection(self)
Definition: simple_pooled_db.py:197
aestate.opera.DBPool.simple_pooled_db.PooledDB._unthreadsafe_return_connection
def _unthreadsafe_return_connection(self, con)
Definition: simple_pooled_db.py:182
aestate.opera.DBPool.simple_pooled_db.PooledDB.addConnection
addConnection
Definition: simple_pooled_db.py:147
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection
Definition: simple_pooled_db.py:86
aestate.opera.DBPool.simple_pooled_db.PooledDB._unthreadsafe_add_connection
def _unthreadsafe_add_connection(self, con)
Definition: simple_pooled_db.py:178
aestate.opera.DBPool.simple_pooled_db.PooledDB._threadsafe_add_connection
def _threadsafe_add_connection(self, con)
Definition: simple_pooled_db.py:211
aestate.opera.DBPool.simple_pooled_db.PooledDBConnection._con
_con
Definition: simple_pooled_db.py:94
aestate.opera.DBPool.simple_pooled_db.PooledDBError
Definition: simple_pooled_db.py:78
aestate.opera.DBPool.simple_pooled_db.PooledDB._nextConnection
_nextConnection
Definition: simple_pooled_db.py:155
aestate.opera.DBPool.simple_pooled_db.NotSupportedError
Definition: simple_pooled_db.py:82
aestate.opera.DBPool.simple_pooled_db.PooledDB.__init__
def __init__(self, dbapi, maxconnections, *args, **kwargs)
Definition: simple_pooled_db.py:122