This can replace startAtomic/endAtomic pairs.
Bug: T122115
Change-Id: I01c8e4773ec2b42dbe87a5508a10b997be103c11
return $this->__call( __FUNCTION__, func_get_args() );
}
+ public function doAtomicSection( $fname, $callback ) {
+ return $this->__call( __FUNCTION__, func_get_args() );
+ }
+
public function begin( $fname = __METHOD__ ) {
return $this->__call( __FUNCTION__, func_get_args() );
}
}
}
+ final public function doAtomicSection( $fname, $callback ) {
+ if ( !is_callable( $callback ) ) {
+ throw new UnexpectedValueException( "Invalid callback." );
+ };
+
+ $this->startAtomic( $fname );
+ try {
+ call_user_func_array( $callback, array( $this, $fname ) );
+ } catch ( Exception $e ) {
+ $this->rollback( $fname );
+ throw $e;
+ }
+ $this->endAtomic( $fname );
+ }
+
/**
* Begin a transaction. If a transaction is already in progress,
* that transaction will be committed before the new transaction is started.
*/
public function endAtomic( $fname = __METHOD__ );
+ /**
+ * Run a callback to do an atomic set of updates for this database
+ *
+ * The $callback takes the following arguments:
+ * - This database object
+ * - The value of $fname
+ *
+ * If any exception occurs in the callback, then rollback() will be called and the error will
+ * be re-thrown. It may also be that the rollback itself fails with an exception before then.
+ * In any case, such errors are expected to terminate the request, without any outside caller
+ * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
+ * atomic section and uncommitted updates, which trashes the current request, requiring an
+ * error to be displayed.
+ *
+ * This can be an alternative to explicit startAtomic()/endAtomic() calls.
+ *
+ * @see DatabaseBase::startAtomic
+ * @see DatabaseBase::endAtomic
+ *
+ * @param string $fname Caller name (usually __METHOD__)
+ * @param callable $callback Callback that issues DB updates
+ * @throws DBError
+ * @throws RuntimeException
+ * @throws UnexpectedValueException
+ * @since 1.27
+ */
+ public function doAtomicSection( $fname, $callback );
+
/**
* Begin a transaction. If a transaction is already in progress,
* that transaction will be committed before the new transaction is started.
$value = 0;
}
- $vals[] = array( 'qc_type' => $this->getName(),
- 'qc_namespace' => $row->namespace,
- 'qc_title' => $row->title,
- 'qc_value' => $value );
+ $vals[] = array(
+ 'qc_type' => $this->getName(),
+ 'qc_namespace' => $row->namespace,
+ 'qc_title' => $row->title,
+ 'qc_value' => $value
+ );
}
- $dbw->startAtomic( __METHOD__ );
- # Clear out any old cached data
- $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
- # Save results into the querycache table on the master
- if ( count( $vals ) ) {
- $dbw->insert( 'querycache', $vals, __METHOD__ );
- }
- # Update the querycache_info record for the page
- $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname );
- $dbw->insert( 'querycache_info',
- array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ),
- $fname );
- $dbw->endAtomic( __METHOD__ );
+ $that = $this;
+ $dbw->doAtomicSection(
+ __METHOD__,
+ function ( IDatabase $dbw, $fname ) use ( $that, $vals ) {
+ # Clear out any old cached data
+ $dbw->delete( 'querycache',
+ array( 'qc_type' => $that->getName() ),
+ $fname
+ );
+ # Save results into the querycache table on the master
+ if ( count( $vals ) ) {
+ $dbw->insert( 'querycache', $vals, $fname );
+ }
+ # Update the querycache_info record for the page
+ $dbw->delete( 'querycache_info',
+ array( 'qci_type' => $that->getName() ),
+ $fname
+ );
+ $dbw->insert( 'querycache_info',
+ array( 'qci_type' => $that->getName(),
+ 'qci_timestamp' => $dbw->timestamp() ),
+ $fname
+ );
+ }
+ );
}
} catch ( DBError $e ) {
if ( !$ignoreErrors ) {