+ /**
+ * Mark a DB as in a transaction with one or more writes pending
+ *
+ * Note that there can be multiple connections to a single DB.
+ *
+ * @param string $server DB server
+ * @param string $db DB name
+ */
+ public function transactionWritingIn( $server, $db ) {
+ $name = "{$server} ({$db})";
+ if ( isset( $this->mDBTrxHoldingLocks[$name] ) ) {
+ ++$this->mDBTrxHoldingLocks[$name]['refs'];
+ } else {
+ $this->mDBTrxHoldingLocks[$name] = array( 'refs' => 1, 'start' => microtime( true ) );
+ $this->mDBTrxMethodTimes[$name] = array();
+ }
+ }
+
+ /**
+ * Register the name and time of a method for slow DB trx detection
+ *
+ * @param string $method Function name
+ * @param float $realtime Wal time ellapsed
+ */
+ protected function updateTrxProfiling( $method, $realtime ) {
+ if ( !$this->mDBTrxHoldingLocks ) {
+ return; // short-circuit
+ // @TODO: hardcoded check is a tad janky (what about FOR UPDATE?)
+ } elseif ( !preg_match( '/^query-m: (?!SELECT)/', $method )
+ && $realtime < $this->mDBLockThreshold )
+ {
+ return; // not a DB master query nor slow enough
+ }
+ $now = microtime( true );
+ foreach ( $this->mDBTrxHoldingLocks as $name => $info ) {
+ // Hacky check to exclude entries from before the first TRX write
+ if ( ( $now - $realtime ) >= $info['start'] ) {
+ $this->mDBTrxMethodTimes[$name][] = array( $method, $realtime );
+ }
+ }
+ }
+
+ /**
+ * Mark a DB as no longer in a transaction
+ *
+ * This will check if locks are possibly held for longer than
+ * needed and log any affected transactions to a special DB log.
+ * Note that there can be multiple connections to a single DB.
+ *
+ * @param string $server DB server
+ * @param string $db DB name
+ */
+ public function transactionWritingOut( $server, $db ) {
+ $name = "{$server} ({$db})";
+ if ( --$this->mDBTrxHoldingLocks[$name]['refs'] <= 0 ) {
+ $slow = false;
+ foreach ( $this->mDBTrxMethodTimes[$name] as $info ) {
+ list( $method, $realtime ) = $info;
+ if ( $realtime >= $this->mDBLockThreshold ) {
+ $slow = true;
+ break;
+ }
+ }
+ if ( $slow ) {
+ $dbs = implode( ', ', array_keys( $this->mDBTrxHoldingLocks ) );
+ $msg = "Sub-optimal transaction on DB(s) {$dbs}:\n";
+ foreach ( $this->mDBTrxMethodTimes[$name] as $i => $info ) {
+ list( $method, $realtime ) = $info;
+ $msg .= sprintf( "%d\t%.6f\t%s\n", $i, $realtime, $method );
+ }
+ wfDebugLog( 'DBPerformance', $msg );
+ }
+ unset( $this->mDBTrxHoldingLocks[$name] );
+ unset( $this->mDBTrxMethodTimes[$name] );
+ }
+ }
+