* @ingroup Database
*/
+use Psr\Log\LoggerInterface;
+use MediaWiki\Logger\LoggerFactory;
+
/**
* An interface for generating database load balancers
* @ingroup Database
/** @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 array $args
*/
private function forEachLBCallMethod( $methodName, array $args = array() ) {
- $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
- call_user_func_array( array( $loadBalancer, $methodName ), $args );
- }, array( $methodName, $args ) );
+ $this->forEachLB(
+ function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
+ call_user_func_array( array( $loadBalancer, $methodName ), $args );
+ },
+ array( $methodName, $args )
+ );
}
/**
* 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
*/
- public function commitAll() {
- $this->forEachLBCallMethod( 'commitAll' );
+ public function commitAll( $fname = __METHOD__ ) {
+ $this->logMultiDbTransaction();
+
+ $start = microtime( true );
+ $this->forEachLBCallMethod( 'commitAll', array( $fname ) );
+ $timeMs = 1000 * ( microtime( true ) - $start );
+
+ RequestContext::getMain()->getStats()->timing( "db.commit-all", $timeMs );
}
/**
* Commit changes on all master connections
+ * @param string $fname Caller name
*/
- public function commitMasterChanges() {
+ public function commitMasterChanges( $fname = __METHOD__ ) {
+ $this->logMultiDbTransaction();
+
$start = microtime( true );
- $this->forEachLBCallMethod( 'commitMasterChanges' );
+ $this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
$timeMs = 1000 * ( microtime( true ) - $start );
+
RequestContext::getMain()->getStats()->timing( "db.commit-masters", $timeMs );
}
/**
* Rollback changes on all master connections
+ * @param string $fname Caller name
* @since 1.23
*/
- public function rollbackMasterChanges() {
- $this->forEachLBCallMethod( 'rollbackMasterChanges' );
+ public function rollbackMasterChanges( $fname = __METHOD__ ) {
+ $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 );
+ }
}
/**