* @deprecated since 1.27, use LBFactory::destroy()
*/
public static function destroyInstance() {
- self::singleton()->destroy();
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
}
/**
* @param bool|string $wiki Wiki ID, or false for the current wiki
* @return LoadBalancer
*/
- abstract public function &getExternalLB( $cluster, $wiki = false );
+ abstract public function getExternalLB( $cluster, $wiki = false );
/**
* Execute a function for each tracked load balancer
);
}
+ /**
+ * 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->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+ }
+
+ /**
+ * Commit on all connections. Done for two reasons:
+ * 1. To commit changes to the masters.
+ * 2. To release the snapshot on all connections, master and replica DB.
+ * @param string $fname Caller name
+ * @param array $options Options map:
+ * - maxWriteDuration: abort if more than this much time was spent in write queries
+ */
+ public function commitAll( $fname = __METHOD__, array $options = [] ) {
+ $this->commitMasterChanges( $fname, $options );
+ $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
+ }
+
/**
* Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
*
* - commitMasterChanges()
* - rollbackMasterChanges()
* - commitAll()
+ *
* This allows for custom transaction rounds from any outer transaction scope.
*
* @param string $fname
if ( $this->trxRoundId !== false ) {
throw new DBTransactionError(
null,
- "Transaction round '{$this->trxRoundId}' already started."
+ "$fname: transaction round '{$this->trxRoundId}' already started."
);
}
$this->trxRoundId = $fname;
$this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
}
- /**
- * 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->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
- }
-
- /**
- * Commit on all connections. Done for two reasons:
- * 1. To commit changes to the masters.
- * 2. To release the snapshot on all connections, master and slave.
- * @param string $fname Caller name
- * @param array $options Options map:
- * - maxWriteDuration: abort if more than this much time was spent in write queries
- */
- public function commitAll( $fname = __METHOD__, array $options = [] ) {
- $this->commitMasterChanges( $fname, $options );
- $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
- }
-
/**
* Commit changes on all master connections
* @param string $fname Caller name
* @throws Exception
*/
public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
+ if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
+ throw new DBTransactionError(
+ null,
+ "$fname: transaction round '{$this->trxRoundId}' still running."
+ );
+ }
// Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
$this->forEachLBCallMethod( 'finalizeMasterChanges' );
$this->trxRoundId = false;
}
/**
- * Detemine if any lagged slave connection was used
- * @since 1.27
+ * Detemine if any lagged replica DB connection was used
* @return bool
+ * @since 1.28
*/
- public function laggedSlaveUsed() {
+ public function laggedReplicaUsed() {
$ret = false;
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
- $ret = $ret || $lb->laggedSlaveUsed();
+ $ret = $ret || $lb->laggedReplicaUsed();
} );
return $ret;
}
+ /**
+ * @return bool
+ * @since 1.27
+ * @deprecated Since 1.28; use laggedReplicaUsed()
+ */
+ public function laggedSlaveUsed() {
+ return $this->laggedReplicaUsed();
+ }
+
/**
* Determine if any master connection has pending/written changes from this request
* @return bool
}
/**
- * Waits for the slave DBs to catch up to the current master position
+ * Waits for the replica DBs to catch up to the current master position
*
* Use this when updating very large numbers of rows, as in maintenance scripts,
- * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
*
* By default this waits on all DB clusters actually used in this request.
* This makes sense when lag being waiting on is caused by the code that does this check.
$masterPositions = array_fill( 0, count( $lbs ), false );
foreach ( $lbs as $i => $lb ) {
if ( $lb->getServerCount() <= 1 ) {
- // Bug 27975 - Don't try to wait for slaves if there are none
+ // Bug 27975 - Don't try to wait for replica DBs if there are none
// Prevents permission error when getting master position
continue;
} elseif ( $opts['ifWritesSince']
if ( $failed ) {
throw new DBReplicationWaitError(
- "Could not wait for slaves to catch up to " .
+ "Could not wait for replica DBs to catch up to " .
implode( ', ', $failed )
);
}
* This will commit and wait unless $ticket indicates it is unsafe to do so
*
* @param string $fname Caller name (e.g. __METHOD__)
- * @param mixed $ticket Result of getOuterTransactionScopeTicket()
+ * @param mixed $ticket Result of getEmptyTransactionTicket()
* @param array $opts Options to waitForReplication()
* @throws DBReplicationWaitError
* @since 1.28
return;
}
- $this->commitMasterChanges( $fname );
+ // The transaction owner and any caller with the empty transaction ticket can commit
+ // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
+ if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
+ $this->trxLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
+ $fnameEffective = $this->trxRoundId;
+ } else {
+ $fnameEffective = $fname;
+ }
+
+ $this->commitMasterChanges( $fnameEffective );
$this->waitForReplication( $opts );
+ // If a nested caller committed on behalf of $fname, start another empty $fname
+ // transaction, leaving the caller with the same empty transaction state as before.
+ if ( $fnameEffective !== $fname ) {
+ $this->beginMasterChanges( $fnameEffective );
+ }
+ }
+
+ /**
+ * @param string $dbName DB master name (e.g. "db1052")
+ * @return float|bool UNIX timestamp when client last touched the DB or false if not recent
+ * @since 1.28
+ */
+ public function getChronologyProtectorTouched( $dbName ) {
+ return $this->chronProt->getTouched( $dbName );
}
/**
// Write them to the stash
$unsavedPositions = $cp->shutdown();
// If the positions failed to write to the stash, at least wait on local datacenter
- // slaves to catch up before responding. Even if there are several DCs, this increases
+ // replica DBs to catch up before responding. Even if there are several DCs, this increases
// the chance that the user will see their own changes immediately afterwards. As long
// as the sticky DC cookie applies (same domain), this is not even an issue.
$this->forEachLB( function ( LoadBalancer $lb ) use ( $unsavedPositions ) {
} );
}
+ /**
+ * Base parameters to LoadBalancer::__construct()
+ */
+ final protected function baseLoadBalancerParams() {
+ return [
+ 'readOnlyReason' => $this->readOnlyReason,
+ 'trxProfiler' => $this->trxProfiler,
+ 'srvCache' => $this->srvCache,
+ 'wanCache' => $this->wanCache,
+ 'localDomain' => wfWikiID(),
+ 'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
+ ];
+ }
+
/**
* @param LoadBalancer $lb
*/