Merge ~cjwatson/launchpad:rename-master-slave-flavors into launchpad:master
- Git
- lp:~cjwatson/launchpad
- rename-master-slave-flavors
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | e3abd7078ce5ff79b9a69bc4064ac0eab9ed983a |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:rename-master-slave-flavors |
Merge into: | launchpad:master |
Diff against target: |
885 lines (+155/-153) 11 files modified
lib/lp/services/database/doc/db-policy.txt (+1/-1) lib/lp/services/database/interfaces.py (+16/-16) lib/lp/services/database/policy.py (+36/-36) lib/lp/services/oauth/tests/test_oauth.py (+6/-5) lib/lp/services/verification/model/logintoken.py (+2/-2) lib/lp/services/webapp/adapter.py (+8/-8) lib/lp/services/webapp/doc/test_adapter.txt (+4/-4) lib/lp/services/webapp/doc/test_adapter_permissions.txt (+16/-15) lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled (+3/-3) lib/lp/services/webapp/publication.py (+5/-5) lib/lp/services/webapp/tests/test_dbpolicy.py (+58/-58) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jürgen Gmach | Approve | ||
Review via email: mp+411554@code.launchpad.net |
Commit message
Rename master/slave DB flavors to primary/standby
Description of the change
The corresponding `I*Store` interfaces are still called master/slave, so there's still some mixed terminology, but we're getting closer.
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/services/database/doc/db-policy.txt b/lib/lp/services/database/doc/db-policy.txt |
2 | index 17dc147..8a1cce2 100644 |
3 | --- a/lib/lp/services/database/doc/db-policy.txt |
4 | +++ b/lib/lp/services/database/doc/db-policy.txt |
5 | @@ -83,7 +83,7 @@ resources. |
6 | ... Person, Person.name == 'janitor').one() |
7 | Traceback (most recent call last): |
8 | ... |
9 | - lp.services.database.interfaces.DisallowedStore: master |
10 | + lp.services.database.interfaces.DisallowedStore: primary |
11 | |
12 | We can even ensure no database activity occurs at all, for instance |
13 | if we need to guarantee a potentially long running call doesn't access |
14 | diff --git a/lib/lp/services/database/interfaces.py b/lib/lp/services/database/interfaces.py |
15 | index c9709ef..14407be 100644 |
16 | --- a/lib/lp/services/database/interfaces.py |
17 | +++ b/lib/lp/services/database/interfaces.py |
18 | @@ -14,8 +14,8 @@ __all__ = [ |
19 | 'IStore', |
20 | 'IStoreSelector', |
21 | 'MAIN_STORE', |
22 | - 'MASTER_FLAVOR', |
23 | - 'SLAVE_FLAVOR', |
24 | + 'PRIMARY_FLAVOR', |
25 | + 'STANDBY_FLAVOR', |
26 | ] |
27 | |
28 | |
29 | @@ -46,8 +46,8 @@ MAIN_STORE = 'main' # The main database. |
30 | ALL_STORES = frozenset([MAIN_STORE]) |
31 | |
32 | DEFAULT_FLAVOR = 'default' # Default flavor for current state. |
33 | -MASTER_FLAVOR = 'master' # The master database. |
34 | -SLAVE_FLAVOR = 'slave' # A slave database. |
35 | +PRIMARY_FLAVOR = 'primary' # The primary database. |
36 | +STANDBY_FLAVOR = 'standby' # A standby database. |
37 | |
38 | |
39 | class IDatabasePolicy(Interface): |
40 | @@ -75,7 +75,7 @@ class IDatabasePolicy(Interface): |
41 | |
42 | :param name: one of ALL_STORES. |
43 | |
44 | - :param flavor: MASTER_FLAVOR, SLAVE_FLAVOR, or DEFAULT_FLAVOR. |
45 | + :param flavor: PRIMARY_FLAVOR, STANDBY_FLAVOR, or DEFAULT_FLAVOR. |
46 | """ |
47 | |
48 | def install(): |
49 | @@ -94,15 +94,15 @@ class DisallowedStore(Exception): |
50 | class IStoreSelector(Interface): |
51 | """Get a Storm store with a desired flavor. |
52 | |
53 | - Stores come in two flavors - MASTER_FLAVOR and SLAVE_FLAVOR. |
54 | + Stores come in two flavors - PRIMARY_FLAVOR and STANDBY_FLAVOR. |
55 | |
56 | - The master is writable and up to date, but we should not use it |
57 | - whenever possible because there is only one master and we don't want |
58 | + The primary is writable and up to date, but we should not use it |
59 | + whenever possible because there is only one primary and we don't want |
60 | it to be overloaded. |
61 | |
62 | - The slave is read only replica of the master and may lag behind the |
63 | - master. For many purposes such as serving unauthenticated web requests |
64 | - and generating reports this is fine. We can also have as many slave |
65 | + The standby is a read-only replica of the primary and may lag behind the |
66 | + primary. For many purposes such as serving unauthenticated web requests |
67 | + and generating reports this is fine. We can also have as many standby |
68 | databases as we are prepared to pay for, so they will perform better |
69 | because they are less loaded. |
70 | """ |
71 | @@ -126,16 +126,16 @@ class IStoreSelector(Interface): |
72 | returned for a given name or flavor can depend on thread state |
73 | (eg. the HTTP request currently being handled). |
74 | |
75 | - If a SLAVE_FLAVOR is requested, the MASTER_FLAVOR may be returned |
76 | + If a STANDBY_FLAVOR is requested, the PRIMARY_FLAVOR may be returned |
77 | anyway. |
78 | |
79 | - The DEFAULT_FLAVOR flavor may return either a master or slave |
80 | + The DEFAULT_FLAVOR flavor may return either a primary or standby |
81 | depending on process state. Application code using the |
82 | - DEFAULT_FLAVOR flavor should assume they have a MASTER and that |
83 | + DEFAULT_FLAVOR flavor should assume they have a PRIMARY and that |
84 | a higher level will catch the exception raised if an attempt is |
85 | - made to write changes to a read only store. DEFAULT_FLAVOR exists |
86 | + made to write changes to a read-only store. DEFAULT_FLAVOR exists |
87 | for backwards compatibility, and new code should explicitly state |
88 | - if they want a master or a slave. |
89 | + if they want a primary or a standby. |
90 | |
91 | :raises DisconnectionError: |
92 | |
93 | diff --git a/lib/lp/services/database/policy.py b/lib/lp/services/database/policy.py |
94 | index a347060..838df09 100644 |
95 | --- a/lib/lp/services/database/policy.py |
96 | +++ b/lib/lp/services/database/policy.py |
97 | @@ -47,8 +47,8 @@ from lp.services.database.interfaces import ( |
98 | ISlaveStore, |
99 | IStoreSelector, |
100 | MAIN_STORE, |
101 | - MASTER_FLAVOR, |
102 | - SLAVE_FLAVOR, |
103 | + PRIMARY_FLAVOR, |
104 | + STANDBY_FLAVOR, |
105 | ) |
106 | from lp.services.database.sqlbase import StupidCache |
107 | |
108 | @@ -107,7 +107,7 @@ class BaseDatabasePolicy: |
109 | """Base class for database policies.""" |
110 | |
111 | # The default flavor to use. |
112 | - default_flavor = MASTER_FLAVOR |
113 | + default_flavor = PRIMARY_FLAVOR |
114 | |
115 | def __init__(self, request=None): |
116 | pass |
117 | @@ -121,21 +121,21 @@ class BaseDatabasePolicy: |
118 | store = get_connected_store(name, flavor) |
119 | except DisconnectionError: |
120 | |
121 | - # A request for a master database connection was made |
122 | + # A request for a primary database connection was made |
123 | # and failed. Nothing we can do so reraise the exception. |
124 | - if flavor != SLAVE_FLAVOR: |
125 | + if flavor != STANDBY_FLAVOR: |
126 | raise |
127 | |
128 | - # A request for a slave database connection was made |
129 | - # and failed. Try to return a master connection, this |
130 | + # A request for a standby database connection was made |
131 | + # and failed. Try to return a primary connection, this |
132 | # will be good enough. Note we don't call self.getStore() |
133 | # recursively because we want to make this attempt even if |
134 | - # the DatabasePolicy normally disallows master database |
135 | + # the DatabasePolicy normally disallows primary database |
136 | # connections. All this behaviour allows read-only requests |
137 | - # to keep working when slave databases are being rebuilt or |
138 | + # to keep working when standby databases are being rebuilt or |
139 | # updated. |
140 | try: |
141 | - flavor = MASTER_FLAVOR |
142 | + flavor = PRIMARY_FLAVOR |
143 | store = get_connected_store(name, flavor) |
144 | except DisconnectionError: |
145 | store = None |
146 | @@ -155,7 +155,7 @@ class BaseDatabasePolicy: |
147 | store._cache = storm_cache_factory() |
148 | |
149 | # Attach our marker interfaces so our adapters don't lie. |
150 | - if flavor == MASTER_FLAVOR: |
151 | + if flavor == PRIMARY_FLAVOR: |
152 | alsoProvides(store, IMasterStore) |
153 | else: |
154 | alsoProvides(store, ISlaveStore) |
155 | @@ -193,7 +193,7 @@ class DatabaseBlockedPolicy(BaseDatabasePolicy): |
156 | |
157 | |
158 | class PrimaryDatabasePolicy(BaseDatabasePolicy): |
159 | - """`IDatabasePolicy` that selects the MASTER_FLAVOR by default. |
160 | + """`IDatabasePolicy` that selects the PRIMARY_FLAVOR by default. |
161 | |
162 | Standby databases can still be accessed if requested explicitly. |
163 | |
164 | @@ -201,29 +201,29 @@ class PrimaryDatabasePolicy(BaseDatabasePolicy): |
165 | support session cookies. It is also used when no policy has been |
166 | installed. |
167 | """ |
168 | - default_flavor = MASTER_FLAVOR |
169 | + default_flavor = PRIMARY_FLAVOR |
170 | |
171 | |
172 | class StandbyDatabasePolicy(BaseDatabasePolicy): |
173 | - """`IDatabasePolicy` that selects the SLAVE_FLAVOR by default. |
174 | + """`IDatabasePolicy` that selects the STANDBY_FLAVOR by default. |
175 | |
176 | Access to the primary can still be made if requested explicitly. |
177 | """ |
178 | - default_flavor = SLAVE_FLAVOR |
179 | + default_flavor = STANDBY_FLAVOR |
180 | |
181 | |
182 | class StandbyOnlyDatabasePolicy(BaseDatabasePolicy): |
183 | - """`IDatabasePolicy` that only allows access to SLAVE_FLAVOR stores. |
184 | + """`IDatabasePolicy` that only allows access to STANDBY_FLAVOR stores. |
185 | |
186 | This policy is used for Feeds requests and other always-read only request. |
187 | """ |
188 | - default_flavor = SLAVE_FLAVOR |
189 | + default_flavor = STANDBY_FLAVOR |
190 | |
191 | def getStore(self, name, flavor): |
192 | """See `IDatabasePolicy`.""" |
193 | - if flavor == MASTER_FLAVOR: |
194 | + if flavor == PRIMARY_FLAVOR: |
195 | raise DisallowedStore(flavor) |
196 | - return super().getStore(name, SLAVE_FLAVOR) |
197 | + return super().getStore(name, STANDBY_FLAVOR) |
198 | |
199 | |
200 | def LaunchpadDatabasePolicyFactory(request): |
201 | @@ -263,25 +263,25 @@ class LaunchpadDatabasePolicy(BaseDatabasePolicy): |
202 | """See `IDatabasePolicy`.""" |
203 | default_flavor = None |
204 | |
205 | - # If this is a Retry attempt, force use of the master database. |
206 | + # If this is a Retry attempt, force use of the primary database. |
207 | if getattr(self.request, '_retry_count', 0) > 0: |
208 | - default_flavor = MASTER_FLAVOR |
209 | + default_flavor = PRIMARY_FLAVOR |
210 | |
211 | - # Select if the DEFAULT_FLAVOR Store will be the master or a |
212 | - # slave. We select slave if this is a readonly request, and |
213 | + # Select if the DEFAULT_FLAVOR Store will be the primary or a |
214 | + # standby. We select standby if this is a readonly request, and |
215 | # only readonly requests have been made by this user recently. |
216 | # This ensures that a user will see any changes they just made |
217 | - # on the master, despite the fact it might take a while for |
218 | - # those changes to propagate to the slave databases. |
219 | + # on the primary, despite the fact it might take a while for |
220 | + # those changes to propagate to the standby databases. |
221 | elif self.read_only: |
222 | lag = self.getReplicationLag() |
223 | if (lag is not None |
224 | and lag > timedelta(seconds=config.database.max_usable_lag)): |
225 | - # Don't use the slave at all if lag is greater than the |
226 | + # Don't use the standby at all if lag is greater than the |
227 | # configured threshold. This reduces replication oddities |
228 | # noticed by users, as well as reducing load on the |
229 | - # slave allowing it to catch up quicker. |
230 | - default_flavor = MASTER_FLAVOR |
231 | + # standby allowing it to catch up quicker. |
232 | + default_flavor = PRIMARY_FLAVOR |
233 | else: |
234 | # We don't want to even make a DB query to read the session |
235 | # if we can tell that it is not around. This can be |
236 | @@ -300,11 +300,11 @@ class LaunchpadDatabasePolicy(BaseDatabasePolicy): |
237 | else: |
238 | recently = timedelta(minutes=2) + lag |
239 | if last_write is None or last_write < now - recently: |
240 | - default_flavor = SLAVE_FLAVOR |
241 | + default_flavor = STANDBY_FLAVOR |
242 | else: |
243 | - default_flavor = MASTER_FLAVOR |
244 | + default_flavor = PRIMARY_FLAVOR |
245 | else: |
246 | - default_flavor = MASTER_FLAVOR |
247 | + default_flavor = PRIMARY_FLAVOR |
248 | |
249 | assert default_flavor is not None, 'default_flavor not set!' |
250 | |
251 | @@ -315,7 +315,7 @@ class LaunchpadDatabasePolicy(BaseDatabasePolicy): |
252 | |
253 | If the request just handled was not read_only, we need to store |
254 | this fact and the timestamp in the session. Subsequent requests |
255 | - can then keep using the master until they are sure any changes |
256 | + can then keep using the primary until they are sure any changes |
257 | made have been propagated. |
258 | """ |
259 | if not self.read_only: |
260 | @@ -352,15 +352,15 @@ class LaunchpadDatabasePolicy(BaseDatabasePolicy): |
261 | return _test_lag |
262 | |
263 | # Attempt to retrieve PostgreSQL streaming replication lag |
264 | - # from the slave. |
265 | - slave_store = self.getStore(MAIN_STORE, SLAVE_FLAVOR) |
266 | - hot_standby, streaming_lag = slave_store.execute(""" |
267 | + # from the standby. |
268 | + standby_store = self.getStore(MAIN_STORE, STANDBY_FLAVOR) |
269 | + hot_standby, streaming_lag = standby_store.execute(""" |
270 | SELECT |
271 | pg_is_in_recovery(), |
272 | now() - pg_last_xact_replay_timestamp() |
273 | """).get_one() |
274 | if hot_standby and streaming_lag is not None: |
275 | - # Slave is a PG 9.1 streaming replication hot standby. |
276 | + # standby is a PG 9.1 streaming replication hot standby. |
277 | # Return the lag. |
278 | return streaming_lag |
279 | |
280 | diff --git a/lib/lp/services/oauth/tests/test_oauth.py b/lib/lp/services/oauth/tests/test_oauth.py |
281 | index 1d95ccc..20c192c 100644 |
282 | --- a/lib/lp/services/oauth/tests/test_oauth.py |
283 | +++ b/lib/lp/services/oauth/tests/test_oauth.py |
284 | @@ -12,7 +12,7 @@ from zope.component import getUtility |
285 | |
286 | from lp.services.database.interfaces import ( |
287 | MAIN_STORE, |
288 | - MASTER_FLAVOR, |
289 | + PRIMARY_FLAVOR, |
290 | ) |
291 | from lp.services.oauth.model import ( |
292 | OAuthAccessToken, |
293 | @@ -26,14 +26,15 @@ class BaseOAuthTestCase(unittest.TestCase): |
294 | """Base tests for the OAuth database classes.""" |
295 | layer = DatabaseFunctionalLayer |
296 | |
297 | - def test__getStore_should_return_the_main_master_store(self): |
298 | - """We want all OAuth classes to use the master store. |
299 | + def test__getStore_should_return_the_main_primary_store(self): |
300 | + """We want all OAuth classes to use the primary store. |
301 | Otherwise, the OAuth exchanges will fail because the authorize |
302 | - screen won't probably find the new request token on the slave store. |
303 | + screen won't probably find the new request token on the standby |
304 | + store. |
305 | """ |
306 | zstorm = getUtility(IZStorm) |
307 | self.assertEqual( |
308 | - '%s-%s' % (MAIN_STORE, MASTER_FLAVOR), |
309 | + '%s-%s' % (MAIN_STORE, PRIMARY_FLAVOR), |
310 | zstorm.get_name(self.class_._getStore())) |
311 | |
312 | |
313 | diff --git a/lib/lp/services/verification/model/logintoken.py b/lib/lp/services/verification/model/logintoken.py |
314 | index ca24bf8..9243471 100644 |
315 | --- a/lib/lp/services/verification/model/logintoken.py |
316 | +++ b/lib/lp/services/verification/model/logintoken.py |
317 | @@ -279,7 +279,7 @@ class LoginTokenSet: |
318 | "consumed should be one of {True, False, None}. Got '%s'." |
319 | % consumed) |
320 | |
321 | - # It's important to always use the MASTER_FLAVOR store here |
322 | + # It's important to always use the PRIMARY_FLAVOR store here |
323 | # because we don't want replication lag to cause a 404 error. |
324 | return IMasterStore(LoginToken).find(LoginToken, conditions) |
325 | |
326 | @@ -306,7 +306,7 @@ class LoginTokenSet: |
327 | "consumed should be one of {True, False, None}. Got '%s'." |
328 | % consumed) |
329 | |
330 | - # It's important to always use the MASTER_FLAVOR store here |
331 | + # It's important to always use the PRIMARY_FLAVOR store here |
332 | # because we don't want replication lag to cause a 404 error. |
333 | return IMasterStore(LoginToken).find(LoginToken, conditions) |
334 | |
335 | diff --git a/lib/lp/services/webapp/adapter.py b/lib/lp/services/webapp/adapter.py |
336 | index 816f206..653983d 100644 |
337 | --- a/lib/lp/services/webapp/adapter.py |
338 | +++ b/lib/lp/services/webapp/adapter.py |
339 | @@ -57,8 +57,8 @@ from lp.services.database.interfaces import ( |
340 | IRequestExpired, |
341 | IStoreSelector, |
342 | MAIN_STORE, |
343 | - MASTER_FLAVOR, |
344 | - SLAVE_FLAVOR, |
345 | + PRIMARY_FLAVOR, |
346 | + STANDBY_FLAVOR, |
347 | ) |
348 | from lp.services.database.policy import PrimaryDatabasePolicy |
349 | from lp.services.database.postgresql import ConnectionString |
350 | @@ -467,7 +467,7 @@ class LaunchpadDatabase(Postgres): |
351 | % repr(self._uri.database)) |
352 | |
353 | assert realm == 'main', 'Unknown realm %s' % realm |
354 | - assert flavor in ('master', 'slave'), 'Unknown flavor %s' % flavor |
355 | + assert flavor in ('primary', 'standby'), 'Unknown flavor %s' % flavor |
356 | |
357 | # We set self._dsn here rather than in __init__ so when the Store |
358 | # is reconnected it pays attention to any config changes. |
359 | @@ -496,13 +496,13 @@ class LaunchpadDatabase(Postgres): |
360 | # An alternative would be to use the _ro users generated by |
361 | # security.py, but this would needlessly double the number |
362 | # of database users we need to maintain ACLs for on production. |
363 | - if flavor == SLAVE_FLAVOR: |
364 | + if flavor == STANDBY_FLAVOR: |
365 | raw_connection.cursor().execute( |
366 | 'SET DEFAULT_TRANSACTION_READ_ONLY TO TRUE') |
367 | # Make the altered session setting stick. |
368 | raw_connection.commit() |
369 | - else: |
370 | - assert config_entry.endswith('_master'), ( |
371 | + elif not config_entry.endswith(('_master', '_primary')): |
372 | + raise AssertionError( |
373 | 'DB connection URL %s does not meet naming convention.') |
374 | |
375 | _reset_dirty_commit_flags(*flags) |
376 | @@ -769,12 +769,12 @@ def get_store(storm_class, flavor=DEFAULT_FLAVOR): |
377 | |
378 | def get_master_store(storm_class): |
379 | """Return the master Store for the given database class.""" |
380 | - return get_store(storm_class, MASTER_FLAVOR) |
381 | + return get_store(storm_class, PRIMARY_FLAVOR) |
382 | |
383 | |
384 | def get_slave_store(storm_class): |
385 | """Return the master Store for the given database class.""" |
386 | - return get_store(storm_class, SLAVE_FLAVOR) |
387 | + return get_store(storm_class, STANDBY_FLAVOR) |
388 | |
389 | |
390 | def get_object_from_master_store(obj): |
391 | diff --git a/lib/lp/services/webapp/doc/test_adapter.txt b/lib/lp/services/webapp/doc/test_adapter.txt |
392 | index 0b6712c..abe5036 100644 |
393 | --- a/lib/lp/services/webapp/doc/test_adapter.txt |
394 | +++ b/lib/lp/services/webapp/doc/test_adapter.txt |
395 | @@ -12,7 +12,7 @@ Imports and test setup: |
396 | >>> from lazr.restful.utils import get_current_browser_request |
397 | >>> from storm.zope.interfaces import IZStorm |
398 | >>> from lp.services.database.interfaces import ( |
399 | - ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
400 | + ... IStoreSelector, MAIN_STORE, PRIMARY_FLAVOR) |
401 | >>> from lp.services.config import config |
402 | >>> import lp.services.webapp.adapter |
403 | >>> from lp.services.webapp.adapter import ( |
404 | @@ -24,7 +24,7 @@ Imports and test setup: |
405 | There are several possible database connections available via the |
406 | IStoreSelector utility. |
407 | |
408 | - >>> store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
409 | + >>> store = getUtility(IStoreSelector).get(MAIN_STORE, PRIMARY_FLAVOR) |
410 | >>> dbname = DatabaseLayer._db_fixture.dbname |
411 | >>> active_name = store.execute("SELECT current_database()").get_one()[0] |
412 | >>> if active_name != dbname: print('%s != %s' % (active_name, dbname)) |
413 | @@ -190,7 +190,7 @@ the Postgres statement timeout (a value of zero means no timeout): |
414 | ... zstorm.remove(store) |
415 | ... transaction.abort() |
416 | ... store.close() |
417 | - ... store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
418 | + ... store = getUtility(IStoreSelector).get(MAIN_STORE, PRIMARY_FLAVOR) |
419 | |
420 | >>> set_request_started() |
421 | >>> print(current_statement_timeout(store)) |
422 | @@ -357,7 +357,7 @@ The request time limit was exceeded before the statement was issued to |
423 | the database. |
424 | |
425 | >>> print(pretty(get_request_statements())) |
426 | - [(0, ..., 'SQL-main-master', 'SELECT 2', ...)] |
427 | + [(0, ..., 'SQL-main-primary', 'SELECT 2', ...)] |
428 | |
429 | |
430 | When a RequestExpired exception is raised, the current |
431 | diff --git a/lib/lp/services/webapp/doc/test_adapter_permissions.txt b/lib/lp/services/webapp/doc/test_adapter_permissions.txt |
432 | index 8cf15f6..2ae057f 100644 |
433 | --- a/lib/lp/services/webapp/doc/test_adapter_permissions.txt |
434 | +++ b/lib/lp/services/webapp/doc/test_adapter_permissions.txt |
435 | @@ -1,24 +1,25 @@ |
436 | -Our database adapters need to trap writes to tables in slave replication |
437 | -sets. These tables may be reached directly using a SLAVE_FLAVOR store, or |
438 | -traversed to from a MASTER_FLAVOR store. |
439 | +Our database adapters need to trap writes to tables in standby replication |
440 | +sets. These tables may be reached directly using a STANDBY_FLAVOR store, or |
441 | +traversed to from a PRIMARY_FLAVOR store. |
442 | |
443 | Because our development environment is not replicated, we use database |
444 | -permissions to ensure that tables we should not be writing too cannot |
445 | +permissions to ensure that tables we should not be writing to cannot |
446 | be written to. The same permissions structure is also used on production, |
447 | -so the Slony-I triggers blocking writes to slaved tables will never |
448 | +so the Slony-I triggers blocking writes to some tables will never |
449 | actually be invoked. |
450 | |
451 | >>> from lp.registry.model.person import Person |
452 | >>> from lp.services.database.interfaces import ( |
453 | - ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR) |
454 | + ... IStoreSelector, MAIN_STORE, PRIMARY_FLAVOR, STANDBY_FLAVOR) |
455 | >>> import transaction |
456 | >>> from zope.component import getUtility |
457 | |
458 | -If a SLAVE_FLAVOR store is requested, it should trap all writes. |
459 | +If a STANDBY_FLAVOR store is requested, it should trap all writes. |
460 | |
461 | >>> t = transaction.begin() |
462 | - >>> main_slave = getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR) |
463 | - >>> janitor = main_slave.find(Person, name='janitor').one() |
464 | + >>> main_standby = getUtility(IStoreSelector).get( |
465 | + ... MAIN_STORE, STANDBY_FLAVOR) |
466 | + >>> janitor = main_standby.find(Person, name='janitor').one() |
467 | >>> janitor.display_name = 'Ben Dover' |
468 | >>> transaction.commit() |
469 | Traceback (most recent call last): |
470 | @@ -29,21 +30,21 @@ Test this once more to ensure the settings stick across transactions. |
471 | |
472 | >>> transaction.abort() |
473 | >>> t = transaction.begin() |
474 | - >>> main_slave.find(Person, name='janitor').one().display_name = 'BenD' |
475 | + >>> main_standby.find(Person, name='janitor').one().display_name = 'BenD' |
476 | >>> transaction.commit() |
477 | Traceback (most recent call last): |
478 | ... |
479 | storm.database.InternalError: ... |
480 | |
481 | -If a MASTER_FLAVOR is requested, it should allow writes to table in that |
482 | +If a PRIMARY_FLAVOR is requested, it should allow writes to table in that |
483 | Store's replication set. |
484 | |
485 | >>> t = transaction.begin() |
486 | - >>> main_master = getUtility(IStoreSelector).get( |
487 | - ... MAIN_STORE, MASTER_FLAVOR) |
488 | - >>> main_master.find(Person, name='janitor').one().display_name = 'BenD' |
489 | + >>> main_primary = getUtility(IStoreSelector).get( |
490 | + ... MAIN_STORE, PRIMARY_FLAVOR) |
491 | + >>> main_primary.find(Person, name='janitor').one().display_name = 'BenD' |
492 | >>> transaction.commit() |
493 | >>> t = transaction.begin() |
494 | - >>> print(main_master.find(Person, name='janitor').one().display_name) |
495 | + >>> print(main_primary.find(Person, name='janitor').one().display_name) |
496 | BenD |
497 | >>> transaction.abort() |
498 | diff --git a/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled b/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled |
499 | index dbf7a36..9585e0a 100644 |
500 | --- a/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled |
501 | +++ b/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled |
502 | @@ -29,7 +29,7 @@ exception, and a time machine. |
503 | >>> from lp.services.config import config |
504 | >>> import lp.services.webapp.adapter |
505 | >>> from lp.services.webapp.interfaces import ( |
506 | - ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
507 | + ... IStoreSelector, MAIN_STORE, PRIMARY_FLAVOR) |
508 | >>> from lp.testing.pages import setupBrowser |
509 | |
510 | >>> config.push('set_timeout', dedent(''' |
511 | @@ -53,7 +53,7 @@ exception, and a time machine. |
512 | ... return self, None |
513 | ... def __call__(self): |
514 | ... store = zope.component.getUtility(IStoreSelector).get( |
515 | - ... MAIN_STORE, MASTER_FLAVOR) |
516 | + ... MAIN_STORE, PRIMARY_FLAVOR) |
517 | ... time_travel(config.database.db_statement_timeout + |
518 | ... config.database.db_statement_timeout_precision) |
519 | ... store.execute('SELECT TRUE', noresult=True) |
520 | @@ -63,7 +63,7 @@ exception, and a time machine. |
521 | ... def __call__(self): |
522 | ... global timeout_in_exception_view |
523 | ... store = zope.component.getUtility(IStoreSelector).get( |
524 | - ... MAIN_STORE, MASTER_FLAVOR) |
525 | + ... MAIN_STORE, PRIMARY_FLAVOR) |
526 | ... try: |
527 | ... store.execute('SELECT TRUE', noresult=True) |
528 | ... except TimeoutError: |
529 | diff --git a/lib/lp/services/webapp/publication.py b/lib/lp/services/webapp/publication.py |
530 | index 717cc65..27c29d4 100644 |
531 | --- a/lib/lp/services/webapp/publication.py |
532 | +++ b/lib/lp/services/webapp/publication.py |
533 | @@ -75,7 +75,7 @@ from lp.services.config import config |
534 | from lp.services.database.interfaces import ( |
535 | IDatabasePolicy, |
536 | IStoreSelector, |
537 | - MASTER_FLAVOR, |
538 | + PRIMARY_FLAVOR, |
539 | ) |
540 | from lp.services.database.policy import LaunchpadDatabasePolicy |
541 | from lp.services.features.flags import NullFeatureController |
542 | @@ -707,10 +707,10 @@ class LaunchpadBrowserPublication( |
543 | # used is a replica, raise a Retry exception instead of |
544 | # returning the 404 error page. We do this in case the |
545 | # LookupError is caused by replication lag. Our database |
546 | - # policy forces the use of the master database for retries. |
547 | + # policy forces the use of the primary database for retries. |
548 | if (isinstance(exc_info[1], LookupError) |
549 | and isinstance(db_policy, LaunchpadDatabasePolicy)): |
550 | - if db_policy.default_flavor == MASTER_FLAVOR: |
551 | + if db_policy.default_flavor == PRIMARY_FLAVOR: |
552 | return False |
553 | else: |
554 | return True |
555 | @@ -861,9 +861,9 @@ class LaunchpadBrowserPublication( |
556 | # Reset all Storm stores when not running the test suite. |
557 | # We could reset them when running the test suite but |
558 | # that'd make writing tests a much more painful task. We |
559 | - # still reset the slave stores though to minimize stale |
560 | + # still reset the standby stores though to minimize stale |
561 | # cache issues. |
562 | - if thread_name != 'MainThread' or name.endswith('-slave'): |
563 | + if thread_name != 'MainThread' or name.endswith('-standby'): |
564 | store.reset() |
565 | |
566 | |
567 | diff --git a/lib/lp/services/webapp/tests/test_dbpolicy.py b/lib/lp/services/webapp/tests/test_dbpolicy.py |
568 | index a8eac12..b47c3ba 100644 |
569 | --- a/lib/lp/services/webapp/tests/test_dbpolicy.py |
570 | +++ b/lib/lp/services/webapp/tests/test_dbpolicy.py |
571 | @@ -39,8 +39,8 @@ from lp.services.database.interfaces import ( |
572 | ISlaveStore, |
573 | IStoreSelector, |
574 | MAIN_STORE, |
575 | - MASTER_FLAVOR, |
576 | - SLAVE_FLAVOR, |
577 | + PRIMARY_FLAVOR, |
578 | + STANDBY_FLAVOR, |
579 | ) |
580 | from lp.services.database.policy import ( |
581 | BaseDatabasePolicy, |
582 | @@ -112,10 +112,10 @@ class StandbyDatabasePolicyTestCase(BaseDatabasePolicyTestCase): |
583 | getUtility(IStoreSelector).get(store, DEFAULT_FLAVOR), |
584 | ISlaveStore) |
585 | |
586 | - def test_master_allowed(self): |
587 | + def test_primary_allowed(self): |
588 | for store in ALL_STORES: |
589 | self.assertProvides( |
590 | - getUtility(IStoreSelector).get(store, MASTER_FLAVOR), |
591 | + getUtility(IStoreSelector).get(store, PRIMARY_FLAVOR), |
592 | IMasterStore) |
593 | |
594 | |
595 | @@ -126,11 +126,11 @@ class StandbyOnlyDatabasePolicyTestCase(StandbyDatabasePolicyTestCase): |
596 | self.policy = StandbyOnlyDatabasePolicy() |
597 | super().setUp() |
598 | |
599 | - def test_master_allowed(self): |
600 | + def test_primary_allowed(self): |
601 | for store in ALL_STORES: |
602 | self.assertRaises( |
603 | DisallowedStore, |
604 | - getUtility(IStoreSelector).get, store, MASTER_FLAVOR) |
605 | + getUtility(IStoreSelector).get, store, PRIMARY_FLAVOR) |
606 | |
607 | |
608 | class PrimaryDatabasePolicyTestCase(BaseDatabasePolicyTestCase): |
609 | @@ -156,7 +156,7 @@ class PrimaryDatabasePolicyTestCase(BaseDatabasePolicyTestCase): |
610 | # We get the primary store even if the standby was requested. |
611 | for store in ALL_STORES: |
612 | self.assertProvides( |
613 | - getUtility(IStoreSelector).get(store, SLAVE_FLAVOR), |
614 | + getUtility(IStoreSelector).get(store, STANDBY_FLAVOR), |
615 | ISlaveStore) |
616 | |
617 | |
618 | @@ -235,11 +235,11 @@ class LayerDatabasePolicyTestCase(TestCase): |
619 | self.assertIsInstance(policy, LaunchpadDatabasePolicy) |
620 | |
621 | |
622 | -class MasterFallbackTestCase(TestCase): |
623 | +class PrimaryFallbackTestCase(TestCase): |
624 | layer = DatabaseFunctionalLayer |
625 | |
626 | def setUp(self): |
627 | - super(MasterFallbackTestCase, self).setUp() |
628 | + super().setUp() |
629 | |
630 | self.pgbouncer_fixture = PGBouncerFixture() |
631 | |
632 | @@ -264,7 +264,7 @@ class MasterFallbackTestCase(TestCase): |
633 | |
634 | self.useFixture(self.pgbouncer_fixture) |
635 | |
636 | - def test_can_shutdown_slave_only(self): |
637 | + def test_can_shutdown_standby_only(self): |
638 | '''Confirm that this TestCase's test infrastructure works as needed. |
639 | ''' |
640 | master_store = IMasterStore(Person) |
641 | @@ -280,8 +280,8 @@ class MasterFallbackTestCase(TestCase): |
642 | master_store.get(Person, 2) |
643 | self.assertRaises(DisconnectionError, slave_store.get, Person, 2) |
644 | |
645 | - def test_startup_with_no_slave(self): |
646 | - '''An attempt is made for the first time to connect to a slave.''' |
647 | + def test_startup_with_no_standby(self): |
648 | + '''An attempt is made for the first time to connect to a standby.''' |
649 | self.pgbouncer_fixture.stop() |
650 | |
651 | master_store = IMasterStore(Person) |
652 | @@ -290,8 +290,8 @@ class MasterFallbackTestCase(TestCase): |
653 | # The master and slave Stores are the same object. |
654 | self.assertIs(master_store, slave_store) |
655 | |
656 | - def test_slave_shutdown_during_transaction(self): |
657 | - '''Slave is shutdown while running, but we can recover.''' |
658 | + def test_standby_shutdown_during_transaction(self): |
659 | + '''Standby is shutdown while running, but we can recover.''' |
660 | master_store = IMasterStore(Person) |
661 | slave_store = ISlaveStore(Person) |
662 | |
663 | @@ -313,8 +313,8 @@ class MasterFallbackTestCase(TestCase): |
664 | |
665 | self.assertIs(master_store, slave_store) |
666 | |
667 | - def test_slave_shutdown_between_transactions(self): |
668 | - '''Slave is shutdown in between transactions.''' |
669 | + def test_standby_shutdown_between_transactions(self): |
670 | + '''Standby is shutdown in between transactions.''' |
671 | master_store = IMasterStore(Person) |
672 | slave_store = ISlaveStore(Person) |
673 | self.assertIsNot(master_store, slave_store) |
674 | @@ -322,8 +322,8 @@ class MasterFallbackTestCase(TestCase): |
675 | transaction.abort() |
676 | self.pgbouncer_fixture.stop() |
677 | |
678 | - # The process doesn't notice the slave going down, and things |
679 | - # will fail the next time the slave is used. |
680 | + # The process doesn't notice the standby going down, and things |
681 | + # will fail the next time the standby is used. |
682 | master_store = IMasterStore(Person) |
683 | slave_store = ISlaveStore(Person) |
684 | self.assertIsNot(master_store, slave_store) |
685 | @@ -336,8 +336,8 @@ class MasterFallbackTestCase(TestCase): |
686 | slave_store = ISlaveStore(Person) |
687 | self.assertIs(master_store, slave_store) |
688 | |
689 | - def test_slave_reconnect_after_outage(self): |
690 | - '''The slave is again used once it becomes available.''' |
691 | + def test_standby_reconnect_after_outage(self): |
692 | + '''The standby is again used once it becomes available.''' |
693 | self.pgbouncer_fixture.stop() |
694 | |
695 | master_store = IMasterStore(Person) |
696 | @@ -391,54 +391,54 @@ class TestFastDowntimeRollout(TestCase): |
697 | except DisconnectionError: |
698 | return False |
699 | |
700 | - def store_is_slave(self, store): |
701 | - return store.get_database().name == 'main-slave' |
702 | + def store_is_standby(self, store): |
703 | + return store.get_database().name == 'main-standby' |
704 | |
705 | - def store_is_master(self, store): |
706 | - return not self.store_is_slave(store) |
707 | + def store_is_primary(self, store): |
708 | + return not self.store_is_standby(store) |
709 | |
710 | - def test_slave_only_fast_downtime_rollout(self): |
711 | - '''You can always access a working slave store during fast downtime. |
712 | + def test_standby_only_fast_downtime_rollout(self): |
713 | + '''You can always access a working standby store during fast downtime. |
714 | ''' |
715 | # Everything is running happily. |
716 | store = ISlaveStore(Person) |
717 | original_store = store |
718 | self.assertTrue(self.store_is_working(store)) |
719 | - self.assertTrue(self.store_is_slave(store)) |
720 | + self.assertTrue(self.store_is_standby(store)) |
721 | |
722 | # But fast downtime is about to happen. |
723 | |
724 | - # Replication is stopped on the slave, and lag starts |
725 | + # Replication is stopped on the standby, and lag starts |
726 | # increasing. |
727 | |
728 | - # All connections to the master are killed so database schema |
729 | + # All connections to the primary are killed so database schema |
730 | # updates can be applied. |
731 | self.pgbouncer_cur.execute('DISABLE %s' % self.primary_dbname) |
732 | self.pgbouncer_cur.execute('KILL %s' % self.primary_dbname) |
733 | |
734 | - # Of course, slave connections are unaffected. |
735 | + # Of course, standby connections are unaffected. |
736 | self.assertTrue(self.store_is_working(store)) |
737 | |
738 | - # After schema updates have been made to the master, it is |
739 | + # After schema updates have been made to the primary, it is |
740 | # reenabled. |
741 | self.pgbouncer_cur.execute('RESUME %s' % self.primary_dbname) |
742 | self.pgbouncer_cur.execute('ENABLE %s' % self.primary_dbname) |
743 | |
744 | - # And the slaves taken down, and replication reenabled so the |
745 | + # And the standbys taken down, and replication reenabled so the |
746 | # schema updates can replicate. |
747 | self.pgbouncer_cur.execute('DISABLE %s' % self.standby_dbname) |
748 | self.pgbouncer_cur.execute('KILL %s' % self.standby_dbname) |
749 | |
750 | - # The next attempt at accessing the slave store will fail |
751 | + # The next attempt at accessing the standby store will fail |
752 | # with a DisconnectionError. |
753 | self.assertRaises(DisconnectionError, store.execute, 'SELECT TRUE') |
754 | transaction.abort() |
755 | |
756 | # But if we handle that and retry, we can continue. |
757 | # Now the failed connection has been detected, the next Store |
758 | - # we are handed is a master Store instead of a slave. |
759 | + # we are handed is a primary Store instead of a standby. |
760 | store = ISlaveStore(Person) |
761 | - self.assertTrue(self.store_is_master(store)) |
762 | + self.assertTrue(self.store_is_primary(store)) |
763 | self.assertIsNot(ISlaveStore(Person), original_store) |
764 | |
765 | # But alas, it might not work the first transaction. If it has |
766 | @@ -447,10 +447,10 @@ class TestFastDowntimeRollout(TestCase): |
767 | self.assertFalse(self.store_is_working(store)) |
768 | transaction.abort() |
769 | |
770 | - # Next retry attempt, everything is fine using the master |
771 | - # connection, even though our code only asked for a slave. |
772 | + # Next retry attempt, everything is fine using the primary |
773 | + # connection, even though our code only asked for a standby. |
774 | store = ISlaveStore(Person) |
775 | - self.assertTrue(self.store_is_master(store)) |
776 | + self.assertTrue(self.store_is_primary(store)) |
777 | self.assertTrue(self.store_is_working(store)) |
778 | |
779 | # The original Store is busted though. You cannot reuse Stores |
780 | @@ -459,85 +459,85 @@ class TestFastDowntimeRollout(TestCase): |
781 | self.assertFalse(self.store_is_working(original_store)) |
782 | transaction.abort() |
783 | |
784 | - # Once replication has caught up, the slave is reenabled. |
785 | + # Once replication has caught up, the standby is reenabled. |
786 | self.pgbouncer_cur.execute('RESUME %s' % self.standby_dbname) |
787 | self.pgbouncer_cur.execute('ENABLE %s' % self.standby_dbname) |
788 | |
789 | # And next transaction, we are back to normal. |
790 | store = ISlaveStore(Person) |
791 | self.assertTrue(self.store_is_working(store)) |
792 | - self.assertTrue(self.store_is_slave(store)) |
793 | + self.assertTrue(self.store_is_standby(store)) |
794 | self.assertIs(original_store, store) |
795 | |
796 | - def test_master_slave_fast_downtime_rollout(self): |
797 | + def test_primary_standby_fast_downtime_rollout(self): |
798 | '''Parts of your app can keep working during a fast downtime update. |
799 | ''' |
800 | # Everything is running happily. |
801 | master_store = IMasterStore(Person) |
802 | - self.assertTrue(self.store_is_master(master_store)) |
803 | + self.assertTrue(self.store_is_primary(master_store)) |
804 | self.assertTrue(self.store_is_working(master_store)) |
805 | |
806 | slave_store = ISlaveStore(Person) |
807 | - self.assertTrue(self.store_is_slave(slave_store)) |
808 | + self.assertTrue(self.store_is_standby(slave_store)) |
809 | self.assertTrue(self.store_is_working(slave_store)) |
810 | |
811 | # But fast downtime is about to happen. |
812 | |
813 | - # Replication is stopped on the slave, and lag starts |
814 | + # Replication is stopped on the standby, and lag starts |
815 | # increasing. |
816 | |
817 | - # All connections to the master are killed so database schema |
818 | + # All connections to the primary are killed so database schema |
819 | # updates can be applied. |
820 | self.pgbouncer_cur.execute('DISABLE %s' % self.primary_dbname) |
821 | self.pgbouncer_cur.execute('KILL %s' % self.primary_dbname) |
822 | |
823 | - # Of course, slave connections are unaffected. |
824 | + # Of course, standby connections are unaffected. |
825 | self.assertTrue(self.store_is_working(slave_store)) |
826 | |
827 | - # But attempts to use a master store will fail. |
828 | + # But attempts to use a primary store will fail. |
829 | self.assertFalse(self.store_is_working(master_store)) |
830 | transaction.abort() |
831 | |
832 | - # After schema updates have been made to the master, it is |
833 | + # After schema updates have been made to the primary, it is |
834 | # reenabled. |
835 | self.pgbouncer_cur.execute('RESUME %s' % self.primary_dbname) |
836 | self.pgbouncer_cur.execute('ENABLE %s' % self.primary_dbname) |
837 | |
838 | - # And the slaves taken down, and replication reenabled so the |
839 | + # And the standbys taken down, and replication reenabled so the |
840 | # schema updates can replicate. |
841 | self.pgbouncer_cur.execute('DISABLE %s' % self.standby_dbname) |
842 | self.pgbouncer_cur.execute('KILL %s' % self.standby_dbname) |
843 | |
844 | - # The master store is working again. |
845 | + # The primary store is working again. |
846 | master_store = IMasterStore(Person) |
847 | - self.assertTrue(self.store_is_master(master_store)) |
848 | + self.assertTrue(self.store_is_primary(master_store)) |
849 | self.assertTrue(self.store_is_working(master_store)) |
850 | |
851 | - # The next attempt at accessing the slave store will fail |
852 | + # The next attempt at accessing the standby store will fail |
853 | # with a DisconnectionError. |
854 | slave_store = ISlaveStore(Person) |
855 | - self.assertTrue(self.store_is_slave(slave_store)) |
856 | + self.assertTrue(self.store_is_standby(slave_store)) |
857 | self.assertRaises( |
858 | DisconnectionError, slave_store.execute, 'SELECT TRUE') |
859 | transaction.abort() |
860 | |
861 | # But if we handle that and retry, we can continue. |
862 | # Now the failed connection has been detected, the next Store |
863 | - # we are handed is a master Store instead of a slave. |
864 | + # we are handed is a primary Store instead of a standby. |
865 | slave_store = ISlaveStore(Person) |
866 | - self.assertTrue(self.store_is_master(slave_store)) |
867 | + self.assertTrue(self.store_is_primary(slave_store)) |
868 | self.assertTrue(self.store_is_working(slave_store)) |
869 | |
870 | - # Once replication has caught up, the slave is reenabled. |
871 | + # Once replication has caught up, the standby is reenabled. |
872 | self.pgbouncer_cur.execute('RESUME %s' % self.standby_dbname) |
873 | self.pgbouncer_cur.execute('ENABLE %s' % self.standby_dbname) |
874 | |
875 | # And next transaction, we are back to normal. |
876 | transaction.abort() |
877 | master_store = IMasterStore(Person) |
878 | - self.assertTrue(self.store_is_master(master_store)) |
879 | + self.assertTrue(self.store_is_primary(master_store)) |
880 | self.assertTrue(self.store_is_working(master_store)) |
881 | |
882 | slave_store = ISlaveStore(Person) |
883 | - self.assertTrue(self.store_is_slave(slave_store)) |
884 | + self.assertTrue(self.store_is_standby(slave_store)) |
885 | self.assertTrue(self.store_is_working(slave_store)) |
LGTM, with one suggestion