diff options
Diffstat (limited to 'openstack/common/db/sqlalchemy/session.py')
-rw-r--r-- | openstack/common/db/sqlalchemy/session.py | 74 |
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 |