Add eventlet db_pool use for mysql

This adds the use of eventlet's db_pool module so that we can make mysql
calls without blocking the whole process.
New config options are introduced:

sql_dbpool_enable -- Enables the use of eventlet's db_pool
sql_min_pool_size -- Set the minimum number of SQL connections
sql_max_pool_size -- Set the maximum number of SQL connections
sql_idle_timeout  -- Timeout before idle sql connections are reaped

The default for sql_dbpool_enable is False for now, so there is
no forced behavior changes for those using mysql. sql_min_pool_size
is defaulted to 1 to match behavior if not using db_pool.

Fixes bug 1086173

Change-Id: Ied0aae33211585743fe955028a75c4e192a15d2f
This commit is contained in:
Gary Kotton 2012-12-10 12:39:22 +00:00
parent ca8ed5eee6
commit 7b5f6fd6ae
23 changed files with 283 additions and 39 deletions

View File

@ -10,8 +10,18 @@ sql_connection = sqlite://
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[RESTPROXY]
# All configuration for this plugin is in section '[restproxy]'

View File

@ -30,8 +30,18 @@ sql_connection = sqlite://
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[LINUX_BRIDGE]
# (ListOpt) Comma-separated list of

View File

@ -9,9 +9,20 @@ sql_connection = mysql://root:password@localhost/quantum_metaplugin?charset=utf8
# Database reconnection retry times - in event connectivity is lost
# set to -1 implgies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[META]
## This is list of flavor:quantum_plugins
# extension method is used in the order of this list

View File

@ -10,8 +10,18 @@ sql_connection = sqlite://
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[OVS]
# Do not change this parameter unless you have a good reason to.

View File

@ -10,8 +10,18 @@ sql_connection = sqlite://
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[NVP]
# The number of logical ports to create per bridged logical switch

View File

@ -8,8 +8,18 @@ sql_connection = sqlite://
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# sql_max_retries = 10
# Database reconnection interval in seconds - in event connectivity is lost
# Database reconnection interval in seconds - if the initial connection to the
# database fails
reconnect_interval = 2
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[OVS]
# (StrOpt) Type of network to allocate for tenant networks. The

View File

@ -3,6 +3,15 @@
# Example: sql_connection = mysql://root:nova@127.0.0.1:3306/ryu_quantum
#sql_connection = mysql://<user>:<pass>@<IP>:<port>/<dbname>
sql_connection = sqlite://
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
# sql_dbpool_enable = False
# Minimum number of SQL connections to keep open in a pool
# sql_min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# sql_max_pool_size = 5
# Timeout in seconds before idle sql connections are reaped
# sql_idle_timeout = 3600
[OVS]
integration_bridge = br-int

View File

@ -274,7 +274,7 @@ class Controller(object):
# plugin raised might have been created or not in the db.
# We need a way for ensuring that if it has been created,
# it is then deleted
raise
raise ex
def create(self, request, body=None, **kwargs):
"""Creates a new instance of the requested entity"""

View File

@ -19,6 +19,12 @@
import time
from eventlet import db_pool
from eventlet import greenthread
try:
import MySQLdb
except ImportError:
MySQLdb = None
import sqlalchemy as sql
from sqlalchemy import create_engine
from sqlalchemy.exc import DisconnectionError
@ -87,10 +93,31 @@ def configure_db(options):
if 'mysql' in connection_dict.drivername:
engine_args['listeners'] = [MySQLPingListener()]
if (MySQLdb is not None and
options['sql_dbpool_enable']):
pool_args = {
'db': connection_dict.database,
'passwd': connection_dict.password or '',
'host': connection_dict.host,
'user': connection_dict.username,
'min_size': options['sql_min_pool_size'],
'max_size': options['sql_max_pool_size'],
'max_idle': options['sql_idle_timeout']
}
creator = db_pool.ConnectionPool(MySQLdb, **pool_args)
engine_args['creator'] = creator.create
if (MySQLdb is None and options['sql_dbpool_enable']):
LOG.warn(_("Eventlet connection pooling will not work without "
"python-mysqldb!"))
if 'sqlite' in connection_dict.drivername:
engine_args['listeners'] = [SqliteForeignKeysListener()]
if options['sql_connection'] == "sqlite://":
engine_args["connect_args"] = {'check_same_thread': False}
_ENGINE = create_engine(options['sql_connection'], **engine_args)
sql.event.listen(_ENGINE, 'checkin', greenthread_yield)
base = options.get('base', BASE)
if not register_models(base):
if 'reconnect_interval' in options:
@ -156,3 +183,13 @@ def unregister_models(base=BASE):
global _ENGINE
assert _ENGINE
base.metadata.drop_all(_ENGINE)
def greenthread_yield(dbapi_con, con_record):
"""
Ensure other greenthreads get a chance to execute by forcing a context
switch. With common database backends (eg MySQLdb and sqlite), there is
no implicit yield caused by network I/O since they are implemented by
C libraries that eventlet cannot monkey patch.
"""
greenthread.sleep(0)

View File

@ -910,11 +910,11 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
obj_creator = getattr(self, 'create_%s' % resource)
objects.append(obj_creator(context, item))
context.session.commit()
except Exception:
except Exception as e:
LOG.exception(_("An exception occured while creating "
"the %(resource)s:%(item)s"), locals())
context.session.rollback()
raise
raise e
return objects
def create_network_bulk(self, context, networks):

View File

@ -70,6 +70,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
@ -265,7 +280,11 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
options = {"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2}
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable}
db.configure_db(options)
# 'servers' is the list of network controller REST end-points

View File

@ -37,6 +37,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
bridge_opts = [

View File

@ -29,11 +29,16 @@ LOG = logging.getLogger(__name__)
def initialize():
options = {"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection}
options.update({"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries})
options.update({"reconnect_interval":
cfg.CONF.DATABASE.reconnect_interval})
options.update({"base": models_v2.model_base.BASEV2})
options = {
"sql_connection": cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable
}
db.configure_db(options)

View File

@ -22,6 +22,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
meta_plugin_opts = [

View File

@ -50,12 +50,16 @@ class MetaPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
def __init__(self, configfile=None):
LOG.debug(_("Start initializing metaplugin"))
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
options.update({'base': models_v2.model_base.BASEV2})
sql_max_retries = cfg.CONF.DATABASE.sql_max_retries
options.update({"sql_max_retries": sql_max_retries})
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
options.update({"reconnect_interval": reconnect_interval})
options = {
"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable
}
self.supported_extension_aliases = \
cfg.CONF.META.supported_extension_aliases.split(',')
self.supported_extension_aliases += ['flavor', 'router']

View File

@ -24,6 +24,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
ovs_opts = [

View File

@ -33,7 +33,11 @@ def initialize():
options = {"sql_connection": "%s" % config.DATABASE.sql_connection,
"sql_max_retries": config.DATABASE.sql_max_retries,
"reconnect_interval": config.DATABASE.reconnect_interval,
"base": model_base.BASEV2}
"base": model_base.BASEV2,
"sql_min_pool_size": config.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": config.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": config.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": config.CONF.DATABASE.sql_dbpool_enable}
db.configure_db(options)
@ -67,7 +71,7 @@ def add_ofc_item(model, id, quantum_id):
item = model(id=id, quantum_id=quantum_id)
session.add(item)
session.flush()
except sa.exc.IntegrityError as exc:
except Exception as exc:
LOG.exception(exc)
raise nexc.NECDBException
return item
@ -103,7 +107,7 @@ def add_portinfo(id, datapath_id='', port_no=0, vlan_id=OFP_VLAN_NONE, mac=''):
port_no=port_no, vlan_id=vlan_id, mac=mac)
session.add(portinfo)
session.flush()
except sa.exc.IntegrityError as exc:
except Exception as exc:
LOG.exception(exc)
raise nexc.NECDBException
return portinfo

View File

@ -67,12 +67,16 @@ def parse_config():
NVPCluster objects, 'plugin_config' is a dictionary with plugin
parameters (currently only 'max_lp_per_bridged_ls').
"""
db_options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
db_options.update({'base': models_v2.model_base.BASEV2})
sql_max_retries = cfg.CONF.DATABASE.sql_max_retries
db_options.update({"sql_max_retries": sql_max_retries})
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
db_options.update({"reconnect_interval": reconnect_interval})
db_options = {
"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable
}
nvp_options = {'max_lp_per_bridged_ls': cfg.CONF.NVP.max_lp_per_bridged_ls}
nvp_options.update({'failover_time': cfg.CONF.NVP.failover_time})
nvp_options.update({'concurrent_connections':

View File

@ -21,6 +21,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
nvp_opts = [

View File

@ -25,6 +25,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
ovs_opts = [

View File

@ -30,11 +30,16 @@ LOG = logging.getLogger(__name__)
def initialize():
options = {"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection}
options.update({"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries})
options.update({"reconnect_interval":
cfg.CONF.DATABASE.reconnect_interval})
options.update({"base": models_v2.model_base.BASEV2})
options = {
"sql_connection": cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable
}
db.configure_db(options)

View File

@ -21,6 +21,21 @@ database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
cfg.IntOpt('sql_max_retries', default=-1),
cfg.IntOpt('reconnect_interval', default=2),
cfg.IntOpt('sql_min_pool_size',
default=1,
help="Minimum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_max_pool_size',
default=5,
help="Maximum number of SQL connections to keep open in a "
"pool"),
cfg.IntOpt('sql_idle_timeout',
default=3600,
help="Timeout in seconds before idle sql connections are "
"reaped"),
cfg.BoolOpt('sql_dbpool_enable',
default=False,
help="Enable the use of eventlet's db_pool for MySQL"),
]
ovs_opts = [

View File

@ -55,10 +55,16 @@ class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
supported_extension_aliases = ["router"]
def __init__(self, configfile=None):
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
options.update({'base': models_v2.model_base.BASEV2})
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
options.update({"reconnect_interval": reconnect_interval})
options = {
"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection,
"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries,
"reconnect_interval": cfg.CONF.DATABASE.reconnect_interval,
"base": models_v2.model_base.BASEV2,
"sql_min_pool_size": cfg.CONF.DATABASE.sql_min_pool_size,
"sql_max_pool_size": cfg.CONF.DATABASE.sql_max_pool_size,
"sql_idle_timeout": cfg.CONF.DATABASE.sql_idle_timeout,
"sql_dbpool_enable": cfg.CONF.DATABASE.sql_dbpool_enable
}
db.configure_db(options)
self.tunnel_key = db_api_v2.TunnelKey(