Make MultiWriteBagOStuff use the native merge() of each backend
authorAaron Schulz <aschulz@wikimedia.org>
Tue, 10 Jul 2018 23:26:43 +0000 (00:26 +0100)
committerAaron Schulz <aschulz@wikimedia.org>
Wed, 18 Jul 2018 21:30:56 +0000 (22:30 +0100)
This lets backends that support CAS take advantage of it.

Also fix race condition with merge() in asyncWrites mode where two
threads might add to a list/set but the first request finishes after
the second, which would erase an item during the set().

Bug: T198239
Change-Id: Ibd2539429746f12b4de74d0f031089cb80aa8b4b

includes/libs/objectcache/MultiWriteBagOStuff.php

index 91f4167..043f8cb 100644 (file)
@@ -122,7 +122,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                        && $missIndexes
                        && ( $flags & self::READ_VERIFIED ) == self::READ_VERIFIED
                ) {
-                       // Backfill the value to the lower (and often larger) cache tiers
+                       // Backfill the value to the higher (and often faster/smaller) cache tiers
                        $this->doWrite(
                                $missIndexes, $this->asyncWrites, 'set', $key, $value, self::UPGRADE_TTL
                        );
@@ -171,6 +171,23 @@ class MultiWriteBagOStuff extends BagOStuff {
                return $this->doWrite( $this->cacheIndexes, $this->asyncWrites, 'decr', $key, $value );
        }
 
+       public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
+               $asyncWrites = ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC )
+                       ? false
+                       : $this->asyncWrites;
+
+               return $this->doWrite(
+                       $this->cacheIndexes,
+                       $asyncWrites,
+                       'merge',
+                       $key,
+                       $callback,
+                       $exptime,
+                       $attempts,
+                       $flags
+               );
+       }
+
        public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
                // Only need to lock the first cache; also avoids deadlocks
                return $this->caches[0]->lock( $key, $timeout, $expiry, $rclass );
@@ -202,7 +219,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                $ret = true;
                $args = array_slice( func_get_args(), 3 );
 
-               if ( array_diff( $indexes, [ 0 ] ) && $asyncWrites ) {
+               if ( array_diff( $indexes, [ 0 ] ) && $asyncWrites && $method !== 'merge' ) {
                        // Deep-clone $args to prevent misbehavior when something writes an
                        // object to the BagOStuff then modifies it afterwards, e.g. T168040.
                        $args = unserialize( serialize( $args ) );