Aaron Schulz has uploaded a new change for review.
https://gerrit.wikimedia.org/r/304687
Change subject: Make transaction enforcement stricter
......................................................................
Make transaction enforcement stricter
* Nested begin() will fail for DBO_TRX transactions too,
even if no writes happened yet. This avoids snapshot
less from REPEATABLE-READ or SSI SERIALABLE (postgres).
* Warn when begin() happens and DBO_TRX is set. This is
not meant to happen as it can make multi-DB commits
less robust. Callers should use LBFactory methods
for doing commit/rollback instead.
Change-Id: I31b8b1b112cf9110b805a023732bce4dcff0604c
---
M includes/db/DBConnRef.php
M includes/db/Database.php
M includes/db/DatabasePostgres.php
M includes/db/IDatabase.php
4 files changed, 23 insertions(+), 42 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/87/304687/1
diff --git a/includes/db/DBConnRef.php b/includes/db/DBConnRef.php
index 53862b9..2ad5e8c 100644
--- a/includes/db/DBConnRef.php
+++ b/includes/db/DBConnRef.php
@@ -445,7 +445,7 @@
return $this->__call( __FUNCTION__, func_get_args() );
}
- public function begin( $fname = __METHOD__ ) {
+ public function begin( $fname = __METHOD__, $mode = '' ) {
return $this->__call( __FUNCTION__, func_get_args() );
}
diff --git a/includes/db/Database.php b/includes/db/Database.php
index 3ea2cda..e01ecb9 100644
--- a/includes/db/Database.php
+++ b/includes/db/Database.php
@@ -815,7 +815,7 @@
if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX )
&& $this->isTransactableQuery( $sql )
) {
- $this->begin( __METHOD__ . " ($fname)" );
+ $this->begin( __METHOD__ . " ($fname)",
self::TRANSACTION_INTERNAL );
$this->mTrxAutomatic = true;
}
@@ -2203,7 +2203,7 @@
$useTrx = !$this->mTrxLevel;
if ( $useTrx ) {
- $this->begin( $fname );
+ $this->begin( $fname, self::TRANSACTION_INTERNAL );
}
try {
# Update any existing conflicting row(s)
@@ -2221,7 +2221,7 @@
throw $e;
}
if ( $useTrx ) {
- $this->commit( $fname );
+ $this->commit( $fname, self::TRANSACTION_INTERNAL );
}
return $ok;
@@ -2520,7 +2520,7 @@
$this->mTrxPreCommitCallbacks[] = [ $callback,
wfGetCaller() ];
} else {
// If no transaction is active, then make one for this
callback
- $this->begin( __METHOD__ );
+ $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
try {
call_user_func( $callback );
$this->commit( __METHOD__ );
@@ -2628,7 +2628,7 @@
final public function startAtomic( $fname = __METHOD__ ) {
if ( !$this->mTrxLevel ) {
- $this->begin( $fname );
+ $this->begin( $fname, self::TRANSACTION_INTERNAL );
$this->mTrxAutomatic = true;
// If DBO_TRX is set, a series of startAtomic/endAtomic
pairs will result
// in all changes being in one transaction to keep
requests transactional.
@@ -2666,43 +2666,21 @@
$this->endAtomic( $fname );
}
- final public function begin( $fname = __METHOD__ ) {
- if ( $this->mTrxLevel ) { // implicit commit
+ final public function begin( $fname = __METHOD__, $mode = '' ) {
+ if ( $this->mTrxLevel ) {
+ // Protect against mismatched atomic section,
transaction nesting, and snapshot loss
if ( $this->mTrxAtomicLevels ) {
- // If the current transaction was an automatic
atomic one, then we definitely have
- // a problem. Same if there is any unclosed
atomic level.
$levels = implode( ', ',
$this->mTrxAtomicLevels );
- throw new DBUnexpectedError(
- $this,
- "Got explicit BEGIN from $fname while
atomic section(s) $levels are open."
- );
+ $msg = "Got explicit BEGIN from $fname while
atomic section(s) $levels are open.";
} elseif ( !$this->mTrxAutomatic ) {
- // We want to warn about inadvertently nested
begin/commit pairs, but not about
- // auto-committing implicit transactions that
were started by query() via DBO_TRX
- throw new DBUnexpectedError(
- $this,
- "$fname: Transaction already in
progress (from {$this->mTrxFname}), " .
- " performing implicit commit!"
- );
- } elseif ( $this->mTrxDoneWrites ) {
- // The transaction was automatic and has done
write operations
- throw new DBUnexpectedError(
- $this,
- "$fname: Automatic transaction with
writes in progress" .
- " (from {$this->mTrxFname}),
performing implicit commit!\n"
- );
+ $msg = "$fname: Explicit transaction already
active (from {$this->mTrxFname}).";
+ } else {
+ $msg = "$fname: Implicit transaction already
active (from {$this->mTrxFname}).";
}
-
- $this->runOnTransactionPreCommitCallbacks();
- $writeTime = $this->pendingWriteQueryDuration();
- $this->doCommit( $fname );
- if ( $this->mTrxDoneWrites ) {
- $this->mDoneWrites = microtime( true );
-
$this->getTransactionProfiler()->transactionWritingOut(
- $this->mServer, $this->mDBname,
$this->mTrxShortId, $writeTime );
- }
-
- $this->runOnTransactionIdleCallbacks(
self::TRIGGER_COMMIT );
+ throw new DBUnexpectedError( $this, $msg );
+ } elseif ( $this->getFlag( DBO_TRX ) && $mode !==
self::TRANSACTION_INTERNAL ) {
+ // @TODO: make this an exception for multi-DB commit
safety
+ wfLogDBError( "$fname: Implicit transaction expected
(DBO_TRX set)." );
}
// Avoid fatals if close() was called
diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php
index 867aeb8..1ecdd2608 100644
--- a/includes/db/DatabasePostgres.php
+++ b/includes/db/DatabasePostgres.php
@@ -149,7 +149,7 @@
$this->didbegin = false;
/* If we are not in a transaction, we need to be for savepoint
trickery */
if ( !$dbw->trxLevel() ) {
- $dbw->begin( "FOR SAVEPOINT" );
+ $dbw->begin( "FOR SAVEPOINT",
DatabasePostgres::TRANSACTION_INTERNAL );
$this->didbegin = true;
}
}
@@ -1207,7 +1207,7 @@
* @param string $desiredSchema
*/
function determineCoreSchema( $desiredSchema ) {
- $this->begin( __METHOD__ );
+ $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
if ( $this->schemaExists( $desiredSchema ) ) {
if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
$this->mCoreSchema = $desiredSchema;
diff --git a/includes/db/IDatabase.php b/includes/db/IDatabase.php
index af024b8..5bbd151 100644
--- a/includes/db/IDatabase.php
+++ b/includes/db/IDatabase.php
@@ -40,6 +40,8 @@
/** @var int Callback triggered by rollback */
const TRIGGER_ROLLBACK = 3;
+ /** @var string Transaction is started interally via
DBO_TRX/startAtomic() */
+ const TRANSACTION_INTERNAL = 'implicit';
/** @var string Transaction operation comes from service managing all
DBs */
const FLUSHING_ALL_PEERS = 'flush';
/** @var string Transaction operation comes from the database class
internally */
@@ -1362,9 +1364,10 @@
* automatically because of the DBO_TRX flag.
*
* @param string $fname
+ * @param string $mode A situationally valid IDatabase::TRANSACTION_*
constant
* @throws DBError
*/
- public function begin( $fname = __METHOD__ );
+ public function begin( $fname = __METHOD__, $mode = '' );
/**
* Commits a transaction previously started using begin().
--
To view, visit https://gerrit.wikimedia.org/r/304687
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I31b8b1b112cf9110b805a023732bce4dcff0604c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits