Make sure dead connections are handled in a thread-safe manner
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
try:
|
||||||
|
from Queue import PriorityQueue
|
||||||
|
except ImportError:
|
||||||
|
from queue import PriorityQueue
|
||||||
|
|
||||||
class ConnectionSelector(object):
|
class ConnectionSelector(object):
|
||||||
" Base class for Selectors. "
|
" Base class for Selectors. "
|
||||||
def __init__(self, opts):
|
def __init__(self, opts):
|
||||||
@@ -22,7 +27,7 @@ class RoundRobinSelector(ConnectionSelector):
|
|||||||
class ConnectionPool(object):
|
class ConnectionPool(object):
|
||||||
def __init__(self, connections, dead_timeout=60, selector_class=RoundRobinSelector, randomize_hosts=True, **kwargs):
|
def __init__(self, connections, dead_timeout=60, selector_class=RoundRobinSelector, randomize_hosts=True, **kwargs):
|
||||||
self.connections = [c for (c, opts) in connections]
|
self.connections = [c for (c, opts) in connections]
|
||||||
self.dead = []
|
self.dead = PriorityQueue(len(self.connections))
|
||||||
|
|
||||||
if randomize_hosts:
|
if randomize_hosts:
|
||||||
# randomize the connection list to avoid all clients hitting same node
|
# randomize the connection list to avoid all clients hitting same node
|
||||||
@@ -40,23 +45,26 @@ class ConnectionPool(object):
|
|||||||
try:
|
try:
|
||||||
self.connections.remove(connection)
|
self.connections.remove(connection)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# connection not alive, ignore
|
# connection not alive or another thread marked it already, ignore
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
# TODO: detect repeated failure and extend the timeout
|
# TODO: detect repeated failure and extend the timeout
|
||||||
self.dead.append((now + self.dead_timeout, connection))
|
self.dead.put((now + self.dead_timeout, connection))
|
||||||
|
|
||||||
def resurrect(self, force=False):
|
def resurrect(self, force=False):
|
||||||
# no dead connections
|
# no dead connections
|
||||||
if not self.dead:
|
if self.dead.empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
# no elligible connections to retry
|
# retrieve a connection to check
|
||||||
if not force and self.dead[0][0] > time.time():
|
timeout, connection = self.dead.get()
|
||||||
|
|
||||||
|
if not force and timeout > time.time():
|
||||||
|
# return it back if not eligible and not forced
|
||||||
|
self.dead.put((timeout, connection))
|
||||||
return
|
return
|
||||||
|
|
||||||
# either we were forced or the node is elligible to be retried
|
# either we were forced or the connection is elligible to be retried
|
||||||
connection = self.dead.pop(0)[1]
|
|
||||||
self.connections.append(connection)
|
self.connections.append(connection)
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ class TestConnectionPool(TestCase):
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
pool.mark_dead(42, now=now)
|
pool.mark_dead(42, now=now)
|
||||||
self.assertEquals(99, len(pool.connections))
|
self.assertEquals(99, len(pool.connections))
|
||||||
self.assertEquals([(now + 60, 42)], pool.dead)
|
self.assertEquals(1, pool.dead.qsize())
|
||||||
|
self.assertEquals((now + 60, 42), pool.dead.get())
|
||||||
|
|
||||||
def test_connection_is_skipped_when_dead(self):
|
def test_connection_is_skipped_when_dead(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(2)])
|
pool = ConnectionPool([(x, {}) for x in range(2)])
|
||||||
|
|||||||
Reference in New Issue
Block a user