private $mLoads;
/** @var array[] Map of (group => server index => weight) */
private $mGroupLoads;
- /** @var bool Whether to disregard slave lag as a factor in slave selection */
+ /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
private $mAllowLagged;
- /** @var integer Seconds to spend waiting on slave lag to resolve */
+ /** @var integer Seconds to spend waiting on replica DB lag to resolve */
private $mWaitTimeout;
/** @var array LBFactory information */
private $mParentInfo;
private $srvCache;
/** @var WANObjectCache */
private $wanCache;
+ /** @var TransactionProfiler */
+ protected $trxProfiler;
/** @var bool|DatabaseBase Database connection that caused a problem */
private $mErrorConnection;
- /** @var integer The generic (not query grouped) slave index (of $mServers) */
+ /** @var integer The generic (not query grouped) replica DB index (of $mServers) */
private $mReadIndex;
/** @var bool|DBMasterPos False if not set */
private $mWaitForPos;
- /** @var bool Whether the generic reader fell back to a lagged slave */
- private $laggedSlaveMode = false;
- /** @var bool Whether the generic reader fell back to a lagged slave */
- private $slavesDownMode = false;
+ /** @var bool Whether the generic reader fell back to a lagged replica DB */
+ private $laggedReplicaMode = false;
+ /** @var bool Whether the generic reader fell back to a lagged replica DB */
+ private $allReplicasDownMode = false;
/** @var string The last DB selection or connection error */
private $mLastError = 'Unknown error';
/** @var string|bool Reason the LB is read-only or false if not */
private $readOnlyReason = false;
/** @var integer Total connections opened */
private $connsOpened = 0;
-
- /** @var TransactionProfiler */
- protected $trxProfiler;
+ /** @var string|bool String if a requested DBO_TRX transaction round is active */
+ private $trxRoundId = false;
+ /** @var array[] Map of (name => callable) */
+ private $trxRecurringCallbacks = [];
/** @var integer Warn when this many connection are held */
const CONN_HELD_WARN_THRESHOLD = 10;
/** @var integer Default 'max lag' when unspecified */
const MAX_LAG = 10;
- /** @var integer Max time to wait for a slave to catch up (e.g. ChronologyProtector) */
+ /** @var integer Max time to wait for a replica DB to catch up (e.g. ChronologyProtector) */
const POS_WAIT_TIMEOUT = 10;
/** @var integer Seconds to cache master server read-only status */
const TTL_CACHE_READONLY = 5;
}
}
- # Find out if all the slaves with non-zero load are lagged
+ # Find out if all the replica DBs with non-zero load are lagged
$sum = 0;
foreach ( $loads as $load ) {
$sum += $load;
}
if ( $sum == 0 ) {
- # No appropriate DB servers except maybe the master and some slaves with zero load
+ # No appropriate DB servers except maybe the master and some replica DBs with zero load
# Do NOT use the master
# Instead, this function will return false, triggering read-only mode,
- # and a lagged slave will be used instead.
+ # and a lagged replica DB will be used instead.
return false;
}
}
/**
- * Get the index of the reader connection, which may be a slave
+ * Get the index of the reader connection, which may be a replica DB
* This takes into account load ratios and lag times. It should
* always return a consistent index during a given invocation
*
# Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
$this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
- $laggedSlaveMode = false;
+ $laggedReplicaMode = false;
# No server found yet
$i = false;
- $conn = false;
# First try quickly looking through the available servers for a server that
# meets our criteria
$currentLoads = $nonErrorLoads;
while ( count( $currentLoads ) ) {
- if ( $this->mAllowLagged || $laggedSlaveMode ) {
+ if ( $this->mAllowLagged || $laggedReplicaMode ) {
$i = ArrayUtils::pickRandom( $currentLoads );
} else {
$i = false;
$i = $this->getRandomNonLagged( $currentLoads, $wiki );
}
if ( $i === false && count( $currentLoads ) != 0 ) {
- # All slaves lagged. Switch to read-only mode
- wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
+ # All replica DBs lagged. Switch to read-only mode
+ wfDebugLog( 'replication', "All replica DBs lagged. Switch to read-only mode" );
$i = ArrayUtils::pickRandom( $currentLoads );
- $laggedSlaveMode = true;
+ $laggedReplicaMode = true;
}
}
}
if ( $i !== false ) {
- # Slave connection successful
- # Wait for the session master pos for a short time
+ # Replica DB connection successful.
+ # Wait for the session master pos for a short time.
if ( $this->mWaitForPos && $i > 0 ) {
- if ( !$this->doWait( $i ) ) {
- $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
- }
+ $this->doWait( $i );
}
if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
$this->mReadIndex = $i;
- # Record if the generic reader index is in "lagged slave" mode
- if ( $laggedSlaveMode ) {
- $this->laggedSlaveMode = true;
+ # Record if the generic reader index is in "lagged replica DB" mode
+ if ( $laggedReplicaMode ) {
+ $this->laggedReplicaMode = true;
}
}
$serverName = $this->getServerName( $i );
/**
* Set the master wait position
- * If a DB_SLAVE connection has been opened already, waits
+ * If a DB_REPLICA connection has been opened already, waits
* Otherwise sets a variable telling it to wait if such a connection is opened
* @param DBMasterPos $pos
*/
if ( $i > 0 ) {
if ( !$this->doWait( $i ) ) {
- $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
- $this->laggedSlaveMode = true;
+ $this->laggedReplicaMode = true;
}
}
}
/**
- * Set the master wait position and wait for a "generic" slave to catch up to it
+ * Set the master wait position and wait for a "generic" replica DB to catch up to it
*
* This can be used a faster proxy for waitForAll()
*
$i = $this->mReadIndex;
if ( $i <= 0 ) {
- // Pick a generic slave if there isn't one yet
+ // Pick a generic replica DB if there isn't one yet
$readLoads = $this->mLoads;
- unset( $readLoads[$this->getWriterIndex()] ); // slaves only
+ unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
$readLoads = array_filter( $readLoads ); // with non-zero load
$i = ArrayUtils::pickRandom( $readLoads );
}
}
/**
- * Set the master wait position and wait for ALL slaves to catch up to it
+ * Set the master wait position and wait for ALL replica DBs to catch up to it
* @param DBMasterPos $pos
* @param int $timeout Max seconds to wait; default is mWaitTimeout
* @return bool Success (able to connect and no timeouts reached)
}
/**
- * Wait for a given slave to catch up to the master pos stored in $this
+ * Wait for a given replica DB to catch up to the master pos stored in $this
* @param int $index Server index
* @param bool $open Check the server even if a new connection has to be made
* @param int $timeout Max seconds to wait; default is mWaitTimeout
$knownReachedPos = $this->srvCache->get( $key );
if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
wfDebugLog( 'replication', __METHOD__ .
- ": slave $server known to be caught up (pos >= $knownReachedPos).\n" );
+ ": replica DB $server known to be caught up (pos >= $knownReachedPos).\n" );
return true;
}
}
}
- wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
+ wfDebugLog( 'replication', __METHOD__ . ": Waiting for replica DB $server to catch up...\n" );
$timeout = $timeout ?: $this->mWaitTimeout;
$result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
if ( $result == -1 || is_null( $result ) ) {
- // Timed out waiting for slave, use master instead
+ // Timed out waiting for replica DB, use master instead
$msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
wfDebugLog( 'replication', "$msg\n" );
wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
}
# Operation-based index
- if ( $i == DB_SLAVE ) {
+ if ( $i == DB_REPLICA ) {
$this->mLastError = 'Unknown error'; // reset error string
# Try the general server pool if $groups are unavailable.
$i = in_array( false, $groups, true )
: $this->getReaderIndex( false, $wiki );
# Couldn't find a working server in getReaderIndex()?
if ( $i === false ) {
- $this->mLastError = 'No working slave server: ' . $this->mLastError;
+ $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
return $this->reportConnectionError();
}
$serverIndex = $conn->getLBInfo( 'serverIndex' );
$refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
if ( $serverIndex === null || $refCount === null ) {
- wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
/**
* This can happen in code like:
* foreach ( $dbs as $db ) {
- * $conn = $lb->getConnection( DB_SLAVE, [], $db );
+ * $conn = $lb->getConnection( DB_REPLICA, [], $db );
* ...
* $lb->reuseConnection( $conn );
* }
$this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() )
);
$db->setTransactionProfiler( $this->trxProfiler );
+ if ( $this->trxRoundId !== false ) {
+ $this->applyTransactionRoundFlags( $db );
+ }
+
+ if ( $server['serverIndex'] === $this->getWriterIndex() ) {
+ foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
+ $db->setTransactionListener( $name, $callback );
+ }
+ }
return $db;
}
* @return mixed
*/
public function getMasterPos() {
- # If this entire request was served from a slave without opening a connection to the
- # master (however unlikely that may be), then we can fetch the position from the slave.
+ # If this entire request was served from a replica DB without opening a connection to the
+ # master (however unlikely that may be), then we can fetch the position from the replica DB.
$masterConn = $this->getAnyOpenConnection( 0 );
if ( !$masterConn ) {
$serverCount = count( $this->mServers );
/**
* Commit transactions on all open connections
* @param string $fname Caller name
+ * @throws DBExpectedError
*/
public function commitAll( $fname = __METHOD__ ) {
- $this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( $fname ) {
- $conn->commit( $fname, IDatabase::FLUSHING_ALL_PEERS );
- } );
+ $failures = [];
+
+ $restore = ( $this->trxRoundId !== false );
+ $this->trxRoundId = false;
+ $this->forEachOpenConnection(
+ function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+ try {
+ $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+ } catch ( DBError $e ) {
+ MWExceptionHandler::logException( $e );
+ $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+ }
+ if ( $restore && $conn->getLBInfo( 'master' ) ) {
+ $this->undoTransactionRoundFlags( $conn );
+ }
+ }
+ );
+
+ if ( $failures ) {
+ throw new DBExpectedError(
+ null,
+ "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+ );
+ }
}
/**
* Perform all pre-commit callbacks that remain part of the atomic transactions
- * and disable any post-commit callbacks until runMasterPostCommitCallbacks()
+ * and disable any post-commit callbacks until runMasterPostTrxCallbacks()
* @since 1.28
*/
- public function runMasterPreCommitCallbacks() {
+ public function finalizeMasterChanges() {
$this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
- // Any error will cause all DB transactions to be rolled back together.
+ // Any error should cause all DB transactions to be rolled back together
+ $conn->setTrxEndCallbackSuppression( false );
$conn->runOnTransactionPreCommitCallbacks();
- // Defer post-commit callbacks until COMMIT finishes for all DBs.
- $conn->setPostCommitCallbackSupression( true );
+ // Defer post-commit callbacks until COMMIT finishes for all DBs
+ $conn->setTrxEndCallbackSuppression( true );
} );
}
}
// Assert that the time to replicate the transaction will be sane.
// If this fails, then all DB transactions will be rollback back together.
- $time = $conn->pendingWriteQueryDuration();
+ $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );
if ( $limit > 0 && $time > $limit ) {
throw new DBTransactionError(
$conn,
} );
}
+ /**
+ * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+ *
+ * The DBO_TRX setting will be reverted to the default in each of these methods:
+ * - commitMasterChanges()
+ * - rollbackMasterChanges()
+ * - commitAll()
+ * This allows for custom transaction rounds from any outer transaction scope.
+ *
+ * @param string $fname
+ * @throws DBExpectedError
+ * @since 1.28
+ */
+ public function beginMasterChanges( $fname = __METHOD__ ) {
+ if ( $this->trxRoundId !== false ) {
+ throw new DBTransactionError(
+ null,
+ "$fname: Transaction round '{$this->trxRoundId}' already started."
+ );
+ }
+ $this->trxRoundId = $fname;
+
+ $failures = [];
+ $this->forEachOpenMasterConnection(
+ function ( DatabaseBase $conn ) use ( $fname, &$failures ) {
+ $conn->setTrxEndCallbackSuppression( true );
+ try {
+ $conn->clearSnapshot( $fname );
+ } catch ( DBError $e ) {
+ MWExceptionHandler::logException( $e );
+ $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+ }
+ $conn->setTrxEndCallbackSuppression( false );
+ $this->applyTransactionRoundFlags( $conn );
+ }
+ );
+
+ if ( $failures ) {
+ throw new DBExpectedError(
+ null,
+ "$fname: Flush failed on server(s) " . implode( "\n", array_unique( $failures ) )
+ );
+ }
+ }
+
/**
* Issue COMMIT on all master connections where writes where done
* @param string $fname Caller name
+ * @throws DBExpectedError
*/
public function commitMasterChanges( $fname = __METHOD__ ) {
- $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
- if ( $conn->writesOrCallbacksPending() ) {
- $conn->commit( $fname, IDatabase::FLUSHING_ALL_PEERS );
+ $failures = [];
+
+ $restore = ( $this->trxRoundId !== false );
+ $this->trxRoundId = false;
+ $this->forEachOpenMasterConnection(
+ function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+ try {
+ if ( $conn->writesOrCallbacksPending() ) {
+ $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+ } elseif ( $restore ) {
+ $conn->clearSnapshot( $fname );
+ }
+ } catch ( DBError $e ) {
+ MWExceptionHandler::logException( $e );
+ $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+ }
+ if ( $restore ) {
+ $this->undoTransactionRoundFlags( $conn );
+ }
}
- } );
+ );
+
+ if ( $failures ) {
+ throw new DBExpectedError(
+ null,
+ "$fname: Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+ );
+ }
}
/**
- * Issue all pending post-commit callbacks
+ * Issue all pending post-COMMIT/ROLLBACK callbacks
+ * @param integer $type IDatabase::TRIGGER_* constant
* @return Exception|null The first exception or null if there were none
* @since 1.28
*/
- public function runMasterPostCommitCallbacks() {
+ public function runMasterPostTrxCallbacks( $type ) {
$e = null; // first exception
- $this->forEachOpenMasterConnection( function ( DatabaseBase $db ) use ( &$e ) {
- $db->setPostCommitCallbackSupression( false );
+ $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $type, &$e ) {
+ $conn->clearSnapshot( __METHOD__ ); // clear no-op transactions
+
+ $conn->setTrxEndCallbackSuppression( false );
+ try {
+ $conn->runOnTransactionIdleCallbacks( $type );
+ } catch ( Exception $ex ) {
+ $e = $e ?: $ex;
+ }
try {
- $db->runOnTransactionIdleCallbacks( IDatabase::TRIGGER_COMMIT );
+ $conn->runTransactionListenerCallbacks( $type );
} catch ( Exception $ex ) {
$e = $e ?: $ex;
}
* @since 1.23
*/
public function rollbackMasterChanges( $fname = __METHOD__ ) {
- $failedServers = [];
-
- $masterIndex = $this->getWriterIndex();
- foreach ( $this->mConns as $conns2 ) {
- if ( empty( $conns2[$masterIndex] ) ) {
- continue;
- }
- /** @var DatabaseBase $conn */
- foreach ( $conns2[$masterIndex] as $conn ) {
- if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
- try {
- $conn->rollback( $fname, IDatabase::FLUSHING_ALL_PEERS );
- } catch ( DBError $e ) {
- MWExceptionHandler::logException( $e );
- $failedServers[] = $conn->getServer();
- }
+ $restore = ( $this->trxRoundId !== false );
+ $this->trxRoundId = false;
+ $this->forEachOpenMasterConnection(
+ function ( DatabaseBase $conn ) use ( $fname, $restore ) {
+ if ( $conn->writesOrCallbacksPending() ) {
+ $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
+ }
+ if ( $restore ) {
+ $this->undoTransactionRoundFlags( $conn );
}
}
+ );
+ }
+
+ /**
+ * Suppress all pending post-COMMIT/ROLLBACK callbacks
+ * @return Exception|null The first exception or null if there were none
+ * @since 1.28
+ */
+ public function suppressTransactionEndCallbacks() {
+ $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+ $conn->setTrxEndCallbackSuppression( true );
+ } );
+ }
+
+ /**
+ * @param DatabaseBase $conn
+ */
+ private function applyTransactionRoundFlags( DatabaseBase $conn ) {
+ if ( $conn->getFlag( DBO_DEFAULT ) ) {
+ // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
+ // Force DBO_TRX even in CLI mode since a commit round is expected soon.
+ $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
+ // If config has explicitly requested DBO_TRX be either on or off by not
+ // setting DBO_DEFAULT, then respect that. Forcing no transactions is useful
+ // for things like blob stores (ExternalStore) which want auto-commit mode.
}
+ }
- if ( $failedServers ) {
- throw new DBExpectedError( null, "Rollback failed on server(s) " .
- implode( ', ', array_unique( $failedServers ) ) );
+ /**
+ * @param DatabaseBase $conn
+ */
+ private function undoTransactionRoundFlags( DatabaseBase $conn ) {
+ if ( $conn->getFlag( DBO_DEFAULT ) ) {
+ $conn->restoreFlags( $conn::RESTORE_PRIOR );
}
}
+ /**
+ * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+ *
+ * @param string $fname Caller name
+ * @since 1.28
+ */
+ public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+ $this->forEachOpenReplicaConnection( function ( DatabaseBase $conn ) {
+ $conn->clearSnapshot( __METHOD__ );
+ } );
+ }
+
/**
* @return bool Whether a master connection is already open
* @since 1.24
/**
* @note This method will trigger a DB connection if not yet done
- *
* @param string|bool $wiki Wiki ID, or false for the current wiki
* @return bool Whether the generic connection for reads is highly "lagged"
*/
- public function getLaggedSlaveMode( $wiki = false ) {
+ public function getLaggedReplicaMode( $wiki = false ) {
// No-op if there is only one DB (also avoids recursion)
- if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
+ if ( !$this->laggedReplicaMode && $this->getServerCount() > 1 ) {
try {
- // See if laggedSlaveMode gets set
- $conn = $this->getConnection( DB_SLAVE, false, $wiki );
+ // See if laggedReplicaMode gets set
+ $conn = $this->getConnection( DB_REPLICA, false, $wiki );
$this->reuseConnection( $conn );
} catch ( DBConnectionError $e ) {
// Avoid expensive re-connect attempts and failures
- $this->slavesDownMode = true;
- $this->laggedSlaveMode = true;
+ $this->allReplicasDownMode = true;
+ $this->laggedReplicaMode = true;
}
}
- return $this->laggedSlaveMode;
+ return $this->laggedReplicaMode;
+ }
+
+ /**
+ * @param bool $wiki
+ * @return bool
+ * @deprecated 1.28; use getLaggedReplicaMode()
+ */
+ public function getLaggedSlaveMode( $wiki = false ) {
+ return $this->getLaggedReplicaMode( $wiki );
}
/**
* @note This method will never cause a new DB connection
* @return bool Whether any generic connection used for reads was highly "lagged"
+ * @since 1.28
+ */
+ public function laggedReplicaUsed() {
+ return $this->laggedReplicaMode;
+ }
+
+ /**
+ * @return bool
* @since 1.27
+ * @deprecated Since 1.28; use laggedReplicaUsed()
*/
public function laggedSlaveUsed() {
- return $this->laggedSlaveMode;
+ return $this->laggedReplicaUsed();
}
/**
public function getReadOnlyReason( $wiki = false, DatabaseBase $conn = null ) {
if ( $this->readOnlyReason !== false ) {
return $this->readOnlyReason;
- } elseif ( $this->getLaggedSlaveMode( $wiki ) ) {
- if ( $this->slavesDownMode ) {
+ } elseif ( $this->getLaggedReplicaMode( $wiki ) ) {
+ if ( $this->allReplicasDownMode ) {
return 'The database has been automatically locked ' .
- 'until the slave database servers become available';
+ 'until the replica database servers become available';
} else {
return 'The database has been automatically locked ' .
- 'while the slave database servers catch up to the master.';
+ 'while the replica database servers catch up to the master.';
}
} elseif ( $this->masterRunningReadOnly( $wiki, $conn ) ) {
return 'The database master is running in read-only mode.';
}
/**
- * Get the hostname and lag time of the most-lagged slave
+ * Call a function with each open replica DB connection object
+ * @param callable $callback
+ * @param array $params
+ * @since 1.28
+ */
+ public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
+ foreach ( $this->mConns as $connsByServer ) {
+ foreach ( $connsByServer as $i => $serverConns ) {
+ if ( $i === $this->getWriterIndex() ) {
+ continue; // skip master
+ }
+ foreach ( $serverConns as $conn ) {
+ $mergedParams = array_merge( [ $conn ], $params );
+ call_user_func_array( $callback, $mergedParams );
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the hostname and lag time of the most-lagged replica DB
*
* This is useful for maintenance scripts that need to throttle their updates.
- * May attempt to open connections to slaves on the default DB. If there is
+ * 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
}
/**
- * Wait for a slave DB to reach a specified master position
+ * Wait for a replica DB to reach a specified master position
*
* This will connect to the master to get an accurate position if $pos is not given
*
- * @param IDatabase $conn Slave DB
+ * @param IDatabase $conn Replica DB
* @param DBMasterPos|bool $pos Master position; default: current position
* @param integer $timeout Timeout in seconds
* @return bool Success
* @since 1.27
*/
public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
- if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'slave' ) ) {
- return true; // server is not a slave DB
+ if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'replica' ) ) {
+ return true; // server is not a replica DB
}
$pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
public function clearLagTimeCache() {
$this->getLoadMonitor()->clearCaches();
}
+
+ /**
+ * Set a callback via DatabaseBase::setTransactionListener() on
+ * all current and future master connections of this load balancer
+ *
+ * @param string $name Callback name
+ * @param callable|null $callback
+ * @since 1.28
+ */
+ public function setTransactionListener( $name, callable $callback = null ) {
+ if ( $callback ) {
+ $this->trxRecurringCallbacks[$name] = $callback;
+ } else {
+ unset( $this->trxRecurringCallbacks[$name] );
+ }
+ $this->forEachOpenMasterConnection(
+ function ( DatabaseBase $conn ) use ( $name, $callback ) {
+ $conn->setTransactionListener( $name, $callback );
+ }
+ );
+ }
}