From 3f277e87441ee4699d7eb2955961cd1cfb113c72 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 29 Aug 2012 12:32:47 -0700 Subject: [PATCH] Added post-commit callback support to DB classes. * This can allow for autocommit job queue insertion without breaking existing transactions (which currently happens since job queue insert does begin/commit). Change-Id: I021d4a73635168c05bbc1068912c2c6dbdf9cdc4 --- includes/db/Database.php | 80 ++++++++++++++++++++++++++++++--- includes/db/DatabaseIbm_db2.php | 6 +-- includes/db/DatabaseMssql.php | 6 +-- includes/db/DatabaseOracle.php | 6 +-- includes/db/DatabaseSqlite.php | 6 +-- 5 files changed, 86 insertions(+), 18 deletions(-) diff --git a/includes/db/Database.php b/includes/db/Database.php index ae5335b63a..3354f984c3 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -225,12 +225,12 @@ abstract class DatabaseBase implements DatabaseType { protected $mServer, $mUser, $mPassword, $mDBname; - /** - * @var DatabaseBase - */ protected $mConn = null; protected $mOpened = false; + /** @var Array */ + protected $trxIdleCallbacks = array(); + protected $mTablePrefix; protected $mFlags; protected $mTrxLevel = 0; @@ -2825,12 +2825,62 @@ abstract class DatabaseBase implements DatabaseType { } } + /** + * Run an anonymous function as soon as there is no transaction pending. + * If there is a transaction and it is rolled back, then the callback is cancelled. + * Callbacks must commit any transactions that they begin. + * + * This is useful for updates to different systems or separate transactions are needed. + * + * @param Closure $callback + * @return void + */ + final public function onTransactionIdle( Closure $callback ) { + if ( $this->mTrxLevel ) { + $this->trxIdleCallbacks[] = $callback; + } else { + $callback(); + } + } + + /** + * Actually run the "on transaction idle" callbacks + */ + protected function runOnTransactionIdleCallbacks() { + $e = null; // last exception + do { // callbacks may add callbacks :) + $callbacks = $this->trxIdleCallbacks; + $this->trxIdleCallbacks = array(); // recursion guard + foreach ( $callbacks as $callback ) { + try { + $callback(); + } catch ( Exception $e ) {} + } + } while ( count( $this->trxIdleCallbacks ) ); + + if ( $e instanceof Exception ) { + throw $e; // re-throw any last exception + } + } + /** * Begin a transaction * * @param $fname string */ - public function begin( $fname = 'DatabaseBase::begin' ) { + final public function begin( $fname = 'DatabaseBase::begin' ) { + if ( $this->mTrxLevel ) { // implicit commit + $this->doCommit( $fname ); + $this->runOnTransactionIdleCallbacks(); + } + $this->doBegin( $fname ); + } + + /** + * @see DatabaseBase::begin() + * @param type $fname + */ + protected function doBegin( $fname ) { $this->query( 'BEGIN', $fname ); $this->mTrxLevel = 1; } @@ -2840,7 +2890,16 @@ abstract class DatabaseBase implements DatabaseType { * * @param $fname string */ - public function commit( $fname = 'DatabaseBase::commit' ) { + final public function commit( $fname = 'DatabaseBase::commit' ) { + $this->doCommit( $fname ); + $this->runOnTransactionIdleCallbacks(); + } + + /** + * @see DatabaseBase::commit() + * @param type $fname + */ + protected function doCommit( $fname ) { if ( $this->mTrxLevel ) { $this->query( 'COMMIT', $fname ); $this->mTrxLevel = 0; @@ -2853,7 +2912,16 @@ abstract class DatabaseBase implements DatabaseType { * * @param $fname string */ - public function rollback( $fname = 'DatabaseBase::rollback' ) { + final public function rollback( $fname = 'DatabaseBase::rollback' ) { + $this->doRollback( $fname ); + $this->trxIdleCallbacks = array(); // cancel + } + + /** + * @see DatabaseBase::rollback() + * @param type $fname + */ + protected function doRollback( $fname ) { if ( $this->mTrxLevel ) { $this->query( 'ROLLBACK', $fname, true ); $this->mTrxLevel = 0; diff --git a/includes/db/DatabaseIbm_db2.php b/includes/db/DatabaseIbm_db2.php index 80220af0ec..f1f6dfca58 100644 --- a/includes/db/DatabaseIbm_db2.php +++ b/includes/db/DatabaseIbm_db2.php @@ -807,7 +807,7 @@ class DatabaseIbm_db2 extends DatabaseBase { /** * Start a transaction (mandatory) */ - public function begin( $fname = 'DatabaseIbm_db2::begin' ) { + protected function doBegin( $fname = 'DatabaseIbm_db2::begin' ) { // BEGIN is implicit for DB2 // However, it requires that AutoCommit be off. @@ -823,7 +823,7 @@ class DatabaseIbm_db2 extends DatabaseBase { * End a transaction * Must have a preceding begin() */ - public function commit( $fname = 'DatabaseIbm_db2::commit' ) { + protected function doCommit( $fname = 'DatabaseIbm_db2::commit' ) { db2_commit( $this->mConn ); // Some MediaWiki code is still transaction-less (?). @@ -837,7 +837,7 @@ class DatabaseIbm_db2 extends DatabaseBase { /** * Cancel a transaction */ - public function rollback( $fname = 'DatabaseIbm_db2::rollback' ) { + protected function doRollback( $fname = 'DatabaseIbm_db2::rollback' ) { db2_rollback( $this->mConn ); // turn auto-commit back on // not sure if this is appropriate diff --git a/includes/db/DatabaseMssql.php b/includes/db/DatabaseMssql.php index 3846e96101..914ab4089a 100644 --- a/includes/db/DatabaseMssql.php +++ b/includes/db/DatabaseMssql.php @@ -694,7 +694,7 @@ class DatabaseMssql extends DatabaseBase { /** * Begin a transaction, committing any previously open transaction */ - function begin( $fname = 'DatabaseMssql::begin' ) { + protected function doBegin( $fname = 'DatabaseMssql::begin' ) { sqlsrv_begin_transaction( $this->mConn ); $this->mTrxLevel = 1; } @@ -702,7 +702,7 @@ class DatabaseMssql extends DatabaseBase { /** * End a transaction */ - function commit( $fname = 'DatabaseMssql::commit' ) { + protected function doCommit( $fname = 'DatabaseMssql::commit' ) { sqlsrv_commit( $this->mConn ); $this->mTrxLevel = 0; } @@ -711,7 +711,7 @@ class DatabaseMssql extends DatabaseBase { * Rollback a transaction. * No-op on non-transactional databases. */ - function rollback( $fname = 'DatabaseMssql::rollback' ) { + protected function doRollback( $fname = 'DatabaseMssql::rollback' ) { sqlsrv_rollback( $this->mConn ); $this->mTrxLevel = 0; } diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php index cf3e45dc2e..7d8884fb6b 100644 --- a/includes/db/DatabaseOracle.php +++ b/includes/db/DatabaseOracle.php @@ -955,12 +955,12 @@ class DatabaseOracle extends DatabaseBase { return $this->fieldInfoMulti ($table, $field); } - function begin( $fname = 'DatabaseOracle::begin' ) { + protected function doBegin( $fname = 'DatabaseOracle::begin' ) { $this->mTrxLevel = 1; $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' ); } - function commit( $fname = 'DatabaseOracle::commit' ) { + protected function doCommit( $fname = 'DatabaseOracle::commit' ) { if ( $this->mTrxLevel ) { $ret = oci_commit( $this->mConn ); if ( !$ret ) { @@ -971,7 +971,7 @@ class DatabaseOracle extends DatabaseBase { } } - function rollback( $fname = 'DatabaseOracle::rollback' ) { + protected function doRollback( $fname = 'DatabaseOracle::rollback' ) { if ( $this->mTrxLevel ) { oci_rollback( $this->mConn ); $this->mTrxLevel = 0; diff --git a/includes/db/DatabaseSqlite.php b/includes/db/DatabaseSqlite.php index cb3da1e71a..f1e553d736 100644 --- a/includes/db/DatabaseSqlite.php +++ b/includes/db/DatabaseSqlite.php @@ -645,7 +645,7 @@ class DatabaseSqlite extends DatabaseBase { return false; } - function begin( $fname = '' ) { + protected function doBegin( $fname = '' ) { if ( $this->mTrxLevel == 1 ) { $this->commit( __METHOD__ ); } @@ -653,7 +653,7 @@ class DatabaseSqlite extends DatabaseBase { $this->mTrxLevel = 1; } - function commit( $fname = '' ) { + protected function doCommit( $fname = '' ) { if ( $this->mTrxLevel == 0 ) { return; } @@ -661,7 +661,7 @@ class DatabaseSqlite extends DatabaseBase { $this->mTrxLevel = 0; } - function rollback( $fname = '' ) { + protected function doRollback( $fname = '' ) { if ( $this->mTrxLevel == 0 ) { return; } -- 2.20.1