return $this->__call( __FUNCTION__, func_get_args() );
}
+ public function pendingWriteCallers() {
+ return $this->__call( __FUNCTION__, func_get_args() );
+ }
+
public function isOpen() {
return $this->__call( __FUNCTION__, func_get_args() );
}
*/
private $mTrxAutomaticAtomic = false;
+ /**
+ * Track the write query callers of the current transaction
+ *
+ * @var string[]
+ */
+ private $mTrxWriteCallers = array();
+
/**
* Track the seconds spent in write queries for the current transaction
*
return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
}
+ public function pendingWriteCallers() {
+ return $this->mTrxLevel ? $this->mTrxWriteCallers : array();
+ }
+
public function isOpen() {
return $this->mOpened;
}
if ( $isWriteQuery && $this->mTrxLevel ) {
$this->mTrxWriteDuration += $queryRuntime;
+ $this->mTrxWriteCallers[] = $fname;
}
return $res;
$this->mTrxPreCommitCallbacks = array();
$this->mTrxShortId = wfRandomString( 12 );
$this->mTrxWriteDuration = 0.0;
+ $this->mTrxWriteCallers = array();
// First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
// Get an estimate of the slave lag before then, treating estimate staleness
// as lag itself just to be safe
public function writesOrCallbacksPending();
/**
- * Get the time spend running write queries for this
+ * Get the time spend running write queries for this transaction
*
* High times could be due to scanning, updates, locking, and such
*
*/
public function pendingWriteQueryDuration();
+ /**
+ * Get the list of method names that did write queries for this transaction
+ *
+ * @return array
+ * @since 1.27
+ */
+ public function pendingWriteCallers();
+
/**
* Is a connection to the database open?
* @return bool
* @ingroup Database
*/
+use Psr\Log\LoggerInterface;
+use MediaWiki\Logger\LoggerFactory;
+
/**
* An interface for generating database load balancers
* @ingroup Database
abstract class LBFactory {
/** @var ChronologyProtector */
protected $chronProt;
+
/** @var TransactionProfiler */
protected $trxProfiler;
+ /** @var LoggerInterface */
+ protected $logger;
+
/** @var LBFactory */
private static $instance;
$this->chronProt = $this->newChronologyProtector();
$this->trxProfiler = Profiler::instance()->getTransactionProfiler();
+ $this->logger = LoggerFactory::getInstance( 'DBTransaction' );
}
/**
* @param string $fname Caller name
*/
public function commitAll( $fname = __METHOD__ ) {
+ $this->logMultiDbTransaction();
+
$start = microtime( true );
$this->forEachLBCallMethod( 'commitAll', array( $fname ) );
$timeMs = 1000 * ( microtime( true ) - $start );
* @param string $fname Caller name
*/
public function commitMasterChanges( $fname = __METHOD__ ) {
+ $this->logMultiDbTransaction();
+
$start = microtime( true );
$this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
$timeMs = 1000 * ( microtime( true ) - $start );
$this->forEachLBCallMethod( 'rollbackMasterChanges', array( $fname ) );
}
+ /**
+ * Log query info if multi DB transactions are going to be committed now
+ */
+ private function logMultiDbTransaction() {
+ $callersByDB = array();
+ $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
+ $masterName = $lb->getServerName( $lb->getWriterIndex() );
+ $callers = $lb->pendingMasterChangeCallers();
+ if ( $callers ) {
+ $callersByDB[$masterName] = $callers;
+ }
+ } );
+
+ if ( count( $callersByDB ) >= 2 ) {
+ $dbs = implode( ', ', array_keys( $callersByDB ) );
+ $msg = "Multi-DB transaction [{$dbs}]:\n";
+ foreach ( $callersByDB as $db => $callers ) {
+ $msg .= "$db: " . implode( '; ', $callers ) . "\n";
+ }
+ $this->logger->info( $msg );
+ }
+ }
+
/**
* Determine if any master connection has pending changes
* @return bool
|| $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
}
+ /**
+ * Get the list of callers that have pending master changes
+ *
+ * @return array
+ * @since 1.27
+ */
+ public function pendingMasterChangeCallers() {
+ $fnames = array();
+
+ $masterIndex = $this->getWriterIndex();
+ foreach ( $this->mConns as $conns2 ) {
+ if ( empty( $conns2[$masterIndex] ) ) {
+ continue;
+ }
+ /** @var DatabaseBase $conn */
+ foreach ( $conns2[$masterIndex] as $conn ) {
+ $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
+ }
+ }
+
+ return $fnames;
+ }
+
/**
* @param mixed $value
* @return mixed