Merge "rdbms: remove DB domain parameter from various lag/read-only methods"

This commit is contained in:
jenkins-bot 2022-10-18 23:17:23 +00:00 committed by Gerrit Code Review
commit 11de30462e
4 changed files with 39 additions and 59 deletions

View file

@ -137,10 +137,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
return true;
}
$lb = $this->getLoadBalancer( $location );
$domainId = $this->getDomainId( $lb->getServerInfo( $lb->getWriterIndex() ) );
return ( $lb->getReadOnlyReason( $domainId ) !== false );
return ( $this->getLoadBalancer( $location )->getReadOnlyReason() !== false );
}
/**

View file

@ -1060,15 +1060,17 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
public function wasErrorReissuable();
/**
* Wait for the replica DB to catch up to a given primary DB position
* Wait for the replica server to catch up to a given primary server position
*
* Note that this does not start any new transactions. If any existing transaction
* is flushed, and this is called, then queries will reflect the point the DB was synced
* up to (on success) without interference from REPEATABLE-READ snapshots.
* Note that this does not start any new transactions.
*
* Callers might want to flush any existing transaction before invoking this method.
* Upon success, this assures that replica server queries will reflect all changes up
* to the given position, without interference from prior REPEATABLE-READ snapshots.
*
* @param DBPrimaryPos $pos
* @param int $timeout The maximum number of seconds to wait for synchronisation
* @return int|null Zero if the replica DB was past that position already,
* @return int|null Zero if the replica DB server was past that position already,
* greater than zero if we waited for some period of time, less than
* zero if it timed out, and null on error
* @throws DBError If an error occurs, {@see query}
@ -1094,7 +1096,7 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
public function getPrimaryPos();
/**
* @return bool Whether the DB is marked as read-only server-side
* @return bool Whether the DB server is marked as read-only server-side
* @throws DBError If an error occurs, {@see query}
* @since 1.28
*/
@ -1350,8 +1352,8 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
*
* Note that a call to IDatabase::rollback() will also roll back any open atomic sections.
*
* @note As a micro-optimization to save a few DB calls, this method may only
* be called when startAtomic() was called with the ATOMIC_CANCELABLE flag.
* @note As an optimization to save rountrips, this method may only be called
* when startAtomic() was called with the ATOMIC_CANCELABLE flag.
* @since 1.31
* @see IDatabase::startAtomic
* @param string $fname
@ -1423,7 +1425,7 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
* @see Database::cancelAtomic
*
* @param string $fname Caller name (usually __METHOD__)
* @param callable $callback Callback that issues DB updates
* @param callable $callback Callback that issues write queries
* @param string $cancelable Pass self::ATOMIC_CANCELABLE to use a
* savepoint and enable self::cancelAtomic() for this section.
* @return mixed Result of the callback (since 1.28)
@ -1524,7 +1526,8 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
* This is intended for clearing out REPEATABLE-READ snapshots so that callers can
* see a new point-in-time of the database. This is useful when one of many transaction
* rounds finished and significant time will pass in the script's lifetime. It is also
* useful to call on a replica DB after waiting on replication to catch up to the primary DB.
* useful to call on a replica server after waiting on replication to catch up to the
* primary server.
*
* @param string $fname Calling function name
* @param string $flush Flush flag, set to situationally valid IDatabase::FLUSHING_*
@ -1558,7 +1561,7 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
public function getLag();
/**
* Get the replica DB lag when the current transaction started
* Get the replica server lag when the current transaction started
* or a general lag estimate if not transaction is active
*
* This is useful when transactions might use snapshot isolation
@ -1649,7 +1652,7 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
/**
* Acquire a named lock, flush any transaction, and return an RAII style unlocker object
*
* Only call this from outer transaction scope and when only one DB will be affected.
* Only call this from outer transaction scope and when only one DB server will be affected.
* See https://www.mediawiki.org/wiki/Database_transactions for details.
*
* This is suitable for transactions that need to be serialized using cooperative locks,
@ -1689,7 +1692,7 @@ interface IDatabase extends ISQLPlatform, DbQuoter, IDatabaseFlags {
public function setBigSelects( $value = true );
/**
* @return bool Whether this DB is read-only
* @return bool Whether this DB server is read-only
* @since 1.27
*/
public function isReadOnly();

View file

@ -517,10 +517,9 @@ interface ILoadBalancer {
/**
* @note This method will trigger a DB connection if not yet done
* @param string|false $domain DB domain ID or false for the local domain
* @return bool Whether the database for generic connections this request is highly "lagged"
*/
public function getLaggedReplicaMode( $domain = false );
public function getLaggedReplicaMode();
/**
* Checks whether the database for generic connections this request was both:
@ -534,7 +533,7 @@ interface ILoadBalancer {
/**
* @note This method may trigger a DB connection if not yet done
* @param string|false $domain DB domain ID or false for the local domain
* @param string|false $domain DB domain ID or false (unused and deprecated since 1.40)
* @return string|false Reason the primary is read-only or false if it is not
*/
public function getReadOnlyReason( $domain = false );
@ -551,10 +550,9 @@ interface ILoadBalancer {
* 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 string|false $domain Domain ID or false for the default database
* @return array{0:string,1:float|int|false,2:int} (host, max lag, index of max lagged host)
*/
public function getMaxLag( $domain = false );
public function getMaxLag();
/**
* Get an estimate of replication lag (in seconds) for each server
@ -563,10 +561,9 @@ interface ILoadBalancer {
*
* Values may be "false" if replication is too broken to estimate
*
* @param string|false $domain
* @return float[]|int[]|false[] Map of (server index => lag) in order of server index
*/
public function getLagTimes( $domain = false );
public function getLagTimes();
/**
* Wait for a replica DB to reach a specified primary position

View file

@ -447,12 +447,11 @@ class LoadBalancer implements ILoadBalancerForOwner {
/**
* @param array $loads
* @param string $domain Resolved DB domain
* @param int|float $maxLag Restrict the maximum allowed lag to this many seconds, or INF for no max
* @return int|string|false
*/
private function getRandomNonLagged( array $loads, string $domain, $maxLag = INF ) {
$lags = $this->getLagTimes( $domain );
private function getRandomNonLagged( array $loads, $maxLag = INF ) {
$lags = $this->getLagTimes();
# Unset excessively lagged servers
foreach ( $lags as $i => $lag ) {
@ -645,11 +644,11 @@ class LoadBalancer implements ILoadBalancerForOwner {
// avoid lagged servers so as to avoid excessive delay in that method.
$ago = microtime( true ) - $this->waitForPos->asOfTime();
// Aim for <= 1 second of waiting (being too picky can backfire)
$i = $this->getRandomNonLagged( $currentLoads, $domain, $ago + 1 );
$i = $this->getRandomNonLagged( $currentLoads, $ago + 1 );
}
if ( $i === false ) {
// Any server with less lag than it's 'max lag' param is preferable
$i = $this->getRandomNonLagged( $currentLoads, $domain );
$i = $this->getRandomNonLagged( $currentLoads );
}
if ( $i === false && count( $currentLoads ) ) {
// All replica DBs lagged. Switch to read-only mode
@ -956,7 +955,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
if (
$conn &&
$serverIndex === $this->getWriterIndex() &&
$this->getLaggedReplicaMode( $domain ) &&
$this->getLaggedReplicaMode() &&
!is_string( $conn->getLBInfo( $conn::LB_READ_ONLY_REASON ) )
) {
$genericIndex = $this->getExistingReaderIndex( self::GROUP_GENERIC );
@ -1348,7 +1347,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
? ( $domain->getDatabase() ?? $server['dbname'] ?? null )
: $domain->getDatabase(),
// Override the $server default schema with that of $domain if specified
'schema' => $domain->getSchema() ?? $server['schema'] ?? null,
'schema' => $domain->getSchema(),
// Use the table prefix specified in $domain
'tablePrefix' => $domain->getTablePrefix(),
// Participate in transaction rounds if $server does not specify otherwise
@ -2101,9 +2100,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
return false;
}
public function getLaggedReplicaMode( $domain = false ) {
$domain = $this->resolveDomainID( $domain );
public function getLaggedReplicaMode() {
if ( $this->laggedReplicaMode ) {
// Stay in lagged replica mode once it is observed on any domain
return true;
@ -2111,7 +2108,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
if ( $this->hasStreamingReplicaServers() ) {
// This will set "laggedReplicaMode" as needed
$this->getReaderIndex( self::GROUP_GENERIC, $domain );
$this->getReaderIndex( self::GROUP_GENERIC, self::DOMAIN_ANY );
}
return $this->laggedReplicaMode;
@ -2122,13 +2119,11 @@ class LoadBalancer implements ILoadBalancerForOwner {
}
public function getReadOnlyReason( $domain = false ) {
$domainInstance = DatabaseDomain::newFromId( $this->resolveDomainID( $domain ) );
if ( $this->readOnlyReason !== false ) {
return $this->readOnlyReason;
} elseif ( $this->isPrimaryRunningReadOnly( $domainInstance ) ) {
} elseif ( $this->isPrimaryRunningReadOnly() ) {
return 'The primary database server is running in read-only mode.';
} elseif ( $this->getLaggedReplicaMode( $domain ) ) {
} elseif ( $this->getLaggedReplicaMode() ) {
$genericIndex = $this->getExistingReaderIndex( self::GROUP_GENERIC );
return ( $genericIndex !== self::READER_INDEX_NONE )
@ -2147,12 +2142,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
*/
private function isPrimaryConnectionReadOnly( IDatabase $conn, $flags = 0 ) {
// Note that table prefixes are not related to server-side read-only mode
$key = $this->srvCache->makeGlobalKey(
'rdbms-server-readonly',
$conn->getServerName(),
(string)$conn->getDBname(),
(string)$conn->dbSchema()
);
$key = $this->srvCache->makeGlobalKey( 'rdbms-server-readonly', $conn->getServerName() );
if ( self::fieldHasBit( $flags, self::CONN_REFRESH_READ_ONLY ) ) {
// Refresh the local server cache. This is useful when the caller is
@ -2184,28 +2174,25 @@ class LoadBalancer implements ILoadBalancerForOwner {
/**
* @note This method suppresses DBError exceptions in order to avoid severe downtime
* @param DatabaseDomain $domain
* @return bool Whether the entire primary DB server or the local domain DB is read-only
*/
private function isPrimaryRunningReadOnly( DatabaseDomain $domain ) {
private function isPrimaryRunningReadOnly() {
// Context will often be HTTP GET/HEAD; heavily cache the results
return (bool)$this->wanCache->getWithSetCallback(
// Note that table prefixes are not related to server-side read-only mode
$this->wanCache->makeGlobalKey(
'rdbms-server-readonly',
$this->getPrimaryServerName(),
$domain->getDatabase(),
(string)$domain->getSchema()
$this->getPrimaryServerName()
),
self::TTL_CACHE_READONLY,
function () use ( $domain ) {
function () {
$scope = $this->trxProfiler->silenceForScope();
$index = $this->getWriterIndex();
// Refresh the local server cache as well. This is done in order to avoid
// backfilling the WANCache with data that is already significantly stale
$flags = self::CONN_SILENCE_ERRORS | self::CONN_REFRESH_READ_ONLY;
$conn = $this->getServerConnection( $index, $domain->getId(), $flags );
$conn = $this->getServerConnection( $index, self::DOMAIN_ANY, $flags );
if ( $conn ) {
try {
$readOnly = (int)$this->isPrimaryConnectionReadOnly( $conn );
@ -2314,15 +2301,13 @@ class LoadBalancer implements ILoadBalancerForOwner {
return $count;
}
public function getMaxLag( $domain = false ) {
$domain = $this->resolveDomainID( $domain );
public function getMaxLag() {
$host = '';
$maxLag = -1;
$maxIndex = 0;
if ( $this->hasReplicaServers() ) {
$lagTimes = $this->getLagTimes( $domain );
$lagTimes = $this->getLagTimes();
foreach ( $lagTimes as $i => $lag ) {
if ( $this->groupLoads[self::GROUP_GENERIC][$i] > 0 && $lag > $maxLag ) {
$maxLag = $lag;
@ -2335,9 +2320,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
return [ $host, $maxLag, $maxIndex ];
}
public function getLagTimes( $domain = false ) {
$domain = $this->resolveDomainID( $domain );
public function getLagTimes() {
if ( !$this->hasReplicaServers() ) {
return [ $this->getWriterIndex() => 0 ]; // no replication = no lag
}