jenkins-bot has submitted this change and it was merged.

Change subject: Added DatabaseBase::startAtomic and endAtomic
......................................................................


Added DatabaseBase::startAtomic and endAtomic

Added new functions to ensure certain groups of statements
are atomic without having to go through the trouble of
starting a new transaction if one has already been opened.

Change-Id: I5328fb337e5544bf28ea282860ef8f81e19ac43c
---
M includes/db/Database.php
1 file changed, 85 insertions(+), 1 deletion(-)

Approvals:
  Aaron Schulz: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/db/Database.php b/includes/db/Database.php
index c3850b9..ee17904 100644
--- a/includes/db/Database.php
+++ b/includes/db/Database.php
@@ -282,6 +282,20 @@
        private $mTrxAutomatic = false;
 
        /**
+        * Array of levels of atomicity within transactions
+        *
+        * @var SplStack
+        */
+       private $mTrxAtomicLevels;
+
+       /**
+        * Record if the current transaction was started implicitly by 
DatabaseBase::startAtomic
+        *
+        * @var Bool
+        */
+       private $mTrxAutomaticAtomic = false;
+
+       /**
         * @since 1.21
         * @var file handle for upgrade
         */
@@ -687,6 +701,7 @@
        ) {
                global $wgDBprefix, $wgCommandLineMode, $wgDebugDBTransactions;
 
+               $this->mTrxAtomicLevels = new SplStack;
                $this->mFlags = $flags;
 
                if ( $this->mFlags & DBO_DEFAULT ) {
@@ -3185,6 +3200,39 @@
        }
 
        /**
+        * Begin an atomic section of statements
+        *
+        * If a transaction has been started already, just keep track of the 
given
+        * section name to make sure the transaction is not committed 
pre-maturely.
+        * This function can be used in layers (with sub-sections), so use a 
stack
+        * to keep track of the different atomic sections. If there is no 
transaction,
+        * start one implicitly.
+        *
+        * The goal of this function is to create an atomic section of SQL 
queries
+        * without having to start a new transaction if it already exists.
+        *
+        * Atomic sections are more strict than transactions. With transactions,
+        * attempting to begin a new transaction when one is already running 
results
+        * in MediaWiki issuing a brief warning and doing an implicit commit. 
All
+        * atomic levels *must* be explicitly closed using 
DatabaseBase::endAtomic(),
+        * and any database transactions cannot be began or committed until all 
atomic
+        * levels are closed. There is no such thing as implicitly opening or 
closing
+        * an atomic section.
+        *
+        * @since 1.23
+        * @param string $fname
+        */
+       final public function startAtomic( $fname = __METHOD__ ) {
+               if ( !$this->mTrxLevel ) {
+                       $this->begin( $fname );
+                       $this->mTrxAutomatic = true;
+                       $this->mTrxAutomaticAtomic = true;
+               }
+
+               $this->mTrxAtomicLevels->push( $fname );
+       }
+
+       /**
         * Begin a transaction. If a transaction is already in progress, that 
transaction will be committed before the
         * new transaction is started.
         *
@@ -3200,7 +3248,13 @@
                global $wgDebugDBTransactions;
 
                if ( $this->mTrxLevel ) { // implicit commit
-                       if ( !$this->mTrxAutomatic ) {
+                       if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+                               // If the current transaction was an automatic 
atomic one, then we definitely have
+                               // a problem. Same if there is any unclosed 
atomic level.
+                               throw new DBUnexpectedError( $this,
+                                       "Attempted to start explicit 
transaction when atomic levels are still 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
                                $msg = "$fname: Transaction already in progress 
(from {$this->mTrxFname}), " .
@@ -3229,6 +3283,8 @@
                $this->mTrxFname = $fname;
                $this->mTrxDoneWrites = false;
                $this->mTrxAutomatic = false;
+               $this->mTrxAutomaticAtomic = false;
+               $this->mTrxAtomicLevels = new SplStack;
        }
 
        /**
@@ -3240,6 +3296,28 @@
        protected function doBegin( $fname ) {
                $this->query( 'BEGIN', $fname );
                $this->mTrxLevel = 1;
+       }
+
+       /**
+        * Ends an atomic section of SQL statements
+        *
+        * Ends the next section of atomic SQL statements and commits the 
transaction
+        * if necessary.
+        *
+        * @since 1.23
+        * @see DatabaseBase::startAtomic
+        * @param string $fname
+        */
+       final public function endAtomic( $fname = __METHOD__ ) {
+               if ( $this->mTrxAtomicLevels->isEmpty() ||
+                       $this->mTrxAtomicLevels->pop() !== $fname
+               ) {
+                       throw new DBUnexpectedError( $this, 'Invalid atomic 
section ended.' );
+               }
+
+               if ( $this->mTrxAtomicLevels->isEmpty() && 
$this->mTrxAutomaticAtomic ) {
+                       $this->commit( $fname, 'flush' );
+               }
        }
 
        /**
@@ -3255,6 +3333,11 @@
         *        that it is safe to ignore these warnings in your context.
         */
        final public function commit( $fname = __METHOD__, $flush = '' ) {
+               if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+                       // There are still atomic sections open. This cannot be 
ignored
+                       throw new DBUnexpectedError( $this, "Attempted to 
commit transaction while atomic sections are still open" );
+               }
+
                if ( $flush != 'flush' ) {
                        if ( !$this->mTrxLevel ) {
                                wfWarn( "$fname: No transaction to commit, 
something got out of sync!" );
@@ -3305,6 +3388,7 @@
                $this->doRollback( $fname );
                $this->mTrxIdleCallbacks = array(); // cancel
                $this->mTrxPreCommitCallbacks = array(); // cancel
+               $this->mTrxAtomicLevels = new SplStack;
                if ( $this->mTrxDoneWrites ) {
                        Profiler::instance()->transactionWritingOut( 
$this->mServer, $this->mDBname );
                }

-- 
To view, visit https://gerrit.wikimedia.org/r/90856
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5328fb337e5544bf28ea282860ef8f81e19ac43c
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Parent5446 <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Parent5446 <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to