[API][ADMIN_API]: Improvements to DB driver

* Use native python driver so connection problems don't hang DB
* Change connection algorithm:

Now uses the first DB as a master and will auto failover as required.
It will switch back to the first DB after 60 seconds.

Change-Id: I673503b212f6b6e29306c75beeb3fdbb44bdb488
This commit is contained in:
Andrew Hutchings
2013-09-13 14:22:02 +01:00
parent b81b70852d
commit 55b81ce291
2 changed files with 20 additions and 16 deletions

View File

@@ -18,7 +18,6 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref, sessionmaker, Session from sqlalchemy.orm import relationship, backref, sessionmaker, Session
import sqlalchemy.types as types import sqlalchemy.types as types
import time import time
import random
import ConfigParser import ConfigParser
from pecan import conf from pecan import conf
import logging import logging
@@ -146,12 +145,14 @@ class HealthMonitor(DeclarativeBase):
class RoutingSession(Session): class RoutingSession(Session):
""" If an engine is already in use, re-use it. Otherwise we can end up """ Try to use the first engine provided. If this fails use the next in
with deadlocks in Galera, see http://tinyurl.com/9h6qlly sequence and so on. Reset to the first after 60 seconds
switch engines every 60 seconds of idle time """ we do this because we can end up with deadlocks in Galera, see
http://tinyurl.com/9h6qlly """
engines = [] engines = {}
last_engine = None engines_count = 0
use_engine = 0
last_engine_time = 0 last_engine_time = 0
def get_bind(self, mapper=None, clause=None): def get_bind(self, mapper=None, clause=None):
@@ -159,14 +160,12 @@ class RoutingSession(Session):
self._build_engines() self._build_engines()
if ( if (
RoutingSession.last_engine RoutingSession.use_engine > 0
and time.time() < RoutingSession.last_engine_time + 60 and time.time() < RoutingSession.last_engine_time + 60
): ):
RoutingSession.last_engine_time = time.time() RoutingSession.last_engine_time = time.time()
return RoutingSession.last_engine RoutingSession.use_engine = 0
engine = random.choice(RoutingSession.engines) engine = RoutingSession.engines[RoutingSession.use_engine]
RoutingSession.last_engine = engine
RoutingSession.last_engine_time = time.time()
return engine return engine
def _build_engines(self): def _build_engines(self):
@@ -175,7 +174,7 @@ class RoutingSession(Session):
for section in conf.database: for section in conf.database:
db_conf = config._sections[section] db_conf = config._sections[section]
conn_string = '''mysql://%s:%s@%s:%d/%s''' % ( conn_string = '''mysql+mysqlconnector://%s:%s@%s:%d/%s''' % (
db_conf['username'], db_conf['username'],
db_conf['password'], db_conf['password'],
db_conf['host'], db_conf['host'],
@@ -199,7 +198,8 @@ class RoutingSession(Session):
conn_string, isolation_level="READ COMMITTED", conn_string, isolation_level="READ COMMITTED",
pool_size=20, pool_recycle=3600 pool_size=20, pool_recycle=3600
) )
RoutingSession.engines.append(engine) RoutingSession.engines[RoutingSession.engines_count] = engine
RoutingSession.engines_count += 1
class db_session(object): class db_session(object):
@@ -216,10 +216,14 @@ class db_session(object):
except: except:
self.logger.error( self.logger.error(
'Could not connect to DB server: {0}'.format( 'Could not connect to DB server: {0}'.format(
RoutingSession.last_engine.url RoutingSession.engines[RoutingSession.use_engine].url
) )
) )
RoutingSession.last_engine = None RoutingSession.last_engine_time = time.time()
if RoutingSession.use_engine == RoutingSession.engines_count:
RoutingSession.use_engine = 0
else:
RoutingSession.use_engine += 1
self.logger.error('Could not connect to any DB server') self.logger.error('Could not connect to any DB server')
return None return None

View File

@@ -10,5 +10,5 @@ dogapi
pecan pecan
sqlalchemy>=0.8.0 sqlalchemy>=0.8.0
wsme>=0.5b2 wsme>=0.5b2
MySQL-python mysql-connector-python
ipaddress==1.0.4 ipaddress==1.0.4