Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20namespace Wikimedia\Rdbms;
21
22use Generator;
23use InvalidArgumentException;
24
25/**
26 * Manager of ILoadBalancer objects and, indirectly, IDatabase connections
27 *
28 * Each Load balancer instances corresponds to a specific database cluster.
29 * A "cluster" is the set of database servers that manage a given dataset.
30 *
31 * The "main" clusters are meant to colocate the most basic and highly relational application
32 * data for one or more "sister projects" managed by this site. This allows for highly flexible
33 * queries. Each project is identified by a database domain. Note that if there are several
34 * projects stored on a cluster, then the cluster dataset is a superset of the dataset for each
35 * of those projects.
36 *
37 * The "external" clusters are meant to provide places for bulk text storage, to colocate bulky
38 * relational data from specific modules, and to colocate data from cross-project modules such
39 * as authentication systems. An external cluster can have a database/schema for each project.
40 *
41 * @see ILoadBalancer
42 *
43 * @ingroup Database
44 * @since 1.28
45 */
46interface ILBFactory extends IConnectionProvider {
47    /** Idiom for "no special shutdown flags" */
48    public const SHUTDOWN_NORMAL = 0;
49    /** Do not save "session consistency" DB replication positions */
50    public const SHUTDOWN_NO_CHRONPROT = 1;
51
52    /** @var string Default main cluster name (do not change this) */
53    public const CLUSTER_MAIN_DEFAULT = 'DEFAULT';
54
55    /**
56     * Sub-classes may extend the required keys in $conf with additional parameters
57     *
58     * @param array $conf Array with keys:
59     *  - localDomain: A DatabaseDomain or database domain ID string
60     *  - virtualDomains: List of virtual database domain ID strings [optional].
61     *     These can be passed to {@see ILBFactory::getPrimaryDatabase()} and
62     *     {@see ILBFactory::getReplicaDatabase()}, with the actual cluster and database
63     *     domain being automatically resolved via "virtualDomainsMapping". Virtual database
64     *     domains not defined there will resolve to the local database domain.
65     *  - virtualDomainsMapping: Map of (virtual database domain ID => config map) [optional].
66     *     Each config map has a "db" key and an optional "cluster" key. The "db" key specifies
67     *     the actual database domain configured for use, with false indicating that the local
68     *     database domain is configured for use. The "cluster" key, if provided, specifies the
69     *     name of the external cluster configured for use, otherwise, the main cluster for the
70     *     actual database domain will be used.
71     *  - chronologyProtector: ChronologyProtector instance [optional]
72     *  - readOnlyReason: Reason the primary server is read-only (false if not)
73     *  - srvCache: BagOStuff instance for server cache [optional]
74     *  - cpStash: BagOStuff instance for ChronologyProtector store [optional].
75     *    See [ChronologyProtector requirements](@ref ChronologyProtector-storage-requirements).
76     *  - wanCache: WANObjectCache instance [optional]
77     *  - cliMode: Whether the execution context is a CLI script [optional]
78     *  - profiler: Callback that takes a profile section name and returns a ScopedCallback
79     *     that ends the profile section in its destructor [optional]
80     *  - trxProfiler: TransactionProfiler instance [optional]
81     *  - logger: PSR-3 logger instance [optional]
82     *  - errorLogger: Callback that takes an Exception and logs it [optional]
83     *  - deprecationLogger: Callback to log a deprecation warning [optional]
84     *  - secret: Secret string to use for HMAC hashing [optional]
85     *  - criticalSectionProvider: CriticalSectionProvider instance [optional]
86     */
87    public function __construct( array $conf );
88
89    /**
90     * Close all connections and make further attempts to open connections result in DBAccessError
91     *
92     * This only applies to the tracked load balancer instances.
93     *
94     * @see ILoadBalancer::disable()
95     */
96    public function destroy();
97
98    /**
99     * Reload the configuration if necessary.
100     * This may or may not have any effect.
101     */
102    public function autoReconfigure(): void;
103
104    /**
105     * Get the local (and default) database domain ID of connection handles
106     *
107     * @see DatabaseDomain
108     * @return string Database domain ID; this specifies DB name, schema, and table prefix
109     * @since 1.32
110     */
111    public function getLocalDomainID(): string;
112
113    /**
114     * Close all connections and redefine the local database domain
115     *
116     * This only applies to the tracked load balancer instances.
117     *
118     * This method is only intended for use with schema creation or integration testing
119     *
120     * @param DatabaseDomain|string $domain
121     * @since 1.33
122     */
123    public function redefineLocalDomain( $domain );
124
125    /**
126     * Get the tracked load balancer instance for a given domain.
127     *
128     * If no tracked instances exists, then one will be instantiated.
129     *
130     * This method accepts virtual domains
131     * ({@see \MediaWiki\MainConfigSchema::VirtualDomainsMapping}).
132     *
133     * @since 1.43
134     * @param string|false $domain Domain ID, or false for the current domain
135     * @return ILoadBalancer
136     */
137    public function getLoadBalancer( $domain = false ): ILoadBalancer;
138
139    /**
140     * Create a new load balancer instance for the main cluster that handles the given domain
141     *
142     * The resulting object is considered to be owned by the caller. Namely, it will be
143     * untracked, the caller is responsible for cleaning it up, and replication positions
144     * from it will not be saved by ChronologyProtector.
145     *
146     * This method is for only advanced usage and callers should almost always use
147     * getMainLB() instead. This method can be useful when a table is used as a key/value
148     * store. In that cases, one might want to query it in autocommit mode (DBO_TRX off)
149     * but still use DBO_TRX transaction rounds on other tables.
150     *
151     * @note The local/default database domain used by the load balancer instance will
152     * still inherit from this ILBFactory instance, regardless of the $domain parameter.
153     *
154     * @param string|false $domain Domain ID, or false for the current domain
155     * @return ILoadBalancerForOwner
156     */
157    public function newMainLB( $domain = false ): ILoadBalancerForOwner;
158
159    /**
160     * Get the tracked load balancer instance for the main cluster that handles the given domain
161     *
162     * If no tracked instances exists, then one will be instantiated
163     *
164     * @note The local/default database domain used by the load balancer instance will
165     * still inherit from this ILBFactory instance, regardless of the $domain parameter.
166     *
167     * @param string|false $domain Domain ID, or false for the current domain
168     * @return ILoadBalancer
169     */
170    public function getMainLB( $domain = false ): ILoadBalancer;
171
172    /**
173     * Create a new load balancer instance for an external cluster
174     *
175     * The resulting object will be untracked and the caller is responsible for cleaning it up.
176     * Database replication positions will not be saved by ChronologyProtector.
177     *
178     * This method is for only advanced usage and callers should almost always use
179     * getExternalLB() instead. This method can be useful when a table is used as a
180     * key/value store. In that cases, one might want to query it in autocommit mode
181     * (DBO_TRX off) but still use DBO_TRX transaction rounds on other tables.
182     *
183     * @param string $cluster External cluster name
184     * @throws InvalidArgumentException If $cluster is not recognized
185     * @return ILoadBalancerForOwner
186     */
187    public function newExternalLB( $cluster ): ILoadBalancerForOwner;
188
189    /**
190     * Get the tracked load balancer instance for an external cluster
191     *
192     * If no tracked instances exists, then one will be instantiated
193     *
194     * @param string $cluster External cluster name
195     * @throws InvalidArgumentException If $cluster is not recognized
196     * @return ILoadBalancer
197     */
198    public function getExternalLB( $cluster ): ILoadBalancer;
199
200    /**
201     * Get the tracked load balancer instances for all main clusters
202     *
203     * If no tracked instance exists for a cluster, then one will be instantiated
204     *
205     * Note that default main cluster name is ILoadBalancer::CLUSTER_MAIN_DEFAULT
206     *
207     * @return ILoadBalancer[] Map of (cluster name => ILoadBalancer)
208     * @since 1.29
209     */
210    public function getAllMainLBs(): array;
211
212    /**
213     * Get the tracked load balancer instances for all external clusters
214     *
215     * If no tracked instance exists for a cluster, then one will be instantiated
216     *
217     * @return ILoadBalancer[] Map of (cluster name => ILoadBalancer)
218     * @since 1.29
219     */
220    public function getAllExternalLBs(): array;
221
222    /**
223     * Get all tracked load balancer instances (generator)
224     *
225     * @return Generator|ILoadBalancer[]
226     * @since 1.39
227     */
228    public function getAllLBs();
229
230    /**
231     * Prepare all instantiated tracked load balancer instances for shutdown
232     *
233     * @param int $flags Bit field of ILBFactory::SHUTDOWN_* constants
234     * @param callable|null $workCallback Work to mask ChronologyProtector writes
235     * @param int|null &$cpIndex Position key write counter for ChronologyProtector [returned]
236     * @param string|null &$cpClientId Client ID hash for ChronologyProtector [returned]
237     */
238    public function shutdown(
239        $flags = self::SHUTDOWN_NORMAL,
240        ?callable $workCallback = null,
241        &$cpIndex = null,
242        &$cpClientId = null
243    );
244
245    /**
246     * Commit all replica database server transactions, clearing any point-in-time view snapshots
247     *
248     * This only applies to the instantiated tracked load balancer instances.
249     *
250     * This is useful for getting rid of stale data from an implicit transaction round
251     *
252     * @param string $fname Caller name @phan-mandatory-param
253     * @deprecated Since 1.43
254     */
255    public function flushReplicaSnapshots( $fname = __METHOD__ );
256
257    /**
258     * Wrap subsequent queries for all transaction round aware primary connections in a transaction
259     *
260     * Each of these transactions will be owned by this ILBFactory instance such that direct
261     * calls to {@link IDatabase::commit()} or {@link IDatabase::rollback()} will be disabled.
262     * These transactions get resolved by a single call to either {@link commitPrimaryChanges()}
263     * or {@link rollbackPrimaryChanges()}, after which, the transaction wrapping and ownership
264     * behavior revert back to the default. When there are multiple connections involved, these
265     * methods perform best-effort distributed transactions. When using distributed transactions,
266     * the RDBMS should be configured to used pessimistic concurrency control such that the commit
267     * step of each transaction is unlikely to fail.
268     *
269     * Transactions on replication connections are flushed so that future reads will not keep
270     * using the same point-in-time view snapshots (e.g. from MySQL REPEATABLE-READ). However,
271     * this does not wait for replication to catch up, so subsequent reads from replicas might
272     * not reflect recently committed changes.
273     *
274     * This only applies to the tracked load balancer instances.
275     *
276     * This allows for custom transaction rounds from any outer transaction scope.
277     *
278     * @param string $fname @phan-mandatory-param
279     * @throws DBTransactionError
280     * @since 1.37
281     */
282    public function beginPrimaryChanges( $fname = __METHOD__ );
283
284    /**
285     * Commit all primary connection transactions and flush all replica connection transactions
286     *
287     * Transactions on replication connections are flushed so that future reads will not keep
288     * using the same point-in-time view snapshots (e.g. from MySQL REPEATABLE-READ). However,
289     * this does not wait for replication to catch up, so subsequent reads from replicas might
290     * not reflect the committed changes.
291     *
292     * This only applies to the instantiated tracked load balancer instances.
293     *
294     * @param string $fname Caller name @phan-mandatory-param
295     * @param int $maxWriteDuration abort if more than this much time was spent in write queries
296     * @throws DBTransactionError
297     * @since 1.37
298     */
299    public function commitPrimaryChanges( $fname = __METHOD__, int $maxWriteDuration = 0 );
300
301    /**
302     * Rollback all primary connection transactions and flush all replica connection transactions
303     *
304     * This only applies to the instantiated tracked load balancer instances.
305     *
306     * @param string $fname Caller name @phan-mandatory-param
307     * @since 1.37
308     */
309    public function rollbackPrimaryChanges( $fname = __METHOD__ );
310
311    /**
312     * Release important session-level state (named lock, table locks) as post-rollback cleanup
313     *
314     * This only applies to the instantiated tracked load balancer instances.
315     *
316     * This should only be called by application entry point functions, since there must be
317     * no chance that a future caller will still be expecting some of the lost session state.
318     *
319     * @param string $fname Caller name @phan-mandatory-param
320     * @since 1.38
321     */
322    public function flushPrimarySessions( $fname = __METHOD__ );
323
324    /**
325     * Check if an explicit transaction round is active
326     *
327     * @return bool
328     * @since 1.29
329     */
330    public function hasTransactionRound();
331
332    /**
333     * Check if transaction rounds can be started, committed, or rolled back right now
334     *
335     * This can be used as a recursion guard to avoid exceptions in transaction callbacks.
336     *
337     * @return bool
338     * @since 1.32
339     */
340    public function isReadyForRoundOperations();
341
342    /**
343     * Determine if any primary connection has pending changes
344     *
345     * This only applies to the instantiated tracked load balancer instances.
346     *
347     * @return bool
348     * @since 1.37
349     */
350    public function hasPrimaryChanges();
351
352    /**
353     * Determine if any lagged replica database server connection was used.
354     *
355     * This only applies to the instantiated tracked load balancer instances.
356     *
357     * @return bool
358     */
359    public function laggedReplicaUsed();
360
361    /**
362     * Determine if any primary connection has pending/written changes from this request
363     *
364     * This only applies to the instantiated tracked load balancer instances.
365     *
366     * @param float|null $age How many seconds ago is "recent" [defaults to LB lag wait timeout]
367     * @return bool
368     */
369    public function hasOrMadeRecentPrimaryChanges( $age = null );
370
371    /**
372     * Waits for the replica database server to catch up to the current primary position
373     *
374     * Use this when updating very large numbers of rows, as in maintenance scripts, to
375     * avoid causing too much lag. This is a no-op if there are no replica database servers.
376     *
377     * By default this waits on all DB clusters actually used in this request.
378     * This makes sense when lag being waiting on is caused by the code that does this check.
379     * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
380     * that were not changed since the last wait check.
381     *
382     * Never call this function after a large DB write that is *still* in a transaction.
383     * It only makes sense to call this after the possible lag inducing changes were committed.
384     *
385     * This only applies to the instantiated tracked load balancer instances.
386     *
387     * @param array $opts Optional fields that include:
388     *   - timeout: Max wait time. Default: 60 seconds for CLI, 1 second for web.
389     *   - ifWritesSince: Only wait if writes were done since this UNIX timestamp.
390     * @return bool True on success, false if a timeout or error occurred while waiting
391     */
392    public function waitForReplication( array $opts = [] );
393
394    /**
395     * Add a callback to be run in every call to waitForReplication() prior to any waiting
396     *
397     * Callbacks must clear any transactions that they start.
398     *
399     * @param string $name Callback name
400     * @param callable|null $callback Use null to unset a callback
401     */
402    public function setWaitForReplicationListener( $name, ?callable $callback = null );
403
404    /**
405     * Disable the ChronologyProtector on all instantiated tracked load balancer instances
406     *
407     * This can be called at the start of special API entry points.
408     */
409    public function disableChronologyProtection();
410
411    /**
412     * Set a new table prefix for the existing local domain ID for testing
413     *
414     * @param string $prefix
415     * @since 1.33
416     */
417    public function setLocalDomainPrefix( $prefix );
418
419    /**
420     * Close all connections on instantiated tracked load balancer instances
421     *
422     * @param string $fname Caller name (e.g. __METHOD__) @phan-mandatory-param
423     */
424    public function closeAll( $fname = __METHOD__ );
425
426    /**
427     * @param string $agent Agent name for query profiling
428     */
429    public function setAgentName( $agent );
430
431    /**
432     * Whether it has streaming replica servers.
433     *
434     * @since 1.41
435     * @return bool
436     */
437    public function hasStreamingReplicaServers();
438
439    /**
440     * Set the default timeout for replication wait checks
441     *
442     * @param int $seconds Timeout, in seconds
443     * @return int The previous default timeout
444     * @since 1.35
445     */
446    public function setDefaultReplicationWaitTimeout( $seconds );
447
448    /**
449     * Make certain table names use their own database, schema, and table prefix
450     * when passed into SQL queries pre-escaped and without a qualified database name
451     *
452     * For example, "user" can be converted to "myschema.mydbname.user" for convenience.
453     * Appearances like `user`, somedb.user, somedb.someschema.user will used literally.
454     *
455     * Calling this twice will completely clear any old table aliases. Also, note that
456     * callers are responsible for making sure the schemas and databases actually exist.
457     *
458     * @param array[] $aliases Map of (table => (dbname, schema, prefix) map)
459     * @since 1.31
460     */
461    public function setTableAliases( array $aliases );
462
463    /**
464     * Convert certain index names to alternative names before querying the DB
465     *
466     * Note that this applies to indexes regardless of the table they belong to.
467     *
468     * This can be employed when an index was renamed X => Y in code, but the new Y-named
469     * indexes were not yet built on all DBs. After all the Y-named ones are added by the DBA,
470     * the aliases can be removed, and then the old X-named indexes dropped.
471     *
472     * @param string[] $aliases Map of (index alias => index name)
473     * @since 1.31
474     */
475    public function setIndexAliases( array $aliases );
476
477    /**
478     * Convert certain database domains to alternative ones
479     *
480     * This can be used for backwards compatibility logic.
481     *
482     * @param DatabaseDomain[]|string[] $aliases Map of (domain alias => domain)
483     * @since 1.35
484     */
485    public function setDomainAliases( array $aliases );
486
487    /**
488     * Get the TransactionProfiler used by this instance
489     *
490     * @return TransactionProfiler
491     * @since 1.35
492     */
493    public function getTransactionProfiler(): TransactionProfiler;
494}