summaryrefslogtreecommitdiffstats
path: root/openstack/common/db/sqlalchemy/session.py
diff options
context:
space:
mode:
Diffstat (limited to 'openstack/common/db/sqlalchemy/session.py')
-rw-r--r--openstack/common/db/sqlalchemy/session.py74
1 files changed, 64 insertions, 10 deletions
diff --git a/openstack/common/db/sqlalchemy/session.py b/openstack/common/db/sqlalchemy/session.py
index b96123a..fbc5c91 100644
--- a/openstack/common/db/sqlalchemy/session.py
+++ b/openstack/common/db/sqlalchemy/session.py
@@ -281,6 +281,11 @@ database_opts = [
deprecated_name='sql_connection',
deprecated_group=DEFAULT,
secret=True),
+ cfg.StrOpt('slave_connection',
+ default='',
+ help='The SQLAlchemy connection string used to connect to the '
+ 'slave database',
+ secret=True),
cfg.IntOpt('idle_timeout',
default=3600,
deprecated_name='sql_idle_timeout',
@@ -334,6 +339,8 @@ LOG = logging.getLogger(__name__)
_ENGINE = None
_MAKER = None
+_SLAVE_ENGINE = None
+_SLAVE_MAKER = None
def set_defaults(sql_connection, sqlite_db):
@@ -346,6 +353,7 @@ def set_defaults(sql_connection, sqlite_db):
def cleanup():
global _ENGINE, _MAKER
+ global _SLAVE_ENGINE, _SLAVE_MAKER
if _MAKER:
_MAKER.close_all()
@@ -353,6 +361,12 @@ def cleanup():
if _ENGINE:
_ENGINE.dispose()
_ENGINE = None
+ if _SLAVE_MAKER:
+ _SLAVE_MAKER.close_all()
+ _SLAVE_MAKER = None
+ if _SLAVE_ENGINE:
+ _SLAVE_ENGINE.dispose()
+ _SLAVE_ENGINE = None
class SqliteForeignKeysListener(PoolListener):
@@ -368,15 +382,25 @@ class SqliteForeignKeysListener(PoolListener):
def get_session(autocommit=True, expire_on_commit=False,
- sqlite_fk=False):
+ sqlite_fk=False, slave_session=False):
"""Return a SQLAlchemy session."""
global _MAKER
+ global _SLAVE_MAKER
+ maker = _MAKER
+
+ if slave_session:
+ maker = _SLAVE_MAKER
- if _MAKER is None:
- engine = get_engine(sqlite_fk=sqlite_fk)
- _MAKER = get_maker(engine, autocommit, expire_on_commit)
+ if maker is None:
+ engine = get_engine(sqlite_fk=sqlite_fk, slave_engine=slave_session)
+ maker = get_maker(engine, autocommit, expire_on_commit)
- session = _MAKER()
+ if slave_session:
+ _SLAVE_MAKER = maker
+ else:
+ _MAKER = maker
+
+ session = maker()
return session
@@ -491,13 +515,26 @@ def _wrap_db_error(f):
return _wrap
-def get_engine(sqlite_fk=False):
+def get_engine(sqlite_fk=False, slave_engine=False):
"""Return a SQLAlchemy engine."""
global _ENGINE
- if _ENGINE is None:
- _ENGINE = create_engine(CONF.database.connection,
- sqlite_fk=sqlite_fk)
- return _ENGINE
+ global _SLAVE_ENGINE
+ engine = _ENGINE
+ db_uri = CONF.database.connection
+
+ if slave_engine:
+ engine = _SLAVE_ENGINE
+ db_uri = CONF.database.slave_connection
+
+ if engine is None:
+ engine = create_engine(db_uri,
+ sqlite_fk=sqlite_fk)
+ if slave_engine:
+ _SLAVE_ENGINE = engine
+ else:
+ _ENGINE = engine
+
+ return engine
def _synchronous_switch_listener(dbapi_conn, connection_rec):
@@ -555,6 +592,11 @@ def _is_db_connection_error(args):
def create_engine(sql_connection, sqlite_fk=False):
"""Return a new SQLAlchemy engine."""
+ # NOTE(geekinutah): At this point we could be connecting to the normal
+ # db handle or the slave db handle. Things like
+ # _wrap_db_error aren't going to work well if their
+ # backends don't match. Let's check.
+ _assert_matching_drivers()
connection_dict = sqlalchemy.engine.url.make_url(sql_connection)
engine_args = {
@@ -696,3 +738,15 @@ def _patch_mysqldb_with_stacktrace_comments():
old_mysql_do_query(self, qq)
setattr(MySQLdb.cursors.BaseCursor, '_do_query', _do_query)
+
+
+def _assert_matching_drivers():
+ """Make sure slave handle and normal handle have the same driver."""
+ # NOTE(geekinutah): There's no use case for writing to one backend and
+ # reading from another. Who knows what the future holds?
+ if CONF.database.slave_connection == '':
+ return
+
+ normal = sqlalchemy.engine.url.make_url(CONF.database.connection)
+ slave = sqlalchemy.engine.url.make_url(CONF.database.slave_connection)
+ assert normal.drivername == slave.drivername