*/
/**
- * Interface for database load balancing object that manages IDatabase handles
+ * Database cluster connection, tracking, load balancing, and transaction manager interface
+ *
+ * A "cluster" is considered to be one master database and zero or more replica databases.
+ * Typically, the replica DBs replicate from the master asynchronously. The first node in the
+ * "servers" configuration array is always considered the "master". However, this class can still
+ * be used when all or some of the "replica" DBs are multi-master peers of the master or even
+ * when all the DBs are non-replicating clones of each other holding read-only data. Thus, the
+ * role of "master" is in some cases merely nominal.
+ *
+ * By default, each DB server uses DBO_DEFAULT for its 'flags' setting, unless explicitly set
+ * otherwise in configuration. DBO_DEFAULT behavior depends on whether 'cliMode' is set:
+ * - In CLI mode, the flag has no effect with regards to LoadBalancer.
+ * - In non-CLI mode, the flag causes implicit transactions to be used; the first query on
+ * a database starts a transaction on that database. The transactions are meant to remain
+ * pending until either commitMasterChanges() or rollbackMasterChanges() is called. The
+ * application must have some point where it calls commitMasterChanges() near the end of
+ * the PHP request.
+ * Every iteration of beginMasterChanges()/commitMasterChanges() is called a "transaction round".
+ * Rounds are useful on the master DB connections because they make single-DB (and by and large
+ * multi-DB) updates in web requests all-or-nothing. Also, transactions on replica DBs are useful
+ * when REPEATABLE-READ or SERIALIZABLE isolation is used because all foriegn keys and constraints
+ * hold across separate queries in the DB transaction since the data appears within a consistent
+ * point-in-time snapshot.
+ *
+ * The typical caller will use LoadBalancer::getConnection( DB_* ) to yield a live database
+ * connection handle. The choice of which DB server to use is based on pre-defined loads for
+ * weighted random selection, adjustments thereof by LoadMonitor, and the amount of replication
+ * lag on each DB server. Lag checks might cause problems in certain setups, so they should be
+ * tuned in the server configuration maps as follows:
+ * - Master + N Replica(s): set 'max lag' to an appropriate threshold for avoiding any database
+ * lagged by this much or more. If all DBs are this lagged, then the load balancer considers
+ * the cluster to be read-only.
+ * - Galera Cluster: Seconds_Behind_Master will be 0, so there probably is nothing to tune.
+ * Note that lag is still possible depending on how wsrep-sync-wait is set server-side.
+ * - Read-only archive clones: set 'is static' in the server configuration maps. This will
+ * treat all such DBs as having 0 lag.
+ * - SQL load balancing proxy: any proxy should handle lag checks on its own, so the 'max lag'
+ * parameter should probably be set to INF in the server configuration maps. This will make
+ * the load balancer ignore whatever it detects as the lag of the logical replica is (which
+ * would probably just randomly bounce around).
+ *
+ * If using a SQL proxy service, it would probably be best to have two proxy hosts for the
+ * load balancer to talk to. One would be the 'host' of the master server entry and another for
+ * the (logical) replica server entry. The proxy could map the load balancer's "replica" DB to
+ * any number of physical replica DBs.
*
* @since 1.28
* @ingroup Database
*/
interface ILoadBalancer {
+ /** @var integer Request a replica DB connection */
+ const DB_REPLICA = -1;
+ /** @var integer Request a master DB connection */
+ const DB_MASTER = -2;
+
/**
- * @param array $params Array with keys:
+ * Construct a manager of IDatabase connection objects
+ *
+ * @param array $params Parameter map with keys:
* - servers : Required. Array of server info structures.
+ * - localDomain: A DatabaseDomain or domain ID string.
* - loadMonitor : Name of a class used to fetch server lag and load.
* - readOnlyReason : Reason the master DB is read-only if so [optional]
* - waitTimeout : Maximum time to wait for replicas for consistency [optional]
* - srvCache : BagOStuff object for server cache [optional]
* - memCache : BagOStuff object for cluster memory cache [optional]
* - wanCache : WANObjectCache object [optional]
- * - hostname : the name of the current server [optional]
+ * - hostname : The name of the current server [optional]
+ * - cliMode: Whether the execution context is a CLI script. [optional]
+ * - profiler : Class name or instance with profileIn()/profileOut() methods. [optional]
+ * - trxProfiler: TransactionProfiler instance. [optional]
+ * - replLogger: PSR-3 logger instance. [optional]
+ * - connLogger: PSR-3 logger instance. [optional]
+ * - queryLogger: PSR-3 logger instance. [optional]
+ * - perfLogger: PSR-3 logger instance. [optional]
+ * - errorLogger : Callback that takes an Exception and logs it. [optional]
* @throws InvalidArgumentException
*/
public function __construct( array $params );
*
* Side effect: opens connections to databases
* @param string|bool $group Query group, or false for the generic reader
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @throws DBError
* @return bool|int|string
*/
- public function getReaderIndex( $group = false, $wiki = false );
+ public function getReaderIndex( $group = false, $domain = false );
/**
* Set the master wait position
*
* @param int $i Server index
* @param array|string|bool $groups Query group(s), or false for the generic reader
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
*
* @throws DBError
* @return IDatabase
*/
- public function getConnection( $i, $groups = [], $wiki = false );
+ public function getConnection( $i, $groups = [], $domain = false );
/**
* Mark a foreign connection as being available for reuse under a different
*
* @param int $db
* @param array|string|bool $groups Query group(s), or false for the generic reader
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @return DBConnRef
*/
- public function getConnectionRef( $db, $groups = [], $wiki = false );
+ public function getConnectionRef( $db, $groups = [], $domain = false );
/**
* Get a database connection handle reference without connecting yet
*
* @param int $db
* @param array|string|bool $groups Query group(s), or false for the generic reader
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @return DBConnRef
*/
- public function getLazyConnectionRef( $db, $groups = [], $wiki = false );
+ public function getLazyConnectionRef( $db, $groups = [], $domain = false );
/**
* Open a connection to the server given by the specified index
* @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
*
* @param int $i Server index
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @return IDatabase|bool Returns false on errors
+ * @throws DBAccessError
*/
- public function openConnection( $i, $wiki = false );
+ public function openConnection( $i, $domain = false );
/**
* @return int
/**
* @note This method will trigger a DB connection if not yet done
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @return bool Whether the generic connection for reads is highly "lagged"
*/
- public function getLaggedReplicaMode( $wiki = false );
+ public function getLaggedReplicaMode( $domain = false );
/**
* @note This method will never cause a new DB connection
/**
* @note This method may trigger a DB connection if not yet done
- * @param string|bool $wiki Wiki ID, or false for the current wiki
+ * @param string|bool $domain Domain ID, or false for the current domain
* @param IDatabase|null DB master connection; used to avoid loops [optional]
* @return string|bool Reason the master is read-only or false if it is not
*/
- public function getReadOnlyReason( $wiki = false, IDatabase $conn = null );
+ public function getReadOnlyReason( $domain = false, IDatabase $conn = null );
/**
* Disables/enables lag checks
* May attempt to open connections to replica DBs on the default DB. If there is
* no lag, the maximum lag will be reported as -1.
*
- * @param bool|string $wiki Wiki ID, or false for the default database
+ * @param bool|string $domain Domain ID, or false for the default database
* @return array ( host, max lag, index of max lagged host )
*/
- public function getMaxLag( $wiki = false );
+ public function getMaxLag( $domain = false );
/**
* Get an estimate of replication lag (in seconds) for each server
*
* Values may be "false" if replication is too broken to estimate
*
- * @param string|bool $wiki
+ * @param string|bool $domain
* @return int[] Map of (server index => float|int|bool)
*/
- public function getLagTimes( $wiki = false );
+ public function getLagTimes( $domain = false );
/**
* Get the lag in seconds for a given connection, or zero if this load
*/
public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = null );
- /**
- * Clear the cache for slag lag delay times
- *
- * This is only used for testing
- */
- public function clearLagTimeCache();
-
/**
* Set a callback via IDatabase::setTransactionListener() on
* all current and future master connections of this load balancer
* @param callable|null $callback
*/
public function setTransactionListener( $name, callable $callback = null );
+
+ /**
+ * Set a new table prefix for the existing local domain ID for testing
+ *
+ * @param string $prefix
+ */
+ public function setDomainPrefix( $prefix );
+
+ /**
+ * Make certain table names use their own database, schema, and table prefix
+ * when passed into SQL queries pre-escaped and without a qualified database name
+ *
+ * For example, "user" can be converted to "myschema.mydbname.user" for convenience.
+ * Appearances like `user`, somedb.user, somedb.someschema.user will used literally.
+ *
+ * Calling this twice will completely clear any old table aliases. Also, note that
+ * callers are responsible for making sure the schemas and databases actually exist.
+ *
+ * @param array[] $aliases Map of (table => (dbname, schema, prefix) map)
+ */
+ public function setTableAliases( array $aliases );
}