Retry to connect database when DB2 or mongodb is restarted

The patch https://review.openstack.org/#/c/122387 works fine
with operations with get, record and update functions.
But exception would still occured with the operation of
db.collection.find() function.

This patch can give some benefit to tolerate DB restart
with find() function.
This patch also removes "test_mongo_find" test case since
it doesn't raise AutoReconnect exception at all.

Closes-Bug: #1389985
Change-Id: Ia0474726960ce2b4b611fda0a1c304bb8ad96922
(cherry-picked from commit 8c6841d3c0)
This commit is contained in:
lqslan 2014-11-18 15:38:51 +08:00 committed by Lan Qi Song
parent b3307b1d6c
commit 529b4aaf50
2 changed files with 39 additions and 29 deletions

View File

@ -182,13 +182,12 @@ class ConnectionPool(object):
try:
if cfg.CONF.database.mongodb_replica_set:
client = MongoProxy(
Prefection(
pymongo.MongoReplicaSetClient(
url,
replicaSet=cfg.CONF.database.mongodb_replica_set)))
pymongo.MongoReplicaSetClient(
url,
replicaSet=cfg.CONF.database.mongodb_replica_set))
else:
client = MongoProxy(
Prefection(pymongo.MongoClient(url, safe=True)))
pymongo.MongoClient(url, safe=True))
return client
except pymongo.errors.ConnectionFailure as e:
LOG.warn(_('Unable to connect to the database server: '
@ -368,6 +367,12 @@ class MongoProxy(object):
"""
return MongoProxy(self.conn[item])
def find(self, *args, **kwargs):
# We need this modifying method to return a CursorProxy object so that
# we can handle the Cursor next function to catch the AutoReconnect
# exception.
return CursorProxy(self.conn.find(*args, **kwargs))
def __getattr__(self, item):
"""Wrap MongoDB connection.
@ -385,20 +390,25 @@ class MongoProxy(object):
return self.conn(*args, **kwargs)
class Prefection(pymongo.collection.Collection):
def __init__(self, conn):
self.conn = conn
class CursorProxy(pymongo.cursor.Cursor):
def __init__(self, cursor):
self.cursor = cursor
def find(self, *args, **kwargs):
# We need this modifying method to check a connection for MongoDB
# in context of MongoProxy approach. Initially 'find' returns Cursor
# object and doesn't connect to db while Cursor is not used.
found = self.find(*args, **kwargs)
def __getitem__(self, item):
return self.cursor[item]
@safe_mongo_call
def next(self):
"""Wrap Cursor next method.
This method will be executed before each Cursor next method call.
"""
try:
found[0]
except IndexError:
pass
return found
save_cursor = self.cursor.clone()
return self.cursor.next()
except pymongo.errors.AutoReconnect:
self.cursor = save_cursor
raise
def __getattr__(self, item):
return getattr(self.conn, item)
return getattr(self.cursor, item)

View File

@ -3110,10 +3110,10 @@ class MongoAutoReconnectTest(DBTestBase,
@tests_db.run_with('mongodb')
def test_mongo_client(self):
if cfg.CONF.database.mongodb_replica_set:
self.assertIsInstance(self.conn.conn.conn.conn,
self.assertIsInstance(self.conn.conn.conn,
pymongo.MongoReplicaSetClient)
else:
self.assertIsInstance(self.conn.conn.conn.conn,
self.assertIsInstance(self.conn.conn.conn,
pymongo.MongoClient)
@staticmethod
@ -3126,16 +3126,16 @@ class MongoAutoReconnectTest(DBTestBase,
return side_effect
@tests_db.run_with('mongodb')
def test_mongo_find(self):
def test_mongo_cursor_next(self):
expected_first_sample_timestamp = datetime.datetime(2012, 7, 2, 10, 39)
raise_exc = [False, True]
method = self.conn.db.resource.find
with mock.patch('pymongo.collection.Collection.find',
mock.Mock()) as mock_find:
mock_find.side_effect = self.create_side_effect(method, raise_exc)
mock_find.__name__ = 'find'
resources = list(self.conn.get_resources())
self.assertEqual(9, len(resources))
method = self.conn.db.resource.find().cursor.next
with mock.patch('pymongo.cursor.Cursor.next',
mock.Mock()) as mock_next:
mock_next.side_effect = self.create_side_effect(method, raise_exc)
resource = self.conn.db.resource.find().next()
self.assertEqual(expected_first_sample_timestamp,
resource['first_sample_timestamp'])
@tests_db.run_with('mongodb')
def test_mongo_insert(self):