Merge "reconnect to mongodb on connection failure"
This commit is contained in:
commit
1fa9302fee
@ -19,6 +19,9 @@
|
||||
"""Common functions for MongoDB and DB2 backends
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from oslo.config import cfg
|
||||
import pymongo
|
||||
import weakref
|
||||
|
||||
@ -31,6 +34,11 @@ from ceilometer import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
cfg.CONF.import_opt('max_retries', 'ceilometer.openstack.common.db.options',
|
||||
group="database")
|
||||
cfg.CONF.import_opt('retry_interval', 'ceilometer.openstack.common.db.options',
|
||||
group="database")
|
||||
|
||||
|
||||
def make_timestamp_range(start, end,
|
||||
start_timestamp_op=None, end_timestamp_op=None):
|
||||
@ -120,12 +128,33 @@ class ConnectionPool(object):
|
||||
log_data = {'db': splitted_url.scheme,
|
||||
'nodelist': connection_options['nodelist']}
|
||||
LOG.info(_('Connecting to %(db)s on %(nodelist)s') % log_data)
|
||||
client = pymongo.MongoClient(
|
||||
url,
|
||||
safe=True)
|
||||
client = self._mongo_connect(url)
|
||||
self._pool[pool_key] = weakref.ref(client)
|
||||
return client
|
||||
|
||||
@staticmethod
|
||||
def _mongo_connect(url):
|
||||
max_retries = cfg.CONF.database.max_retries
|
||||
retry_interval = cfg.CONF.database.retry_interval
|
||||
attempts = 0
|
||||
while True:
|
||||
try:
|
||||
client = pymongo.MongoClient(url, safe=True)
|
||||
except pymongo.errors.ConnectionFailure as e:
|
||||
if max_retries >= 0 and attempts >= max_retries:
|
||||
LOG.error(_('Unable to connect to the database after '
|
||||
'%(retries)d retries. Giving up.') %
|
||||
{'retries': max_retries})
|
||||
raise
|
||||
LOG.warn(_('Unable to connect to the database server: '
|
||||
'%(errmsg)s. Trying again in %(retry_interval)d '
|
||||
'seconds.') %
|
||||
{'errmsg': e, 'retry_interval': retry_interval})
|
||||
attempts += 1
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
return client
|
||||
|
||||
|
||||
COMMON_AVAILABLE_CAPABILITIES = {
|
||||
'meters': {'query': {'simple': True,
|
||||
|
@ -13,14 +13,19 @@
|
||||
"""Tests the mongodb and db2 common functionality
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
from mock import call
|
||||
from mock import patch
|
||||
import pymongo
|
||||
import testscenarios
|
||||
|
||||
from ceilometer.openstack.common.gettextutils import _
|
||||
from ceilometer.publisher import utils
|
||||
from ceilometer import sample
|
||||
from ceilometer.storage import pymongo_base
|
||||
from ceilometer.tests import db as tests_db
|
||||
from ceilometer.tests.storage import test_storage_scenarios
|
||||
|
||||
@ -172,3 +177,40 @@ class CompatibilityTest(test_storage_scenarios.DBTestBase,
|
||||
def test_counter_unit(self):
|
||||
meters = list(self.conn.get_meters())
|
||||
self.assertEqual(1, len(meters))
|
||||
|
||||
def test_mongodb_connect_raises_after_custom_number_of_attempts(self):
|
||||
retry_interval = 13
|
||||
max_retries = 37
|
||||
self.CONF.set_override(
|
||||
'retry_interval', retry_interval, group='database')
|
||||
self.CONF.set_override(
|
||||
'max_retries', max_retries, group='database')
|
||||
# PyMongo is being used to connect even to DB2, but it only
|
||||
# accepts URLs with the 'mongodb' scheme. This replacement is
|
||||
# usually done in the DB2 connection implementation, but since
|
||||
# we don't call that, we have to do it here.
|
||||
self.CONF.set_override(
|
||||
'connection', self.db_manager.url.replace('db2:', 'mongodb:', 1),
|
||||
group='database')
|
||||
|
||||
pool = pymongo_base.ConnectionPool()
|
||||
with contextlib.nested(
|
||||
patch('pymongo.MongoClient',
|
||||
side_effect=pymongo.errors.ConnectionFailure('foo')),
|
||||
patch.object(pymongo_base.LOG, 'error'),
|
||||
patch.object(pymongo_base.LOG, 'warn'),
|
||||
patch.object(pymongo_base.time, 'sleep')
|
||||
) as (MockMongo, MockLOGerror, MockLOGwarn, Mocksleep):
|
||||
self.assertRaises(pymongo.errors.ConnectionFailure,
|
||||
pool.connect, self.CONF.database.connection)
|
||||
Mocksleep.assert_has_calls([call(retry_interval)
|
||||
for i in range(max_retries)])
|
||||
MockLOGwarn.assert_any_call(
|
||||
_('Unable to connect to the database server: %(errmsg)s.'
|
||||
' Trying again in %(retry_interval)d seconds.') %
|
||||
{'errmsg': 'foo',
|
||||
'retry_interval': retry_interval})
|
||||
MockLOGerror.assert_called_with(
|
||||
_('Unable to connect to the database after '
|
||||
'%(retries)d retries. Giving up.') %
|
||||
{'retries': max_retries})
|
||||
|
Loading…
Reference in New Issue
Block a user