summaryrefslogtreecommitdiff
path: root/oslo
diff options
context:
space:
mode:
authorRoman Podoliaka <rpodolyaka@mirantis.com>2014-05-21 13:05:43 +0300
committerRoman Podoliaka <rpodolyaka@mirantis.com>2014-06-10 17:25:06 +0300
commit1d2df1a2a3d77dd39744b9bf91a73a4451193732 (patch)
tree5fc6c4cbcbc913bd67b2c0a41338c822242a1df7 /oslo
parentf9167b0e1e7a5dd0bf63d22f17fca99a995ee560 (diff)
downloadoslo-db-1d2df1a2a3d77dd39744b9bf91a73a4451193732.tar.gz
Handle slave database connection in EngineFacade
Make it possible to use an additional (aka, 'slave') database connection. This might be useful for offloading of read operations to reduce the load on the RDBMS. Currently, this is only used in Nova, but can be ported to other projects easily. Change-Id: I4296347bb9ee0743738fe1126d1cef8e43b9f96e
Diffstat (limited to 'oslo')
-rw-r--r--oslo/db/options.py4
-rw-r--r--oslo/db/sqlalchemy/session.py92
2 files changed, 73 insertions, 23 deletions
diff --git a/oslo/db/options.py b/oslo/db/options.py
index 19058d0..72e626c 100644
--- a/oslo/db/options.py
+++ b/oslo/db/options.py
@@ -39,6 +39,10 @@ database_opts = [
group='DATABASE'),
cfg.DeprecatedOpt('connection',
group='sql'), ]),
+ cfg.StrOpt('slave_connection',
+ secret=True,
+ help='The SQLAlchemy connection string to use to connect to the'
+ ' slave database.'),
cfg.StrOpt('mysql_sql_mode',
default='TRADITIONAL',
help='The SQL mode to be used for MySQL sessions. '
diff --git a/oslo/db/sqlalchemy/session.py b/oslo/db/sqlalchemy/session.py
index 3b3a4ca..056644f 100644
--- a/oslo/db/sqlalchemy/session.py
+++ b/oslo/db/sqlalchemy/session.py
@@ -793,11 +793,22 @@ class EngineFacade(object):
"""
- def __init__(self, sql_connection,
+ def __init__(self, sql_connection, slave_connection=None,
sqlite_fk=False, autocommit=True,
expire_on_commit=False, **kwargs):
"""Initialize engine and sessionmaker instances.
+ :param sql_connection: the connection string for the database to use
+ :type sql_connection: string
+
+ :param slave_connection: the connection string for the 'slave' database
+ to use. If not provided, the master database
+ will be used for all operations. Note: this
+ is meant to be used for offloading of read
+ operations to asynchronously replicated slaves
+ to reduce the load on the master database.
+ :type slave_connection: string
+
:param sqlite_fk: enable foreign keys in SQLite
:type sqlite_fk: bool
@@ -839,39 +850,73 @@ class EngineFacade(object):
super(EngineFacade, self).__init__()
- self._engine = create_engine(
- sql_connection=sql_connection,
- sqlite_fk=sqlite_fk,
- mysql_sql_mode=kwargs.get('mysql_sql_mode', 'TRADITIONAL'),
- idle_timeout=kwargs.get('idle_timeout', 3600),
- connection_debug=kwargs.get('connection_debug', 0),
- max_pool_size=kwargs.get('max_pool_size'),
- max_overflow=kwargs.get('max_overflow'),
- pool_timeout=kwargs.get('pool_timeout'),
- sqlite_synchronous=kwargs.get('sqlite_synchronous', True),
- connection_trace=kwargs.get('connection_trace', False),
- max_retries=kwargs.get('max_retries', 10),
- retry_interval=kwargs.get('retry_interval', 10),
- thread_checkin=kwargs.get('thread_checkin', True))
- self._session_maker = get_maker(
- engine=self._engine,
- autocommit=autocommit,
- expire_on_commit=expire_on_commit)
-
- def get_engine(self):
- """Get the engine instance (note, that it's shared)."""
+ engine_kwargs = {
+ 'sqlite_fk': sqlite_fk,
+ 'mysql_sql_mode': kwargs.get('mysql_sql_mode', 'TRADITIONAL'),
+ 'idle_timeout': kwargs.get('idle_timeout', 3600),
+ 'connection_debug': kwargs.get('connection_debug', 0),
+ 'max_pool_size': kwargs.get('max_pool_size'),
+ 'max_overflow': kwargs.get('max_overflow'),
+ 'pool_timeout': kwargs.get('pool_timeout'),
+ 'sqlite_synchronous': kwargs.get('sqlite_synchronous', True),
+ 'connection_trace': kwargs.get('connection_trace', False),
+ 'max_retries': kwargs.get('max_retries', 10),
+ 'retry_interval': kwargs.get('retry_interval', 10),
+ 'thread_checkin': kwargs.get('thread_checkin', True)
+ }
+ maker_kwargs = {
+ 'autocommit': autocommit,
+ 'expire_on_commit': expire_on_commit
+ }
+
+ self._engine = create_engine(sql_connection=sql_connection,
+ **engine_kwargs)
+ self._session_maker = get_maker(engine=self._engine,
+ **maker_kwargs)
+ if slave_connection:
+ self._slave_engine = create_engine(sql_connection=slave_connection,
+ **engine_kwargs)
+ self._slave_session_maker = get_maker(engine=self._slave_engine,
+ **maker_kwargs)
+ else:
+ self._slave_engine = None
+ self._slave_session_maker = None
+
+ def get_engine(self, use_slave=False):
+ """Get the engine instance (note, that it's shared).
+
+ :param use_slave: if possible, use 'slave' database for this engine.
+ If the connection string for the slave database
+ wasn't provided, 'master' engine will be returned.
+ (defaults to False)
+ :type use_slave: bool
+
+ """
+
+ if use_slave and self._slave_engine:
+ return self._slave_engine
return self._engine
- def get_session(self, **kwargs):
+ def get_session(self, use_slave=False, **kwargs):
"""Get a Session instance.
+ :param use_slave: if possible, use 'slave' database connection for
+ this session. If the connection string for the
+ slave database wasn't provided, a session bound
+ to the 'master' engine will be returned.
+ (defaults to False)
+ :type use_slave: bool
+
Keyword arugments will be passed to a sessionmaker instance as is (if
passed, they will override the ones used when the sessionmaker instance
was created). See SQLAlchemy Session docs for details.
"""
+ if use_slave and self._slave_session_maker:
+ return self._slave_session_maker(**kwargs)
+
return self._session_maker(**kwargs)
@classmethod
@@ -896,6 +941,7 @@ class EngineFacade(object):
conf.register_opts(options.database_opts, 'database')
return cls(sql_connection=conf.database.connection,
+ slave_connection=conf.database.slave_connection,
sqlite_fk=sqlite_fk,
autocommit=autocommit,
expire_on_commit=expire_on_commit,