Inject MultiWriteBagOStuff addCallableUpdate() dependency

Inject the DeferredUpdates::addCallableUpdate method via the
ObjectCache. This brings it closer to being able to move to /libs.

Change-Id: Ifa0d893002c3d709a4dc7346c263a92162274bd7
This commit is contained in:
Aaron Schulz 2015-10-09 01:01:28 -07:00
parent 442d4cb497
commit 1171cc00cd
3 changed files with 34 additions and 19 deletions

View file

@ -33,6 +33,8 @@ class MultiWriteBagOStuff extends BagOStuff {
protected $caches;
/** @var bool Use async secondary writes */
protected $asyncWrites = false;
/** @var callback|null */
protected $asyncHandler;
/** Idiom for "write to all backends" */
const ALL = INF;
@ -48,15 +50,16 @@ class MultiWriteBagOStuff extends BagOStuff {
* be read in the fallback chain. Writes happen to all stores
* in the order they are defined. However, lock()/unlock() calls
* only use the primary store.
* - replication: Either 'sync' or 'async'. This controls whether writes to
* secondary stores are deferred when possible. Async writes
* require the HHVM register_postsend_function() function.
* - replication: Either 'sync' or 'async'. This controls whether writes
* to secondary stores are deferred when possible. Async writes
* require setting 'asyncCallback'. HHVM register_postsend_function() function.
* Async writes can increase the chance of some race conditions
* or cause keys to expire seconds later than expected. It is
* safe to use for modules when cached values: are immutable,
* invalidation uses logical TTLs, invalidation uses etag/timestamp
* validation against the DB, or merge() is used to handle races.
*
* - asyncHandler: callable that takes a callback and runs it after the
* current web request ends. In CLI mode, it should run it immediately.
* @param array $params
* @throws InvalidArgumentException
*/
@ -85,7 +88,14 @@ class MultiWriteBagOStuff extends BagOStuff {
}
}
$this->asyncWrites = isset( $params['replication'] ) && $params['replication'] === 'async';
$this->asyncHandler = isset( $params['asyncHandler'] )
? $params['asyncHandler']
: null;
$this->asyncWrites = (
isset( $params['replication'] ) &&
$params['replication'] === 'async' &&
is_callable( $this->asyncHandler )
);
}
/**
@ -226,7 +236,8 @@ class MultiWriteBagOStuff extends BagOStuff {
} else {
// Secondary write in async mode: do not block this HTTP request
$logger = $this->logger;
DeferredUpdates::addCallableUpdate(
call_user_func(
$this->asyncHandler,
function () use ( $cache, $method, $args, $logger ) {
if ( !call_user_func_array( array( $cache, $method ), $args ) ) {
$logger->warning( "Async $method op failed" );

View file

@ -178,6 +178,9 @@ class ObjectCache {
return call_user_func( $params['factory'], $params );
} elseif ( isset( $params['class'] ) ) {
$class = $params['class'];
if ( $class === 'MultiWriteBagOStuff' && !isset( $params['asyncHandler'] ) ) {
$params['asyncHandler'] = 'DeferredUpdates::addCallableUpdate';
}
return new $class( $params );
} else {
throw new MWException( "The definition of cache type \""

View file

@ -18,7 +18,8 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
$this->cache2 = new HashBagOStuff();
$this->cache = new MultiWriteBagOStuff( array(
'caches' => array( $this->cache1, $this->cache2 ),
'replication' => 'async'
'replication' => 'async',
'asyncHandler' => 'DeferredUpdates::addCallableUpdate'
) );
}