|
# This file is part of Buildbot. Buildbot is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright Buildbot Team Members
A wrapper around `sqlalchemy.create_engine` that handles all of the special cases that Buildbot needs. Those include:
- pool_recycle for MySQL - %(basedir) substitution - optimal thread pool size calculation
"""
# from http://www.mail-archive.com/sqlalchemy@googlegroups.com/msg15079.html self.retried = False
# A subclass of the ThreadLocalEngineStrategy that can effectively interact # with Buildbot. # # This adjusts the passed-in parameters to ensure that we get the behaviors # Buildbot wants from particular drivers, and wraps the outgoing Engine # object so that its methods run in threads and return deferreds.
"""For sqlite, percent-substitute %(basedir)s and use a full path to the basedir. If using a memory database, force the pool size to be 1."""
# when given a database path, stick the basedir in there
# Use NullPool instead of the sqlalchemy-0.6.8-default # SingletonThreadpool for sqlite to suppress the error in # http://groups.google.com/group/sqlalchemy/msg/f8482e4721a89589, # which also explains that NullPool is the new default in # sqlalchemy 0.7 for non-memory SQLite databases.
# in-memory databases need exactly one connection
# allow serializing access to the db u.query.pop('serialize_access') max_conns = 1
"""Special setup for sqlite engines""" # try to enable WAL logging
class CheckpointFullfsyncDisabler(object): pass disabler = CheckpointFullfsyncDisabler() disabler.connect = connect_listener engine.pool.add_listener(disabler) else:
except: log.msg("failed to set journal mode - database may fail")
"""For mysql, take max_idle out of the query arguments, and use its value for pool_recycle. Also, force use_unicode and charset to be True and 'utf8', failing if they were set to anything else."""
# default to the InnoDB storage engine 'init_command' : 'SET storage_engine=%s' % storage_engine, }
"(and adds it automatically)") else:
"(and adds it automatically)") else:
"""Special setup for mysql engines""" # add the reconnecting PoolListener that will detect a # disconnected connection and automatically start a new # one. This provides a measure of additional safety over # the pool_recycle parameter, and is useful when e.g., the # mysql server goes away def checkout_listener(dbapi_con, con_record, con_proxy): try: cursor = dbapi_con.cursor() cursor.execute("SELECT 1") except dbapi_con.OperationalError, ex: if ex.args[0] in (2006, 2013, 2014, 2045, 2055): # sqlalchemy will re-create the connection raise sa.exc.DisconnectionError() raise
# older versions of sqlalchemy require the listener to be specified # in the kwargs, in a class instance if sautils.sa_version() < (0,7,0): class ReconnectingListener(object): pass rcl = ReconnectingListener() rcl.checkout = checkout_listener engine.pool.add_listener(rcl) else: sa.event.listen(engine.pool, 'checkout', checkout_listener)
raise TypeError('no basedir supplied to create_engine')
# apply special cases elif u.drivername.startswith('mysql'): u, kwargs, max_conns = self.special_case_mysql(u, kwargs)
# remove the basedir as it may confuse sqlalchemy
# calculate the maximum number of connections from the pool parameters, # if it hasn't already been specified
u, **kwargs)
# annotate the engine with the optimal thread pool size; this is used # by DBConnector to configure the surrounding thread pool
# keep the basedir
elif u.drivername.startswith('mysql'): self.set_up_mysql_engine(u, engine)
# this module is really imported for the side-effects, but pyflakes will like # us to use something from the module -- so offer a copy of create_engine, # which explicitly adds the strategy argument
|