Files
mariadb-connector-python/mariadb/connectionpool.py

223 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import mariadb, sys
import _thread, time
MAX_POOL_SIZE = 64
POOL_IDLE_TIMEOUT = 1800
class ConnectionPool(object):
"""
Class defining a pool of database connections
MariaDB Connector/Python supports simple connection pooling.
A connection pool holds a number of open connections and handles
thread safety when providing connections to threads.
The size of a connection pool is configurable at creation time,
but cannot be changed afterwards. The maximum size of a connection
pool is limited to 64 connections.
"""
def __init__(self, *args, **kwargs):
"""
Creates a connetion pool class
:param str pool_name:
Name of connection pool
:param int pool_size:
Size of pool. If not specified default value of 5 will be used.
Maximum allowed number is 64.
:param bool pool_reset_connection:
Will reset the connection before returning it to the pool.
Default value is True.
"""
self._connections = []
self._pool_args = {}
self._conn_args = {}
self._lock_pool = _thread.RLock()
key_words= ["pool_name", "pool_size", "pool_reset_connection"]
# check if pool_name was provided
if kwargs and "pool_name" in kwargs:
# check if pool_name already exists
if kwargs["pool_name"] in mariadb._CONNECTION_POOLS:
raise mariadb.ProgrammingError("Pool '%s' already exists" \
% kwargs["pool_name"])
else:
raise mariadb.ProgrammingError("No pool name specified")
# save pool keyword arguments
self._pool_args["name"]= kwargs.get("pool_name")
self._pool_args["size"]= kwargs.get("pool_size", 5);
self._pool_args["reset_connection"]= \
kwargs.get("pool_reset_connection", True)
# validate pool size (must be in range between 1 and MAX_POOL_SIZE)
if not (0 < self._pool_args["size"] <= MAX_POOL_SIZE):
raise mariadb.ProgrammError("Pool size must be in range of "\
"1 and %s" % MAX_POOL_SIZE)
# store pool and connection arguments
self._conn_args= kwargs.copy()
for key in key_words:
if key in self._conn_args:
del self._conn_args[key]
if len(self._conn_args) > 0:
with self._lock_pool:
# fill connection pool
for i in range(0, self._pool_args["size"]):
try:
connection= mariadb.Connection(**self._conn_args)
except:
# if an error occured, close all connections and raise exception
for j in range(0, len(self._connections)):
try:
self._connections[j].close()
except:
# connect failed, so we are not interested in errors
# from close() method
pass
del self._connections[j]
raise
self.add_connection(connection)
# store connection pool in _CONNECTION_POOLS
mariadb._CONNECTION_POOLS[self._pool_args["name"]]= self
def add_connection(self, connection= None):
"""
Adds a connection object to the connection pool.
In case that the pool doesnt have a free slot or is not configured
a PoolError exception will be raised.
"""
if not self._conn_args:
raise mariadb.PoolError("Couldn't get configuration for pool %s" % \
self._pool_args["name"])
if connection is not None and \
not isinstance(connection, mariadb.connections.Connection):
raise TypeError("Passed parameter is not a connection object")
if connection == None and len(self._conn_args) == 0:
raise mariadb.PoolError("Can't get configuration for pool %s" % \
self._pool_args["name"])
if len(self._connections) >= self._pool_args["size"]:
raise mariadb.PoolError("Can't add connection to pool %s: "
"No free slot available (%s)." % (self._pool_args["name"], len(self._connections)))
with self._lock_pool:
if connection is None:
connection= mariadb.Connection(**self._conn_args)
connection._Connection__pool= self
connection._Connection__in_use= 0
connection._Connection__last_used= time.monotonic()
self._connections.append(connection)
def get_connection(self):
"""
Returns a connection from the connection pool or raises a PoolError
exception if a connection is not available.
"""
now= time.monotonic()
conn= None
timediff= 0
with self._lock_pool:
for i in range(0, len(self._connections)):
if not self._connections[i]._Connection__in_use:
try:
self._connections[i].ping()
except:
continue
t = now - self._connections[i]._Connection__last_used
if t > timediff:
conn= self._connections[i]
timediff = t
if conn:
conn._Connection__in_use= 1
return conn
def _close_connection(self, connection):
"""
Returns connection to the pool. Internally used
by connection object.
"""
with self._lock_pool:
if self.pool_args["reset_connection"]:
connection.reset()
connection._Connection__in_use= 0
connection._Connection__last_used= time.monotonic()
def set_config(self, **kwargs):
"""
:param dict configuration
configuration settings for pooled connection
Sets the connection configuration for the connection pool.
For valid connection arguments check the mariadb.connect() method.
Note: This method doesn't create connections in the pool.
To fill the pool one has to use add_connection() ḿethod.
"""
self._conn_args= kwargs;
def close(self):
"""Closes connection pool and all connections."""
try:
for c in self._connections:
c._Connection__pool= None
c.close()
finally:
self._connections= None
del mariadb._CONNECTION_POOLS[self._pool_args["name"]]
@property
def pool_name(self):
"""Returns the name of the connection pool."""
return self._pool_args["name"]
@property
def pool_size(self):
"""Returns the size of the connection pool."""
return self._pool_args["size"]
@property
def max_size(self):
"Returns the maximum size for connection pools."""
return MAX_POOL_SIZE
@property
def connection_count(self):
"Returns the number of connections in connection pool."""
return self._connections
@property
def pool_reset_connection(self):
"""
If set to true, the connection will be reset on both client and server side\n
after .close() method was called
"""
return self._pool_args["reset_connection"]
@pool_reset_connection.setter
def pool_reset_connection(self, reset):
self._pool_args["reset_connection"]= reset