1<?php 2/** 3 * Generator and manager of database load balancing instances 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup Database 22 */ 23 24namespace Wikimedia\Rdbms; 25 26use InvalidArgumentException; 27 28/** 29 * An interface for generating database load balancers 30 * @ingroup Database 31 * @since 1.28 32 */ 33interface ILBFactory { 34 /** Idion for "no special shutdown flags" */ 35 public const SHUTDOWN_NORMAL = 0; 36 /** Do not save "session consistency" DB replication positions */ 37 public const SHUTDOWN_NO_CHRONPROT = 1; 38 39 /** @var string Default main LB cluster name (do not change this) */ 40 public const CLUSTER_MAIN_DEFAULT = 'DEFAULT'; 41 42 /** 43 * Construct a manager of ILoadBalancer instances 44 * 45 * Sub-classes will extend the required keys in $conf with additional parameters 46 * 47 * @param array $conf Array with keys: 48 * - localDomain: A DatabaseDomain or domain ID string. 49 * - readOnlyReason: Reason the primary DB is read-only if so [optional] 50 * - srvCache: BagOStuff instance for server cache [optional] 51 * - cpStash: BagOStuff instance for ChronologyProtector store [optional] 52 * See [ChronologyProtector requirements](@ref ChronologyProtector-storage-requirements). 53 * - wanCache: WANObjectCache instance [optional] 54 * - cliMode: Whether the execution context is a CLI script. [optional] 55 * - maxLag: Try to avoid DB replicas with lag above this many seconds [optional] 56 * - profiler: Class name or instance with profileIn()/profileOut() methods. [optional] 57 * - trxProfiler: TransactionProfiler instance. [optional] 58 * - replLogger: PSR-3 logger instance. [optional] 59 * - connLogger: PSR-3 logger instance. [optional] 60 * - queryLogger: PSR-3 logger instance. [optional] 61 * - perfLogger: PSR-3 logger instance. [optional] 62 * - errorLogger: Callback that takes an Exception and logs it. [optional] 63 * - deprecationLogger: Callback to log a deprecation warning. [optional] 64 * - secret: Secret string to use for HMAC hashing [optional] 65 * - criticalSectionProvider: CriticalSectionProvider instance [optional] 66 * @throws InvalidArgumentException 67 */ 68 public function __construct( array $conf ); 69 70 /** 71 * Close all connections and make further attempts to open connections result in DBAccessError 72 * 73 * This only applies to the tracked load balancer instances. 74 * 75 * @see ILoadBalancer::disable() 76 */ 77 public function destroy(); 78 79 /** 80 * Get the local (and default) database domain ID of connection handles 81 * 82 * @see DatabaseDomain 83 * @return string Database domain ID; this specifies DB name, schema, and table prefix 84 * @since 1.32 85 */ 86 public function getLocalDomainID(); 87 88 /** 89 * @param DatabaseDomain|string|bool $domain Database domain 90 * @return string Value of $domain if provided or the local domain otherwise 91 * @since 1.32 92 */ 93 public function resolveDomainID( $domain ); 94 95 /** 96 * Close all connections and redefine the local domain for testing or schema creation 97 * 98 * This only applies to the tracked load balancer instances. 99 * 100 * @param DatabaseDomain|string $domain 101 * @since 1.33 102 */ 103 public function redefineLocalDomain( $domain ); 104 105 /** 106 * Create a new load balancer instance for a main cluster 107 * 108 * The resulting object will be untracked and the caller is responsible for cleaning it up. 109 * Database replication positions will not be saved by ChronologyProtector. 110 * 111 * This method is for only advanced usage and callers should almost always use 112 * getMainLB() instead. This method can be useful when a table is used as a key/value 113 * store. In that cases, one might want to query it in autocommit mode (DBO_TRX off) 114 * but still use DBO_TRX transaction rounds on other tables. 115 * 116 * @param bool|string $domain Domain ID, or false for the current domain 117 * @param int|null $owner Owner ID of the new instance (e.g. this LBFactory ID) 118 * @return ILoadBalancer 119 */ 120 public function newMainLB( $domain = false, $owner = null ): ILoadBalancer; 121 122 /** 123 * Get the tracked load balancer instance for a main cluster 124 * 125 * If no tracked instances exists, then one will be instantiated 126 * 127 * @param bool|string $domain Domain ID, or false for the current domain 128 * @return ILoadBalancer 129 */ 130 public function getMainLB( $domain = false ): ILoadBalancer; 131 132 /** 133 * Create a new load balancer instance for an external cluster 134 * 135 * The resulting object will be untracked and the caller is responsible for cleaning it up. 136 * Database replication positions will not be saved by ChronologyProtector. 137 * 138 * This method is for only advanced usage and callers should almost always use 139 * getExternalLB() instead. This method can be useful when a table is used as a 140 * key/value store. In that cases, one might want to query it in autocommit mode 141 * (DBO_TRX off) but still use DBO_TRX transaction rounds on other tables. 142 * 143 * @param string $cluster External cluster name 144 * @param int|null $owner Owner ID of the new instance (e.g. this LBFactory ID) 145 * @return ILoadBalancer 146 */ 147 public function newExternalLB( $cluster, $owner = null ): ILoadBalancer; 148 149 /** 150 * Get the tracked load balancer instance for an external cluster 151 * 152 * If no tracked instances exists, then one will be instantiated 153 * 154 * @param string $cluster External cluster name 155 * @return ILoadBalancer 156 */ 157 public function getExternalLB( $cluster ): ILoadBalancer; 158 159 /** 160 * Get the tracked load balancer instances for all main clusters 161 * 162 * If no tracked instance exists for a cluster, then one will be instantiated 163 * 164 * Note that default main cluster name is ILoadBalancer::CLUSTER_MAIN_DEFAULT 165 * 166 * @return ILoadBalancer[] Map of (cluster name => ILoadBalancer) 167 * @since 1.29 168 */ 169 public function getAllMainLBs(): array; 170 171 /** 172 * Get the tracked load balancer instances for all external clusters 173 * 174 * If no tracked instance exists for a cluster, then one will be instantiated 175 * 176 * @return ILoadBalancer[] Map of (cluster name => ILoadBalancer) 177 * @since 1.29 178 */ 179 public function getAllExternalLBs(): array; 180 181 /** 182 * Execute a function for each instantiated tracked load balancer instance 183 * 184 * The callback is called with the load balancer as the first parameter, 185 * and $params passed as the subsequent parameters. 186 * 187 * @param callable $callback 188 * @param array $params 189 */ 190 public function forEachLB( $callback, array $params = [] ); 191 192 /** 193 * Prepare all instantiated tracked load balancer instances for shutdown 194 * 195 * @param int $flags Bit field of ILBFactory::SHUTDOWN_* constants 196 * @param callable|null $workCallback Work to mask ChronologyProtector writes 197 * @param int|null &$cpIndex Position key write counter for ChronologyProtector [returned] 198 * @param string|null &$cpClientId Client ID hash for ChronologyProtector [returned] 199 */ 200 public function shutdown( 201 $flags = self::SHUTDOWN_NORMAL, 202 callable $workCallback = null, 203 &$cpIndex = null, 204 &$cpClientId = null 205 ); 206 207 /** 208 * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot 209 * 210 * This only applies to the instantiated tracked load balancer instances. 211 * 212 * This is useful for getting rid of stale data from an implicit transaction round 213 * 214 * @param string $fname Caller name 215 */ 216 public function flushReplicaSnapshots( $fname = __METHOD__ ); 217 218 /** 219 * Commit open transactions on all connections 220 * 221 * This only applies to the instantiated tracked load balancer instances. 222 * 223 * This is useful for two main cases: 224 * - a) Committing changes to the masters 225 * - b) Releasing the snapshot on all connections, primary and replica DBs 226 * 227 * @param string $fname Caller name 228 * @param array $options Options map: 229 * - maxWriteDuration: abort if more than this much time was spent in write queries 230 */ 231 public function commitAll( $fname = __METHOD__, array $options = [] ); 232 233 /** 234 * Flush any primary transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set) 235 * 236 * The DBO_TRX setting will be reverted to the default in each of these methods: 237 * - commitPrimaryChanges() 238 * - rollbackPrimaryChanges() 239 * - commitAll() 240 * 241 * This only applies to the tracked load balancer instances. 242 * 243 * This allows for custom transaction rounds from any outer transaction scope. 244 * 245 * @param string $fname 246 * @throws DBTransactionError 247 * @since 1.37 248 */ 249 public function beginPrimaryChanges( $fname = __METHOD__ ); 250 251 /** 252 * @deprecated since 1.37 Use beginPrimaryChanges() instead. 253 * @param string $fname 254 * @throws DBTransactionError 255 */ 256 public function beginMasterChanges( $fname = __METHOD__ ); 257 258 /** 259 * Commit changes and clear view snapshots on all primary connections 260 * 261 * This only applies to the instantiated tracked load balancer instances. 262 * 263 * @param string $fname Caller name 264 * @param array $options Options map: 265 * - maxWriteDuration: abort if more than this much time was spent in write queries 266 * @throws DBTransactionError 267 * @since 1.37 268 */ 269 public function commitPrimaryChanges( $fname = __METHOD__, array $options = [] ); 270 271 /** 272 * @deprecated since 1.37; please use commitPrimaryChanges() instead. 273 * @param string $fname Caller name 274 * @param array $options Options map: 275 * - maxWriteDuration: abort if more than this much time was spent in write queries 276 * @throws DBTransactionError 277 */ 278 public function commitMasterChanges( $fname = __METHOD__, array $options = [] ); 279 280 /** 281 * Rollback changes on all primary connections 282 * 283 * This only applies to the instantiated tracked load balancer instances. 284 * 285 * @param string $fname Caller name 286 * @since 1.37 287 */ 288 public function rollbackPrimaryChanges( $fname = __METHOD__ ); 289 290 /** 291 * @deprecated since 1.37; please use rollbackPrimaryChanges() instead. 292 * @param string $fname Caller name 293 */ 294 public function rollbackMasterChanges( $fname = __METHOD__ ); 295 296 /** 297 * Check if an explicit transaction round is active 298 * 299 * @return bool 300 * @since 1.29 301 */ 302 public function hasTransactionRound(); 303 304 /** 305 * Check if transaction rounds can be started, committed, or rolled back right now 306 * 307 * This can be used as a recusion guard to avoid exceptions in transaction callbacks. 308 * 309 * @return bool 310 * @since 1.32 311 */ 312 public function isReadyForRoundOperations(); 313 314 /** 315 * Determine if any primary connection has pending changes 316 * 317 * This only applies to the instantiated tracked load balancer instances. 318 * 319 * @return bool 320 * @since 1.37 321 */ 322 public function hasPrimaryChanges(); 323 324 /** 325 * @deprecated since 1.37; please use hasPrimaryChanges() instead. 326 * @return bool 327 */ 328 public function hasMasterChanges(); 329 330 /** 331 * Detemine if any lagged replica DB connection was used 332 * 333 * This only applies to the instantiated tracked load balancer instances. 334 * 335 * @return bool 336 */ 337 public function laggedReplicaUsed(); 338 339 /** 340 * Determine if any primary connection has pending/written changes from this request 341 * 342 * This only applies to the instantiated tracked load balancer instances. 343 * 344 * @param float|null $age How many seconds ago is "recent" [defaults to LB lag wait timeout] 345 * @return bool 346 */ 347 public function hasOrMadeRecentPrimaryChanges( $age = null ); 348 349 /** 350 * @deprecated since 1.37; please use hasOrMadeRecentPrimaryChanges() instead. 351 * @param float|null $age How many seconds ago is "recent" [defaults to LB lag wait timeout] 352 * @return bool 353 */ 354 public function hasOrMadeRecentMasterChanges( $age = null ); 355 356 /** 357 * Waits for the replica DBs to catch up to the current primary position 358 * 359 * Use this when updating very large numbers of rows, as in maintenance scripts, 360 * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs. 361 * 362 * By default this waits on all DB clusters actually used in this request. 363 * This makes sense when lag being waiting on is caused by the code that does this check. 364 * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters 365 * that were not changed since the last wait check. To forcefully wait on a specific cluster 366 * for a given domain, use the 'domain' parameter. To forcefully wait on an "external" cluster, 367 * use the "cluster" parameter. 368 * 369 * Never call this function after a large DB write that is *still* in a transaction. 370 * It only makes sense to call this after the possible lag inducing changes were committed. 371 * 372 * This only applies to the instantiated tracked load balancer instances. 373 * 374 * @param array $opts Optional fields that include: 375 * - domain: Wait on the load balancer DBs that handles the given domain ID. 376 * - cluster: Wait on the given external load balancer DBs. 377 * - timeout: Max wait time. Default: 60 seconds for CLI, 1 second for web. 378 * - ifWritesSince: Only wait if writes were done since this UNIX timestamp. 379 * @return bool True on success, false if a timeout or error occurred while waiting 380 */ 381 public function waitForReplication( array $opts = [] ); 382 383 /** 384 * Add a callback to be run in every call to waitForReplication() before waiting 385 * 386 * Callbacks must clear any transactions that they start. 387 * 388 * @param string $name Callback name 389 * @param callable|null $callback Use null to unset a callback 390 */ 391 public function setWaitForReplicationListener( $name, callable $callback = null ); 392 393 /** 394 * Get a token asserting that no transaction writes are active on tracked load balancers 395 * 396 * @param string $fname Caller name (e.g. __METHOD__) 397 * @return mixed A value to pass to commitAndWaitForReplication() 398 */ 399 public function getEmptyTransactionTicket( $fname ); 400 401 /** 402 * Call commitPrimaryChanges() and waitForReplication() if $ticket indicates it is safe 403 * 404 * The ticket is used to check that the caller owns the transaction round or can act on 405 * behalf of the caller that owns the transaction round. 406 * 407 * @see commitPrimaryChanges() 408 * @see waitForReplication() 409 * 410 * @param string $fname Caller name (e.g. __METHOD__) 411 * @param mixed $ticket Result of getEmptyTransactionTicket() 412 * @param array $opts Options to waitForReplication() 413 * @return bool True if the wait was successful, false on timeout 414 */ 415 public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ); 416 417 /** 418 * Get the UNIX timestamp when the client last touched the DB, if they did so recently 419 * 420 * @param DatabaseDomain|string|bool $domain Domain ID, or false for the current domain 421 * @return float|false UNIX timestamp; false if not recent or on record 422 */ 423 public function getChronologyProtectorTouched( $domain = false ); 424 425 /** 426 * Disable the ChronologyProtector on all instantiated tracked load balancer instances 427 * 428 * This can be called at the start of special API entry points. 429 */ 430 public function disableChronologyProtection(); 431 432 /** 433 * Set a new table prefix for the existing local domain ID for testing 434 * 435 * @param string $prefix 436 * @since 1.33 437 */ 438 public function setLocalDomainPrefix( $prefix ); 439 440 /** 441 * Close all connections on instantiated tracked load balancer instances 442 */ 443 public function closeAll(); 444 445 /** 446 * @param string $agent Agent name for query profiling 447 */ 448 public function setAgentName( $agent ); 449 450 /** 451 * Append ?cpPosIndex parameter to a URL for ChronologyProtector purposes if needed 452 * 453 * Note that unlike cookies, this works across domains. 454 * 455 * @param string $url 456 * @param int $index Write counter index 457 * @return string 458 */ 459 public function appendShutdownCPIndexAsQuery( $url, $index ); 460 461 /** 462 * Get the client ID of the ChronologyProtector instance 463 * 464 * @return string Client ID 465 * @since 1.34 466 */ 467 public function getChronologyProtectorClientId(); 468 469 /** 470 * Inject HTTP request header/cookie information during setup of this instance 471 * 472 * @param array $info Map of fields, including: 473 * - IPAddress : IP address 474 * - UserAgent : User-Agent HTTP header 475 * - ChronologyProtection : cookie/header value specifying ChronologyProtector usage 476 * - ChronologyPositionIndex: timestamp used to get up-to-date DB positions for the agent 477 */ 478 public function setRequestInfo( array $info ); 479 480 /** 481 * Set the default timeout for replication wait checks 482 * 483 * @param int $seconds Timeout, in seconds 484 * @return int The previous default timeout 485 * @since 1.35 486 */ 487 public function setDefaultReplicationWaitTimeout( $seconds ); 488 489 /** 490 * Make certain table names use their own database, schema, and table prefix 491 * when passed into SQL queries pre-escaped and without a qualified database name 492 * 493 * For example, "user" can be converted to "myschema.mydbname.user" for convenience. 494 * Appearances like `user`, somedb.user, somedb.someschema.user will used literally. 495 * 496 * Calling this twice will completely clear any old table aliases. Also, note that 497 * callers are responsible for making sure the schemas and databases actually exist. 498 * 499 * @param array[] $aliases Map of (table => (dbname, schema, prefix) map) 500 * @since 1.31 501 */ 502 public function setTableAliases( array $aliases ); 503 504 /** 505 * Convert certain index names to alternative names before querying the DB 506 * 507 * Note that this applies to indexes regardless of the table they belong to. 508 * 509 * This can be employed when an index was renamed X => Y in code, but the new Y-named 510 * indexes were not yet built on all DBs. After all the Y-named ones are added by the DBA, 511 * the aliases can be removed, and then the old X-named indexes dropped. 512 * 513 * @param string[] $aliases Map of (index alias => index name) 514 * @since 1.31 515 */ 516 public function setIndexAliases( array $aliases ); 517 518 /** 519 * Convert certain database domains to alternative ones 520 * 521 * This can be used for backwards compatibility logic. 522 * 523 * @param DatabaseDomain[]|string[] $aliases Map of (domain alias => domain) 524 * @since 1.35 525 */ 526 public function setDomainAliases( array $aliases ); 527 528 /** 529 * Get the TransactionProfiler used by this instance 530 * 531 * @return TransactionProfiler 532 * @since 1.35 533 */ 534 public function getTransactionProfiler(): TransactionProfiler; 535} 536