diff options
| author | Michael Wilson <geekinutah@gmail.com> | 2013-05-16 18:51:41 -0600 |
|---|---|---|
| committer | Michael Wilson <geekinutah@gmail.com> | 2013-06-03 07:34:55 -0600 |
| commit | 444bdbcf36c189de65b524a596b79d48bcdb6cd5 (patch) | |
| tree | e20142a4d24f81a2579dba822c635f046f6e08bb /openstack/common/db/sqlalchemy | |
| parent | 98e1d15a6b4d773fd292ce0630115feba041cbfc (diff) | |
| download | oslo-444bdbcf36c189de65b524a596b79d48bcdb6cd5.tar.gz oslo-444bdbcf36c189de65b524a596b79d48bcdb6cd5.tar.xz oslo-444bdbcf36c189de65b524a596b79d48bcdb6cd5.zip | |
Add a slave db handle for the SQLAlchemy backend.
Adds an option for a slave DB connection for scaling
purposes. Read queries can be sent here to spread out
db load a bit. This patch adds a new configuration option:
slave_connection.
Implements: blueprint db-slave-handle
Change-Id: I5cc5e0696e3fcd8609ee6b5466b0377ca52b8b58
DocImpact
Diffstat (limited to 'openstack/common/db/sqlalchemy')
| -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 |
