Permalink
Browse files

Implement alive() for Connection and RSC PYTHON-275

1 parent 9b2b750 commit eeb1ff8fde25be5dea73af4bcd1c53a7f6404e20 @ajdavis ajdavis committed Nov 5, 2012
@@ -8,6 +8,7 @@
.. automethod:: disconnect
.. automethod:: close
+ .. automethod:: alive
.. describe:: c[db_name] || c.db_name
@@ -8,6 +8,7 @@
.. automethod:: disconnect
.. automethod:: close
+ .. automethod:: alive
.. describe:: c[db_name] || c.db_name
View
@@ -735,6 +735,35 @@ def close(self):
"""
self.disconnect()
+ def alive(self):
+ """Return ``False`` if there has been an error communicating with the
+ server, else ``True``.
+
+ This method attempts to check the status of the server with minimal I/O.
+ The current thread / greenlet retrieves a socket from the pool (its
+ request socket if it's in a request, or a random idle socket if it's not
+ in a request) and checks whether calling `select`_ on it raises an
+ error. If there are currently no idle sockets, :meth:`alive` will
+ attempt to actually connect to the server.
+
+ A more certain way to determine server availability is::
+
+ connection.admin.command('ping')
+
+ .. _select: http://docs.python.org/2/library/select.html#select.select
+ """
+ # In the common case, a socket is available and was used recently, so
+ # calling select() on it is a reasonable attempt to see if the OS has
+ # reported an error. Note this can be wasteful: __socket implicitly
+ # calls select() if the socket hasn't been checked in the last second,
+ # or it may create a new socket, in which case calling select() is
+ # redundant.
+ try:
+ sock_info = self.__socket()
+ return not pool._closed(sock_info.sock)
+ except (socket.error, ConnectionFailure):
+ return False
+
def set_cursor_manager(self, manager_class):
"""Set this connection's cursor manager.
@@ -921,6 +921,36 @@ def close(self):
self.__writer = None
self.__members = {}
+ def alive(self):
+ """Return ``False`` if there has been an error communicating with the
+ primary, else ``True``.
+
+ This method attempts to check the status of the primary with minimal
+ I/O. The current thread / greenlet retrieves a socket (its request
+ socket if it's in a request, or a random idle socket if it's not in a
+ request) from the primary's connection pool and checks whether calling
+ select_ on it raises an error. If there are currently no idle sockets,
+ or if there is no known primary, :meth:`alive` will attempt to actually
+ find and connect to the primary.
+
+ A more certain way to determine primary availability is to ping it::
+
+ connection.admin.command('ping')
+
+ .. _select: http://docs.python.org/2/library/select.html#select.select
+ """
+ # In the common case, a socket is available and was used recently, so
+ # calling select() on it is a reasonable attempt to see if the OS has
+ # reported an error. Note this can be wasteful: __socket implicitly
+ # calls select() if the socket hasn't been checked in the last second,
+ # or it may create a new socket, in which case calling select() is
+ # redundant.
+ try:
+ sock_info = self.__socket(self.__find_primary())
+ return not pool._closed(sock_info.sock)
+ except (socket.error, ConnectionFailure):
+ return False
+
def __check_response_to_last_error(self, response):
"""Check a response to a lastError message for errors.
@@ -677,6 +677,45 @@ def tearDown(self):
self.c.close()
ha_tools.kill_all_members()
+
+class TestAlive(unittest.TestCase):
+ def setUp(self):
+ members = [{}, {}]
+ self.seed, self.name = ha_tools.start_replica_set(members)
+
+ def test_alive(self):
+ primary = ha_tools.get_primary()
+ secondary = ha_tools.get_random_secondary()
+ primary_cx = Connection(primary, use_greenlets=use_greenlets)
+ secondary_cx = Connection(secondary, use_greenlets=use_greenlets)
+ rsc = ReplicaSetConnection(
+ self.seed, replicaSet=self.name, use_greenlets=use_greenlets)
+
+ try:
+ self.assertTrue(primary_cx.alive())
+ self.assertTrue(secondary_cx.alive())
+ self.assertTrue(rsc.alive())
+
+ ha_tools.kill_primary()
+ time.sleep(0.5)
+
+ self.assertFalse(primary_cx.alive())
+ self.assertTrue(secondary_cx.alive())
+ self.assertFalse(rsc.alive())
+
+ ha_tools.kill_members([secondary], 2)
+ time.sleep(0.5)
+
+ self.assertFalse(primary_cx.alive())
+ self.assertFalse(secondary_cx.alive())
+ self.assertFalse(rsc.alive())
+ finally:
+ rsc.close()
+
+ def tearDown(self):
+ ha_tools.kill_all_members()
+
+
class TestMongosHighAvailability(unittest.TestCase):
def setUp(self):
seed_list = ha_tools.create_sharded_cluster()

0 comments on commit eeb1ff8

Please sign in to comment.