mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-08-11 02:43:15 +00:00
Fix for CONPY-245:
Instead of iterating through all connections and checking the health status via ping, used and unused connections were separated in different lists. This ensures that the last used connection will be always the first.
This commit is contained in:
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
import mariadb
|
import mariadb
|
||||||
import _thread
|
import _thread
|
||||||
import time
|
|
||||||
|
|
||||||
MAX_POOL_SIZE = 64
|
MAX_POOL_SIZE = 64
|
||||||
POOL_IDLE_TIMEOUT = 1800
|
POOL_IDLE_TIMEOUT = 1800
|
||||||
@ -63,7 +62,8 @@ class ConnectionPool(object):
|
|||||||
Will reset the connection before returning it to the pool.
|
Will reset the connection before returning it to the pool.
|
||||||
Default value is True.
|
Default value is True.
|
||||||
"""
|
"""
|
||||||
self._connections = []
|
self._connections_free = []
|
||||||
|
self._connections_used = []
|
||||||
self._pool_args = {}
|
self._pool_args = {}
|
||||||
self._conn_args = {}
|
self._conn_args = {}
|
||||||
self._lock_pool = _thread.RLock()
|
self._lock_pool = _thread.RLock()
|
||||||
@ -107,15 +107,15 @@ class ConnectionPool(object):
|
|||||||
except mariadb.Error:
|
except mariadb.Error:
|
||||||
# if an error occurred, close all connections
|
# if an error occurred, close all connections
|
||||||
# and raise exception
|
# and raise exception
|
||||||
for j in range(0, len(self._connections)):
|
for j in range(0, len(self._connections_free)):
|
||||||
try:
|
try:
|
||||||
self._connections[j].close()
|
self._connections_free[j].close()
|
||||||
except mariadb.Error:
|
except mariadb.Error:
|
||||||
# connect failed, so we are not
|
# connect failed, so we are not
|
||||||
# interested in errors
|
# interested in errors
|
||||||
# from close() method
|
# from close() method
|
||||||
pass
|
pass
|
||||||
del self._connections[j]
|
del self._connections_free[j]
|
||||||
raise
|
raise
|
||||||
self.add_connection(connection)
|
self.add_connection(connection)
|
||||||
|
|
||||||
@ -151,20 +151,19 @@ class ConnectionPool(object):
|
|||||||
raise mariadb.PoolError("Can't get configuration for pool %s" %
|
raise mariadb.PoolError("Can't get configuration for pool %s" %
|
||||||
self._pool_args["name"])
|
self._pool_args["name"])
|
||||||
|
|
||||||
if len(self._connections) >= self._pool_args["size"]:
|
total = len(self._connections_free) + len(self._connections_used)
|
||||||
|
if total >= self._pool_args["size"]:
|
||||||
raise mariadb.PoolError("Can't add connection to pool %s: "
|
raise mariadb.PoolError("Can't add connection to pool %s: "
|
||||||
"No free slot available (%s)." %
|
"No free slot available (%s)." %
|
||||||
(self._pool_args["name"],
|
(self._pool_args["name"],
|
||||||
len(self._connections)))
|
total))
|
||||||
|
|
||||||
with self._lock_pool:
|
with self._lock_pool:
|
||||||
if connection is None:
|
if connection is None:
|
||||||
connection = mariadb.Connection(**self._conn_args)
|
connection = mariadb.Connection(**self._conn_args)
|
||||||
|
|
||||||
connection._Connection__pool = self
|
connection._Connection__pool = self
|
||||||
connection._Connection__in_use = 0
|
self._connections_free.append(connection)
|
||||||
connection._Connection__last_used = time.monotonic()
|
|
||||||
self._connections.append(connection)
|
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
"""
|
"""
|
||||||
@ -172,25 +171,21 @@ class ConnectionPool(object):
|
|||||||
exception if a connection is not available.
|
exception if a connection is not available.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
now = time.monotonic()
|
|
||||||
conn = None
|
conn = None
|
||||||
timediff = -1
|
|
||||||
|
|
||||||
with self._lock_pool:
|
with self._lock_pool:
|
||||||
for i in range(0, len(self._connections)):
|
for i in range(0, len(self._connections_free)):
|
||||||
if not self._connections[i]._Connection__in_use:
|
try:
|
||||||
try:
|
self._connections_free[i].ping()
|
||||||
self._connections[i].ping()
|
except mariadb.Error:
|
||||||
except mariadb.Error:
|
continue
|
||||||
continue
|
conn = self._connections_free[i]
|
||||||
t = now - self._connections[i]._Connection__last_used
|
conn._used += 1
|
||||||
if t > timediff:
|
self._connections_used.append(conn)
|
||||||
conn = self._connections[i]
|
del self._connections_free[i]
|
||||||
timediff = t
|
return conn
|
||||||
|
|
||||||
if conn:
|
return None
|
||||||
conn._Connection__in_use = 1
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def _close_connection(self, connection):
|
def _close_connection(self, connection):
|
||||||
"""
|
"""
|
||||||
@ -201,8 +196,12 @@ class ConnectionPool(object):
|
|||||||
with self._lock_pool:
|
with self._lock_pool:
|
||||||
if self._pool_args["reset_connection"]:
|
if self._pool_args["reset_connection"]:
|
||||||
connection.reset()
|
connection.reset()
|
||||||
connection._Connection__in_use = 0
|
|
||||||
connection._Connection__last_used = time.monotonic()
|
for i in range(0, len(self._connections_used)):
|
||||||
|
if self._connections_used[i] == connection:
|
||||||
|
del self._connections_used[i]
|
||||||
|
self._connections_free.append(connection)
|
||||||
|
return
|
||||||
|
|
||||||
def set_config(self, **kwargs):
|
def set_config(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -218,11 +217,15 @@ class ConnectionPool(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
"""Closes connection pool and all connections."""
|
"""Closes connection pool and all connections."""
|
||||||
try:
|
try:
|
||||||
for c in self._connections:
|
for c in self._connections_free:
|
||||||
|
c._Connection__pool = None
|
||||||
|
c.close()
|
||||||
|
for c in self._connections_used:
|
||||||
c._Connection__pool = None
|
c._Connection__pool = None
|
||||||
c.close()
|
c.close()
|
||||||
finally:
|
finally:
|
||||||
self._connections = None
|
self._connections_free = None
|
||||||
|
self._connections_used = None
|
||||||
del mariadb._CONNECTION_POOLS[self._pool_args["name"]]
|
del mariadb._CONNECTION_POOLS[self._pool_args["name"]]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -51,10 +51,9 @@ class Connection(mariadb._mariadb.connection):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
self._socket = None
|
self._socket = None
|
||||||
self.__in_use = 0
|
self._used = 0
|
||||||
self._last_executed_statement = None
|
self._last_executed_statement = None
|
||||||
self._socket = None
|
self._socket = None
|
||||||
self.__in_use = 0
|
|
||||||
self.__pool = None
|
self.__pool = None
|
||||||
self.__last_used = 0
|
self.__last_used = 0
|
||||||
self.tpc_state = TPC_STATE.NONE
|
self.tpc_state = TPC_STATE.NONE
|
||||||
|
@ -36,6 +36,29 @@ class TestPooling(unittest.TestCase):
|
|||||||
except mariadb.ProgrammingError:
|
except mariadb.ProgrammingError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_conpy245(self):
|
||||||
|
# we can't test performance here, but we can check if LRU works.
|
||||||
|
# All connections must have been used the same number of times.
|
||||||
|
|
||||||
|
default_conf = conf()
|
||||||
|
pool_size = 64
|
||||||
|
iterations = 100
|
||||||
|
|
||||||
|
pool = mariadb.ConnectionPool(pool_name="CONPY245",
|
||||||
|
pool_size=pool_size,
|
||||||
|
**default_conf)
|
||||||
|
for i in range(0, iterations):
|
||||||
|
for j in range(0, pool_size):
|
||||||
|
conn = pool.get_connection()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
for i in range(0, pool_size):
|
||||||
|
conn = pool.get_connection()
|
||||||
|
self.assertEqual(conn._used, iterations + 1)
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
pool.close()
|
||||||
|
|
||||||
def test_connection_pool_conf(self):
|
def test_connection_pool_conf(self):
|
||||||
pool = mariadb.ConnectionPool(pool_name="test_conf")
|
pool = mariadb.ConnectionPool(pool_name="test_conf")
|
||||||
default_conf = conf()
|
default_conf = conf()
|
||||||
|
Reference in New Issue
Block a user