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