diff options
Diffstat (limited to 'oslo/db/api.py')
-rw-r--r-- | oslo/db/api.py | 216 |
1 files changed, 1 insertions, 215 deletions
diff --git a/oslo/db/api.py b/oslo/db/api.py index 906e88d..c0453b5 100644 --- a/oslo/db/api.py +++ b/oslo/db/api.py @@ -1,4 +1,3 @@ -# Copyright (c) 2013 Rackspace Hosting # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,217 +12,4 @@ # License for the specific language governing permissions and limitations # under the License. -""" -================================= -Multiple DB API backend support. -================================= - -A DB backend module should implement a method named 'get_backend' which -takes no arguments. The method can return any object that implements DB -API methods. -""" - -import logging -import threading -import time - -from oslo.utils import importutils -import six - -from oslo.db._i18n import _LE -from oslo.db import exception -from oslo.db import options - - -LOG = logging.getLogger(__name__) - - -def safe_for_db_retry(f): - """Indicate api method as safe for re-connection to database. - - Database connection retries will be enabled for the decorated api method. - Database connection failure can have many causes, which can be temporary. - In such cases retry may increase the likelihood of connection. - - Usage:: - - @safe_for_db_retry - def api_method(self): - self.engine.connect() - - - :param f: database api method. - :type f: function. - """ - f.__dict__['enable_retry'] = True - return f - - -class wrap_db_retry(object): - """Decorator class. Retry db.api methods, if DBConnectionError() raised. - - Retry decorated db.api methods. If we enabled `use_db_reconnect` - in config, this decorator will be applied to all db.api functions, - marked with @safe_for_db_retry decorator. - Decorator catches DBConnectionError() and retries function in a - loop until it succeeds, or until maximum retries count will be reached. - - Keyword arguments: - - :param retry_interval: seconds between transaction retries - :type retry_interval: int - - :param max_retries: max number of retries before an error is raised - :type max_retries: int - - :param inc_retry_interval: determine increase retry interval or not - :type inc_retry_interval: bool - - :param max_retry_interval: max interval value between retries - :type max_retry_interval: int - """ - - def __init__(self, retry_interval, max_retries, inc_retry_interval, - max_retry_interval): - super(wrap_db_retry, self).__init__() - - self.retry_interval = retry_interval - self.max_retries = max_retries - self.inc_retry_interval = inc_retry_interval - self.max_retry_interval = max_retry_interval - - def __call__(self, f): - @six.wraps(f) - def wrapper(*args, **kwargs): - next_interval = self.retry_interval - remaining = self.max_retries - - while True: - try: - return f(*args, **kwargs) - except exception.DBConnectionError as e: - if remaining == 0: - LOG.exception(_LE('DB exceeded retry limit.')) - raise exception.DBError(e) - if remaining != -1: - remaining -= 1 - LOG.exception(_LE('DB connection error.')) - # NOTE(vsergeyev): We are using patched time module, so - # this effectively yields the execution - # context to another green thread. - time.sleep(next_interval) - if self.inc_retry_interval: - next_interval = min( - next_interval * 2, - self.max_retry_interval - ) - return wrapper - - -class DBAPI(object): - """Initialize the chosen DB API backend. - - After initialization API methods is available as normal attributes of - ``DBAPI`` subclass. Database API methods are supposed to be called as - DBAPI instance methods. - - :param backend_name: name of the backend to load - :type backend_name: str - - :param backend_mapping: backend name -> module/class to load mapping - :type backend_mapping: dict - :default backend_mapping: None - - :param lazy: load the DB backend lazily on the first DB API method call - :type lazy: bool - :default lazy: False - - :keyword use_db_reconnect: retry DB transactions on disconnect or not - :type use_db_reconnect: bool - - :keyword retry_interval: seconds between transaction retries - :type retry_interval: int - - :keyword inc_retry_interval: increase retry interval or not - :type inc_retry_interval: bool - - :keyword max_retry_interval: max interval value between retries - :type max_retry_interval: int - - :keyword max_retries: max number of retries before an error is raised - :type max_retries: int - """ - - def __init__(self, backend_name, backend_mapping=None, lazy=False, - **kwargs): - - self._backend = None - self._backend_name = backend_name - self._backend_mapping = backend_mapping or {} - self._lock = threading.Lock() - - if not lazy: - self._load_backend() - - self.use_db_reconnect = kwargs.get('use_db_reconnect', False) - self.retry_interval = kwargs.get('retry_interval', 1) - self.inc_retry_interval = kwargs.get('inc_retry_interval', True) - self.max_retry_interval = kwargs.get('max_retry_interval', 10) - self.max_retries = kwargs.get('max_retries', 20) - - def _load_backend(self): - with self._lock: - if not self._backend: - # Import the untranslated name if we don't have a mapping - backend_path = self._backend_mapping.get(self._backend_name, - self._backend_name) - LOG.debug('Loading backend %(name)r from %(path)r', - {'name': self._backend_name, - 'path': backend_path}) - backend_mod = importutils.import_module(backend_path) - self._backend = backend_mod.get_backend() - - def __getattr__(self, key): - if not self._backend: - self._load_backend() - - attr = getattr(self._backend, key) - if not hasattr(attr, '__call__'): - return attr - # NOTE(vsergeyev): If `use_db_reconnect` option is set to True, retry - # DB API methods, decorated with @safe_for_db_retry - # on disconnect. - if self.use_db_reconnect and hasattr(attr, 'enable_retry'): - attr = wrap_db_retry( - retry_interval=self.retry_interval, - max_retries=self.max_retries, - inc_retry_interval=self.inc_retry_interval, - max_retry_interval=self.max_retry_interval)(attr) - - return attr - - @classmethod - def from_config(cls, conf, backend_mapping=None, lazy=False): - """Initialize DBAPI instance given a config instance. - - :param conf: oslo.config config instance - :type conf: oslo.config.cfg.ConfigOpts - - :param backend_mapping: backend name -> module/class to load mapping - :type backend_mapping: dict - - :param lazy: load the DB backend lazily on the first DB API method call - :type lazy: bool - - """ - - conf.register_opts(options.database_opts, 'database') - - return cls(backend_name=conf.database.backend, - backend_mapping=backend_mapping, - lazy=lazy, - use_db_reconnect=conf.database.use_db_reconnect, - retry_interval=conf.database.db_retry_interval, - inc_retry_interval=conf.database.db_inc_retry_interval, - max_retry_interval=conf.database.db_max_retry_interval, - max_retries=conf.database.db_max_retries) +from oslo_db.api import * # noqa |