/** @var int Bitfield */
protected $syncChecks = 0;
-
/** @var string|bool */
protected $autoResync = false;
+ /** @var bool */
+ protected $asyncWrites = false;
+
/* Possible internal backend consistency checks */
const CHECK_SIZE = 1;
const CHECK_TIME = 2;
* Use "conservative" to limit resyncing to copying newer master
* backend files over older (or non-existing) clone backend files.
* Cases that cannot be handled will result in operation abortion.
+ * - replication : Set to 'async' to defer file operations on the non-master backends.
+ * This will apply such updates post-send for web requests. Note that
+ * any checks from "syncChecks" are still synchronous.
*
* @param array $config
* @throws FileBackendError
$this->autoResync = isset( $config['autoResync'] )
? $config['autoResync']
: false;
+ $this->asyncWrites = isset( $config['replication'] ) && $config['replication'] === 'async';
// Construct backends here rather than via registration
// to keep these backends hidden from outside the proxy.
$namesUsed = array();
$mbe = $this->backends[$this->masterIndex]; // convenience
// Try to lock those files for the scope of this function...
+ $scopeLock = null;
if ( empty( $opts['nonLocking'] ) ) {
// Try to lock those files for the scope of this function...
/** @noinspection PhpUnusedLocalVariableInspection */
// If $ops only had one operation, this might avoid backend sync inconsistencies.
if ( $masterStatus->isOK() && $masterStatus->successCount > 0 ) {
foreach ( $this->backends as $index => $backend ) {
- if ( $index !== $this->masterIndex ) { // not done already
- $realOps = $this->substOpBatchPaths( $ops, $backend );
+ if ( $index === $this->masterIndex ) {
+ continue; // done already
+ }
+
+ $realOps = $this->substOpBatchPaths( $ops, $backend );
+ if ( $this->asyncWrites ) {
+ // Bind $scopeLock to the callback to preserve locks
+ DeferredUpdates::addCallableUpdate(
+ function() use ( $backend, $realOps, $opts, $scopeLock ) {
+ $backend->doOperations( $realOps, $opts );
+ }
+ );
+ } else {
$status->merge( $backend->doOperations( $realOps, $opts ) );
}
}
$status->merge( $masterStatus );
// Propagate the operations to the clone backends...
foreach ( $this->backends as $index => $backend ) {
- if ( $index !== $this->masterIndex ) { // not done already
- $realOps = $this->substOpBatchPaths( $ops, $backend );
+ if ( $index === $this->masterIndex ) {
+ continue; // done already
+ }
+
+ $realOps = $this->substOpBatchPaths( $ops, $backend );
+ if ( $this->asyncWrites ) {
+ DeferredUpdates::addCallableUpdate(
+ function() use ( $backend, $realOps ) {
+ $backend->doQuickOperations( $realOps );
+ }
+ );
+ } else {
$status->merge( $backend->doQuickOperations( $realOps ) );
}
}
}
protected function doPrepare( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $index => $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doPrepare( $realParams ) );
- }
-
- return $status;
+ return $this->doDirectoryOp( 'prepare', $params );
}
protected function doSecure( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $index => $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doSecure( $realParams ) );
- }
-
- return $status;
+ return $this->doDirectoryOp( 'secure', $params );
}
protected function doPublish( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $index => $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doPublish( $realParams ) );
- }
-
- return $status;
+ return $this->doDirectoryOp( 'publish', $params );
}
protected function doClean( array $params ) {
+ return $this->doDirectoryOp( 'clean', $params );
+ }
+
+ /**
+ * @param string $method One of (doPrepare,doSecure,doPublish,doClean)
+ * @param array $params Method arguments
+ * @return Status
+ */
+ protected function doDirectoryOp( $method, array $params ) {
$status = Status::newGood();
+
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ $masterStatus = $this->backends[$this->masterIndex]->$method( $realParams );
+ $status->merge( $masterStatus );
+
foreach ( $this->backends as $index => $backend ) {
+ if ( $index === $this->masterIndex ) {
+ continue; // already done
+ }
+
$realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doClean( $realParams ) );
+ if ( $this->asyncWrites ) {
+ DeferredUpdates::addCallableUpdate(
+ function() use ( $backend, $method, $realParams ) {
+ $backend->$method( $realParams );
+ }
+ );
+ } else {
+ $status->merge( $backend->$method( $realParams ) );
+ }
}
return $status;