1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  """ 
 17  A wrapper around `sqlalchemy.create_engine` that handles all of the 
 18  special cases that Buildbot needs.  Those include: 
 19   
 20   - pool_recycle for MySQL 
 21   - %(basedir) substitution 
 22   - optimal thread pool size calculation 
 23   
 24  """ 
 25   
 26  import os 
 27  import sqlalchemy 
 28  from sqlalchemy.engine import strategies, url 
 29  from sqlalchemy.pool import NullPool 
 30   
 31   
 35 -    def checkout(self, dbapi_con, con_record, con_proxy): 
  36          try: 
 37              try: 
 38                  dbapi_con.ping(False) 
 39              except TypeError: 
 40                  dbapi_con.ping() 
 41          except dbapi_con.OperationalError, ex: 
 42              if ex.args[0] in (2006, 2013, 2014, 2045, 2055): 
 43                   
 44                  raise sqlalchemy.exc.DisconnectionError() 
 45              raise 
   46   
 48      """ 
 49      A subclass of the ThreadLocalEngineStrategy that can effectively interact 
 50      with Buildbot. 
 51   
 52      This adjusts the passed-in parameters to ensure that we get the behaviors 
 53      Buildbot wants from particular drivers, and wraps the outgoing Engine 
 54      object so that its methods run in threads and return deferreds. 
 55      """ 
 56   
 57      name = 'buildbot' 
 58   
 60          """For sqlite, percent-substitute %(basedir)s and use a full 
 61          path to the basedir.  If using a memory database, force the 
 62          pool size to be 1.""" 
 63          max_conns = None 
 64   
 65           
 66          if u.database: 
 67   
 68               
 69               
 70               
 71               
 72               
 73              kwargs.setdefault('poolclass', NullPool) 
 74   
 75              u.database = u.database % dict(basedir = kwargs['basedir']) 
 76              if not os.path.isabs(u.database[0]): 
 77                  u.database = os.path.join(kwargs['basedir'], u.database) 
 78   
 79           
 80          if not u.database: 
 81              kwargs['pool_size'] = 1 
 82              max_conns = 1 
 83   
 84           
 85          if 'serialize_access' in u.query: 
 86              u.query.pop('serialize_access') 
 87              max_conns = 1 
 88   
 89          return u, kwargs, max_conns 
  90   
 92          """For mysql, take max_idle out of the query arguments, and 
 93          use its value for pool_recycle.  Also, force use_unicode and 
 94          charset to be True and 'utf8', failing if they were set to 
 95          anything else.""" 
 96   
 97          kwargs['pool_recycle'] = int(u.query.pop('max_idle', 3600)) 
 98   
 99           
100          storage_engine = u.query.pop('storage_engine', 'MyISAM') 
101          kwargs['connect_args'] = { 
102              'init_command' : 'SET storage_engine=%s' % storage_engine, 
103          } 
104   
105          if 'use_unicode' in u.query: 
106              if u.query['use_unicode'] != "True": 
107                  raise TypeError("Buildbot requires use_unicode=True " + 
108                                   "(and adds it automatically)") 
109          else: 
110              u.query['use_unicode'] = True 
111   
112          if 'charset' in u.query: 
113              if u.query['charset'] != "utf8": 
114                  raise TypeError("Buildbot requires charset=utf8 " + 
115                                   "(and adds it automatically)") 
116          else: 
117              u.query['charset'] = 'utf8' 
118   
119           
120           
121           
122           
123           
124          kwargs['listeners'] = [ ReconnectingListener() ] 
125   
126          return u, kwargs, None 
 127   
128 -    def create(self, name_or_url, **kwargs): 
 129          if 'basedir' not in kwargs: 
130              raise TypeError('no basedir supplied to create_engine') 
131   
132          max_conns = None 
133   
134           
135          u = url.make_url(name_or_url) 
136          if u.drivername.startswith('sqlite'): 
137              u, kwargs, max_conns = self.special_case_sqlite(u, kwargs) 
138          elif u.drivername.startswith('mysql'): 
139              u, kwargs, max_conns = self.special_case_mysql(u, kwargs) 
140   
141           
142          basedir = kwargs.pop('basedir') 
143   
144           
145           
146          if max_conns is None: 
147              max_conns = kwargs.get('pool_size', 5) + kwargs.get('max_overflow', 10) 
148   
149          engine = strategies.ThreadLocalEngineStrategy.create(self, 
150                                              u, **kwargs) 
151   
152           
153           
154          engine.optimal_thread_pool_size = max_conns 
155   
156           
157          engine.buildbot_basedir = basedir 
158   
159          return engine 
  160   
161  BuildbotEngineStrategy() 
162   
163   
164   
165   
167      kwargs['strategy'] = 'buildbot' 
168   
169      return sqlalchemy.create_engine(*args, **kwargs) 
 170