rdbms: Introduce TransactionManager class to move out the logic

This would make Database class smaller and encapsulates the transaction
logic making it easier to understand and change.

Bug: T299698
Change-Id: I409a474209ab4b714c5f62e5e7c0b7a62b9e82c1
This commit is contained in:
Amir Sarabadani 2022-01-20 21:29:32 +01:00 committed by Krinkle
parent c51065fb99
commit 83adf1eb88
3 changed files with 99 additions and 27 deletions

View file

@ -1861,6 +1861,7 @@ $wgAutoloadLocalClasses = [
'Wikimedia\\Rdbms\\Subquery' => __DIR__ . '/includes/libs/rdbms/encasing/Subquery.php',
'Wikimedia\\Rdbms\\TimestampType' => __DIR__ . '/includes/libs/rdbms/dbal/TimestampType.php',
'Wikimedia\\Rdbms\\TinyIntType' => __DIR__ . '/includes/libs/rdbms/dbal/TinyIntType.php',
'Wikimedia\\Rdbms\\TransactionManager' => __DIR__ . '/includes/libs/rdbms/database/TransactionManager.php',
'Wikimedia\\Rdbms\\TransactionProfiler' => __DIR__ . '/includes/libs/rdbms/TransactionProfiler.php',
'Wikimedia\\Reflection\\GhostFieldAccessTrait' => __DIR__ . '/includes/libs/GhostFieldAccessTrait.php',
'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',

View file

@ -68,6 +68,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
protected $profiler;
/** @var TransactionProfiler */
protected $trxProfiler;
/** @var TransactionManager */
private $transactionManager;
/** @var DatabaseDomain */
protected $currentDomain;
@ -127,16 +129,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
/** @var array Map of (table name => 1) for current TEMPORARY tables */
protected $sessionDirtyTempTables = [];
/** @var string Application-side ID of the active transaction or an empty string otherwise */
private $trxId = '';
/** @var int Transaction status */
private $trxStatus = self::STATUS_TRX_NONE;
/** @var Throwable|null The last error that caused the status to become STATUS_TRX_ERROR */
private $trxStatusCause;
/** @var array|null Error details of the last statement-only rollback */
private $trxStatusIgnoredCause;
/** @var float|null UNIX timestamp at the time of BEGIN for the last transaction */
private $trxTimestamp = null;
/** @var array|null Replication lag estimate at the time of BEGIN for the last transaction */
private $trxReplicaLagStatus = null;
/** @var string|null Name of the function that start the last transaction */
@ -282,6 +280,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* @param array $params Parameters passed from Database::factory()
*/
public function __construct( array $params ) {
$this->transactionManager = new TransactionManager();
$this->connectionParams = [
self::CONN_HOST => ( isset( $params['host'] ) && $params['host'] !== '' )
? $params['host']
@ -592,11 +591,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
}
final public function trxLevel() {
return ( $this->trxId != '' ) ? 1 : 0;
// FIXME: A lot of tests disable constructor leading to trx manager being
// null and breaking, this is unacceptable but hopefully this should
// happen less by moving these functions to the transaction manager class.
if ( !$this->transactionManager ) {
$this->transactionManager = new TransactionManager();
}
return $this->transactionManager->trxLevel();
}
public function trxTimestamp() {
return $this->trxLevel() ? $this->trxTimestamp : null;
return $this->transactionManager->trxTimestamp();
}
/**
@ -1441,7 +1446,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$this->trxProfiler->transactionWritingIn(
$this->getServerName(),
$this->getDomainID(),
$this->trxId
$this->transactionManager->getTrxId()
);
}
}
@ -1497,7 +1502,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$startTime,
$isPermWrite,
$isPermWrite ? $this->affectedRows() : $numRows,
$this->trxId,
$this->transactionManager->getTrxId(),
$this->getServerName()
);
@ -1682,7 +1687,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
// https://www.postgresql.org/docs/9.4/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
$this->sessionNamedLocks = [];
// Session loss implies transaction loss
$oldTrxId = $this->consumeTrxId();
$oldTrxId = $this->transactionManager->consumeTrxId();
$this->trxAtomicCounter = 0;
$this->trxPostCommitOrIdleCallbacks = []; // T67263; transaction already lost
$this->trxPreCommitOrIdleCallbacks = []; // T67263; transaction already lost
@ -1719,18 +1724,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
}
/**
* Reset the application-side transaction ID and return the old one
*
* @return string The old transaction ID or an empty string if there wasn't one
*/
private function consumeTrxId() {
$old = $this->trxId;
$this->trxId = '';
return $old;
}
/**
* Checks whether the cause of the error is detected to be a timeout.
*
@ -4901,13 +4894,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$this->completeCriticalSection( __METHOD__, $cs );
throw $e;
}
static $nextTrxId;
$nextTrxId = ( $nextTrxId !== null ? $nextTrxId++ : mt_rand() ) % 0xffff;
$this->trxId = sprintf( '%06x', mt_rand( 0, 0xffffff ) ) . sprintf( '%04x', $nextTrxId );
$this->transactionManager->newTrxId();
$this->trxStatus = self::STATUS_TRX_OK;
$this->trxStatusIgnoredCause = null;
$this->trxAtomicCounter = 0;
$this->trxTimestamp = microtime( true );
$this->transactionManager->setTrxTimestamp( microtime( true ) );
$this->trxFname = $fname;
$this->trxDoneWrites = false;
$this->trxAutomaticAtomic = false;
@ -4994,7 +4985,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$this->completeCriticalSection( __METHOD__, $cs );
throw $e;
}
$oldTrxId = $this->consumeTrxId();
$oldTrxId = $this->transactionManager->consumeTrxId();
$this->trxStatus = self::STATUS_TRX_NONE;
if ( $this->trxDoneWrites ) {
$this->lastWriteTime = microtime( true );
@ -5053,7 +5044,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
$cs = $this->commenceCriticalSection( __METHOD__ );
$this->doRollback( $fname );
$oldTrxId = $this->consumeTrxId();
$oldTrxId = $this->transactionManager->consumeTrxId();
$this->trxStatus = self::STATUS_TRX_NONE;
$this->trxAtomicLevels = [];
// Clear callbacks that depend on transaction or transaction round commit

View file

@ -0,0 +1,80 @@
<?php
/**
* This file deals with database interface functions
* and query specifics/optimisations.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms;
/**
* @ingroup Database
* @internal
*/
class TransactionManager {
/** @var string Application-side ID of the active transaction or an empty string otherwise */
private $trxId = '';
/** @var float|null UNIX timestamp at the time of BEGIN for the last transaction */
private $trxTimestamp = null;
public function trxLevel() {
return ( $this->trxId != '' ) ? 1 : 0;
}
/**
* TODO: This should be removed once all usages have been migrated here
* @return string
*/
public function getTrxId(): string {
return $this->trxId;
}
/**
* TODO: This should be removed once all usages have been migrated here
*/
public function newTrxId() {
static $nextTrxId;
$nextTrxId = ( $nextTrxId !== null ? $nextTrxId++ : mt_rand() ) % 0xffff;
$this->trxId = sprintf( '%06x', mt_rand( 0, 0xffffff ) ) . sprintf( '%04x', $nextTrxId );
}
/**
* Reset the application-side transaction ID and return the old one
* This will become private soon.
* @return string The old transaction ID or an empty string if there wasn't one
*/
public function consumeTrxId() {
$old = $this->trxId;
$this->trxId = '';
return $old;
}
public function trxTimestamp(): ?float {
return $this->trxLevel() ? $this->trxTimestamp : null;
}
/**
* @param float|null $trxTimestamp
* @unstable This will be removed once usages are migrated here
*/
public function setTrxTimestamp( ?float $trxTimestamp ) {
$this->trxTimestamp = $trxTimestamp;
}
}