Use one wsgi app, one dbengine worker.

Return to using one wsgi application but use one database engine
per worker process.

Fixes Bug #1089916

Change-Id: Iea4b1b925208317c3c6ee3c9461b6723f3168405
This commit is contained in:
Therese McHale 2012-12-13 13:34:19 +00:00
parent 19b8b4b73b
commit 0e9111ca59
13 changed files with 110 additions and 58 deletions

View File

@ -57,7 +57,7 @@ if __name__ == '__main__':
glance.store.verify_default_store()
server = wsgi.Server()
server.start(config.load_paste_app, default_port=9292)
server.start(config.load_paste_app(), default_port=9292)
server.wait()
except exception.WorkerCreationFailure, e:
fail(2, e)

View File

@ -47,7 +47,7 @@ if __name__ == '__main__':
log.setup('glance')
server = wsgi.Server()
server.start(config.load_paste_app, default_port=9191)
server.start(config.load_paste_app(), default_port=9191)
server.wait()
except RuntimeError, e:
sys.exit("ERROR: %s" % e)

View File

@ -33,7 +33,7 @@ class ImageDataController(object):
def __init__(self, db_api=None, store_api=None,
policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api()
self.db_api.configure_db()
self.db_api.setup_db_env()
self.store_api = store_api or glance.store
self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()

View File

@ -24,7 +24,7 @@ import glance.db
class Controller(object):
def __init__(self, db=None):
self.db_api = db or glance.db.get_api()
self.db_api.configure_db()
self.db_api.setup_db_env()
@utils.mutating
def update(self, req, image_id, tag_value):

View File

@ -45,7 +45,7 @@ class ImagesController(object):
def __init__(self, db_api=None, policy_enforcer=None, notifier=None,
store_api=None):
self.db_api = db_api or glance.db.get_api()
self.db_api.configure_db()
self.db_api.setup_db_env()
self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.store_api = store_api or glance.store

View File

@ -168,8 +168,7 @@ class Server(object):
"""
Run a WSGI server with the given application.
:param application: A function that can be called with no arguments
that will return the application to run in the WSGI server
:param application: The application to be run in the WSGI server
:param default_port: Port to bind to if none is specified in conf
"""
def kill_children(*args):
@ -188,7 +187,7 @@ class Server(object):
signal.signal(signal.SIGHUP, signal.SIG_IGN)
self.running = False
self.app_func = application
self.application = application
self.sock = get_socket(default_port)
os.umask(027) # ensure files are created with the correct privileges
@ -197,7 +196,7 @@ class Server(object):
if CONF.workers == 0:
# Useful for profiling, test, debug etc.
self.pool = self.create_pool()
self.pool.spawn_n(self._single_run, self.app_func(), self.sock)
self.pool.spawn_n(self._single_run, self.application, self.sock)
return
else:
self.logger.info(_("Starting %d workers") % CONF.workers)
@ -276,7 +275,7 @@ class Server(object):
self.pool = self.create_pool()
try:
eventlet.wsgi.server(self.sock,
self.app_func(),
self.application,
log=WritableLogger(self.logger),
custom_pool=self.pool)
except socket.error, err:

View File

@ -55,6 +55,10 @@ def reset():
}
def setup_db_env(*args, **kwargs):
pass
def configure_db(*args, **kwargs):
pass

View File

@ -81,51 +81,28 @@ def ping_listener(dbapi_conn, connection_rec, connection_proxy):
raise
def setup_db_env():
"""
Setup configuation for database
"""
global sa_logger, _IDLE_TIMEOUT, _MAX_RETRIES, _RETRY_INTERVAL, _CONNECTION
_IDLE_TIMEOUT = CONF.sql_idle_timeout
_MAX_RETRIES = CONF.sql_max_retries
_RETRY_INTERVAL = CONF.sql_retry_interval
_CONNECTION = CONF.sql_connection
sa_logger = logging.getLogger('sqlalchemy.engine')
if CONF.debug:
sa_logger.setLevel(logging.DEBUG)
def configure_db():
"""
Establish the database, create an engine if needed, and
register the models.
"""
global _ENGINE, sa_logger, _MAX_RETRIES, _RETRY_INTERVAL
if not _ENGINE:
sql_connection = CONF.sql_connection
_MAX_RETRIES = CONF.sql_max_retries
_RETRY_INTERVAL = CONF.sql_retry_interval
connection_dict = sqlalchemy.engine.url.make_url(sql_connection)
engine_args = {'pool_recycle': CONF.sql_idle_timeout,
'echo': False,
'convert_unicode': True
}
try:
_ENGINE = sqlalchemy.create_engine(sql_connection, **engine_args)
if 'mysql' in connection_dict.drivername:
sqlalchemy.event.listen(_ENGINE, 'checkout', ping_listener)
_ENGINE.connect = wrap_db_error(_ENGINE.connect)
_ENGINE.connect()
except Exception, err:
msg = _("Error configuring registry database with supplied "
"sql_connection '%(sql_connection)s'. "
"Got error:\n%(err)s") % locals()
LOG.error(msg)
raise
sa_logger = logging.getLogger('sqlalchemy.engine')
if CONF.debug:
sa_logger.setLevel(logging.DEBUG)
if CONF.db_auto_create:
LOG.info(_('auto-creating glance registry DB'))
models.register_models(_ENGINE)
try:
migration.version_control()
except exception.DatabaseMigrationError:
# only arises when the DB exists and is under version control
pass
else:
LOG.info(_('not auto-creating glance registry DB'))
setup_db_env()
get_engine()
def check_mutate_authorization(context, image_ref):
@ -144,11 +121,73 @@ def get_session(autocommit=True, expire_on_commit=False):
"""Helper method to grab session"""
global _MAKER
if not _MAKER:
assert _ENGINE
get_engine()
get_maker(autocommit, expire_on_commit)
assert(_MAKER)
session = _MAKER()
return session
def get_engine():
"""Return a SQLAlchemy engine."""
"""May assign _ENGINE if not already assigned"""
global _ENGINE, sa_logger, _CONNECTION, _IDLE_TIMEOUT, _MAX_RETRIES,\
_RETRY_INTERVAL
if not _ENGINE:
tries = _MAX_RETRIES
retry_interval = _RETRY_INTERVAL
connection_dict = sqlalchemy.engine.url.make_url(_CONNECTION)
engine_args = {
'pool_recycle': _IDLE_TIMEOUT,
'echo': False,
'convert_unicode': True}
try:
_ENGINE = sqlalchemy.create_engine(_CONNECTION, **engine_args)
if 'mysql' in connection_dict.drivername:
sqlalchemy.event.listen(_ENGINE, 'checkout', ping_listener)
_ENGINE.connect = wrap_db_error(_ENGINE.connect)
_ENGINE.connect()
except Exception, err:
msg = _("Error configuring registry database with supplied "
"sql_connection '%s'. "
"Got error:\n%s") % (_CONNECTION, err)
LOG.error(msg)
raise
sa_logger = logging.getLogger('sqlalchemy.engine')
if CONF.debug:
sa_logger.setLevel(logging.DEBUG)
if CONF.db_auto_create:
LOG.info(_('auto-creating glance registry DB'))
models.register_models(_ENGINE)
try:
migration.version_control()
except exception.DatabaseMigrationError:
# only arises when the DB exists and is under version control
pass
else:
LOG.info(_('not auto-creating glance registry DB'))
return _ENGINE
def get_maker(autocommit=True, expire_on_commit=False):
"""Return a SQLAlchemy sessionmaker."""
"""May assign __MAKER if not already assigned"""
global _MAKER, _ENGINE
assert _ENGINE
if not _MAKER:
_MAKER = sa_orm.sessionmaker(bind=_ENGINE,
autocommit=autocommit,
expire_on_commit=expire_on_commit)
return _MAKER()
return _MAKER
def is_db_connection_error(args):

View File

@ -55,7 +55,7 @@ class Controller(object):
def __init__(self):
self.db_api = glance.db.get_api()
self.db_api.configure_db()
self.db_api.setup_db_env()
def _get_images(self, context, filters, **params):
"""

View File

@ -35,7 +35,7 @@ class Controller(object):
def __init__(self):
self.db_api = glance.db.get_api()
self.db_api.configure_db()
self.db_api.setup_db_env()
def index(self, req, image_id):
"""

View File

@ -71,7 +71,7 @@ class TestClientExceptions(functional.FunctionalTest):
server = wsgi.Server()
self.config(bind_host='127.0.0.1')
self.config(workers=0)
server.start(ExceptionTestApp, self.port)
server.start(ExceptionTestApp(), self.port)
self.client = client.BaseClient("127.0.0.1", self.port)
def _do_test_exception(self, path, exc_type):

View File

@ -90,8 +90,8 @@ class TestClientRedirects(functional.FunctionalTest):
server_two = wsgi.Server()
self.config(bind_host='127.0.0.1')
self.config(workers=0)
server_one.start(RedirectTestApp("one"), self.port_one)
server_two.start(RedirectTestApp("two"), self.port_two)
server_one.start(RedirectTestApp("one")(), self.port_one)
server_two.start(RedirectTestApp("two")(), self.port_two)
self.client = client.BaseClient("127.0.0.1", self.port_one)
def test_get_without_redirect(self):

View File

@ -58,6 +58,8 @@ class TestRegistryDb(test_utils.BaseTestCase):
super(TestRegistryDb, self).setUp()
self.stubs = stubout.StubOutForTesting()
self.orig_engine = db_api._ENGINE
self.orig_connection = db_api._CONNECTION
self.orig_maker = db_api._MAKER
def test_bad_sql_connection(self):
"""
@ -70,8 +72,11 @@ class TestRegistryDb(test_utils.BaseTestCase):
# We set this to None to trigger a reconfigure, otherwise
# other modules may have already correctly configured the DB
db_api._ENGINE = None
db_api._CONNECTION = None
db_api._MAKER = None
db_api.setup_db_env()
self.assertRaises((ImportError, exc.ArgumentError),
db_api.configure_db)
db_api.get_engine)
exc_raised = False
self.log_written = False
@ -82,6 +87,9 @@ class TestRegistryDb(test_utils.BaseTestCase):
self.stubs.Set(db_api.LOG, 'error', fake_log_error)
try:
api_obj = rserver.API(routes.Mapper())
api = test_utils.FakeAuthMiddleware(api_obj, is_admin=True)
req = webob.Request.blank('/images/%s' % _gen_uuid())
res = req.get_response(api)
except exc.ArgumentError:
exc_raised = True
except ImportError:
@ -94,6 +102,8 @@ class TestRegistryDb(test_utils.BaseTestCase):
"""Clear the test environment"""
super(TestRegistryDb, self).setUp()
db_api._ENGINE = self.orig_engine
db_api._CONNECTION = self.orig_connection
db_api._MAKER = self.orig_maker
self.stubs.UnsetAll()