Tim Starling has submitted this change and it was merged.

Change subject: [Database] Added onTransactionPreCommitOrIdle() function.
......................................................................


[Database] Added onTransactionPreCommitOrIdle() function.

* Also fixed case where onTransactionIdle() could start uncommitted
  transactions if there was no transanction and DBO_TRX was set.

Change-Id: I0bf6171fac692cf3d6e04011321bed075f58724b
---
M includes/db/Database.php
1 file changed, 60 insertions(+), 15 deletions(-)

Approvals:
  Tim Starling: Verified; Looks good to me, approved



diff --git a/includes/db/Database.php b/includes/db/Database.php
index 2142232..f4d194d 100644
--- a/includes/db/Database.php
+++ b/includes/db/Database.php
@@ -230,11 +230,10 @@
        protected $mConn = null;
        protected $mOpened = false;
 
-       /**
-        * @since 1.20
-        * @var array of Closure
-        */
+       /** @var array of Closure */
        protected $mTrxIdleCallbacks = array();
+       /** @var array of Closure */
+       protected $mTrxPreCommitCallbacks = array();
 
        protected $mTablePrefix;
        protected $mFlags;
@@ -553,12 +552,14 @@
 
        /**
         * Returns true if there is a transaction open with possible write
-        * queries or transaction idle callbacks waiting on it to finish.
+        * queries or transaction pre-commit/idle callbacks waiting on it to 
finish.
         *
         * @return bool
         */
        public function writesOrCallbacksPending() {
-               return $this->mTrxLevel && ( $this->mTrxDoneWrites || 
$this->mTrxIdleCallbacks );
+               return $this->mTrxLevel && (
+                       $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || 
$this->mTrxPreCommitCallbacks
+               );
        }
 
        /**
@@ -964,6 +965,7 @@
                        # Transaction is gone, like it or not
                        $this->mTrxLevel = 0;
                        $this->mTrxIdleCallbacks = array(); // cancel
+                       $this->mTrxPreCommitCallbacks = array(); // cancel
                        wfDebug( "Connection lost, reconnecting...\n" );
 
                        if ( $this->ping() ) {
@@ -2967,21 +2969,39 @@
         * Callbacks must commit any transactions that they begin.
         *
         * This is useful for updates to different systems or separate 
transactions are needed.
-        *
-        * @since 1.20
+        * It can also be used for updates that easily cause deadlocks if locks 
are held too long.
         *
         * @param Closure $callback
+        * @since 1.20
         */
        final public function onTransactionIdle( Closure $callback ) {
-               if ( $this->mTrxLevel ) {
-                       $this->mTrxIdleCallbacks[] = $callback;
-               } else {
-                       $callback();
+               $this->mTrxIdleCallbacks[] = $callback;
+               if ( !$this->mTrxLevel ) {
+                       $this->runOnTransactionIdleCallbacks();
                }
        }
 
        /**
-        * Actually run the "on transaction idle" callbacks.
+        * Run an anonymous function before the current transaction commits or 
now if there is none.
+        * If there is a transaction and it is rolled back, then the callback 
is cancelled.
+        * Callbacks must not start nor commit any transactions.
+        *
+        * This is useful for updates that easily cause deadlocks if locks are 
held too long
+        * but where atomicity is strongly desired for these and some related 
updates.
+        *
+        * @param Closure $callback
+        * @since 1.22
+        */
+       final public function onTransactionPreCommitOrIdle( Closure $callback ) 
{
+               if ( $this->mTrxLevel ) {
+                       $this->mTrxPreCommitCallbacks[] = $callback;
+               } else {
+                       $this->onTransactionIdle( $callback ); // this will 
trigger immediately
+               }
+       }
+
+       /**
+        * Actually any "on transaction idle" callbacks.
         *
         * @since 1.20
         */
@@ -3001,6 +3021,28 @@
                                }
                        }
                } while ( count( $this->mTrxIdleCallbacks ) );
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any last exception
+               }
+       }
+
+       /**
+        * Actually any "on transaction pre-commit" callbacks.
+        *
+        * @since 1.22
+        */
+       protected function runOnTransactionPreCommitCallbacks() {
+               $e = null; // last exception
+               do { // callbacks may add callbacks :)
+                       $callbacks = $this->mTrxPreCommitCallbacks;
+                       $this->mTrxPreCommitCallbacks = array(); // recursion 
guard
+                       foreach ( $callbacks as $callback ) {
+                               try {
+                                       $callback();
+                               } catch ( Exception $e ) {}
+                       }
+               } while ( count( $this->mTrxPreCommitCallbacks ) );
 
                if ( $e instanceof Exception ) {
                        throw $e; // re-throw any last exception
@@ -3040,6 +3082,7 @@
                                }
                        }
 
+                       $this->runOnTransactionPreCommitCallbacks();
                        $this->doCommit( $fname );
                        $this->runOnTransactionIdleCallbacks();
                }
@@ -3088,6 +3131,7 @@
                        }
                }
 
+               $this->runOnTransactionPreCommitCallbacks();
                $this->doCommit( $fname );
                $this->runOnTransactionIdleCallbacks();
        }
@@ -3119,6 +3163,7 @@
                }
                $this->doRollback( $fname );
                $this->mTrxIdleCallbacks = array(); // cancel
+               $this->mTrxPreCommitCallbacks = array(); // cancel
        }
 
        /**
@@ -3704,8 +3749,8 @@
        }
 
        public function __destruct() {
-               if ( count( $this->mTrxIdleCallbacks ) ) { // sanity
-                       trigger_error( "Transaction idle callbacks still 
pending." );
+               if ( count( $this->mTrxIdleCallbacks ) || count( 
$this->mTrxPreCommitCallbacks ) ) {
+                       trigger_error( "Transaction idle or pre-commit 
callbacks still pending." ); // sanity
                }
        }
 }

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I0bf6171fac692cf3d6e04011321bed075f58724b
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Tim Starling <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to