Added BagOStuff READ_LATEST flag for replicated stores
authorAaron Schulz <aschulz@wikimedia.org>
Wed, 27 May 2015 18:52:44 +0000 (11:52 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Tue, 4 Aug 2015 23:03:37 +0000 (16:03 -0700)
Bug: T88493
Change-Id: I7ea050a2eabba635f2aadb4e33b6f8fbfb1b01a8

14 files changed:
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/EmptyBagOStuff.php
includes/libs/objectcache/HashBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WinCacheBagOStuff.php
includes/libs/objectcache/XCacheBagOStuff.php
includes/objectcache/MemcachedBagOStuff.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/MemcachedPhpBagOStuff.php
includes/objectcache/MultiWriteBagOStuff.php
includes/objectcache/ObjectCache.php
includes/objectcache/RedisBagOStuff.php
includes/objectcache/SqlBagOStuff.php

index cc54fe7..0dbbaba 100644 (file)
@@ -27,7 +27,6 @@
  * @ingroup Cache
  */
 class APCBagOStuff extends BagOStuff {
-
        /**
         * @var string String to append to each APC key. This may be changed
         *  whenever the handling of values is changed, to prevent existing code
@@ -35,7 +34,7 @@ class APCBagOStuff extends BagOStuff {
         **/
        const KEY_SUFFIX = ':1';
 
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                $val = apc_fetch( $key . self::KEY_SUFFIX );
 
                $casToken = $val;
index 65ff0ee..726c789 100644 (file)
@@ -59,6 +59,9 @@ abstract class BagOStuff implements LoggerAwareInterface {
        const ERR_UNREACHABLE = 2; // can't connect
        const ERR_UNEXPECTED = 3; // response gave some error
 
+       /** Bitfield constants for get()/getMulti() */
+       const READ_LATEST = 1; // use latest data for replicated stores
+
        public function __construct( array $params = array() ) {
                if ( isset( $params['logger'] ) ) {
                        $this->setLogger( $params['logger'] );
@@ -86,9 +89,10 @@ abstract class BagOStuff implements LoggerAwareInterface {
         * Get an item with the given key. Returns false if it does not exist.
         * @param string $key
         * @param mixed $casToken [optional]
+        * @param integer $flags Bitfield; supports READ_LATEST [optional]
         * @return mixed Returns false on failure
         */
-       abstract public function get( $key, &$casToken = null );
+       abstract public function get( $key, &$casToken = null, $flags = 0 );
 
        /**
         * Set an item.
@@ -262,14 +266,13 @@ abstract class BagOStuff implements LoggerAwareInterface {
                return false;
        }
 
-       /* *** Emulated functions *** */
-
        /**
         * Get an associative array containing the item for each of the keys that have items.
         * @param array $keys List of strings
+        * @param integer $flags Bitfield; supports READ_LATEST [optional]
         * @return array
         */
-       public function getMulti( array $keys ) {
+       public function getMulti( array $keys, $flags = 0 ) {
                $res = array();
                foreach ( $keys as $key ) {
                        $val = $this->get( $key );
index 4ccf270..55e84b0 100644 (file)
@@ -27,7 +27,7 @@
  * @ingroup Cache
  */
 class EmptyBagOStuff extends BagOStuff {
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                return false;
        }
 
index 2c8b05a..185c74b 100644 (file)
@@ -48,7 +48,7 @@ class HashBagOStuff extends BagOStuff {
                return true;
        }
 
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                if ( !isset( $this->bag[$key] ) ) {
                        return false;
                }
index 1b24616..20e146d 100644 (file)
@@ -72,12 +72,16 @@ class ReplicatedBagOStuff extends BagOStuff {
                $this->readStore->setDebug( $debug );
        }
 
-       public function get( $key, &$casToken = null ) {
-               return $this->readStore->get( $key, $casToken );
+       public function get( $key, &$casToken = null, $flags = 0 ) {
+               return ( $flags & self::READ_LATEST )
+                       ? $this->writeStore->get( $key, $casToken, $flags )
+                       : $this->readStore->get( $key, $casToken, $flags );
        }
 
-       public function getMulti( array $keys ) {
-               return $this->readStore->getMulti( $keys );
+       public function getMulti( array $keys, $flags = 0 ) {
+               return ( $flags & self::READ_LATEST )
+                       ? $this->writeStore->getMulti( $keys, $flags )
+                       : $this->readStore->getMulti( $keys, $flags );
        }
 
        public function set( $key, $value, $exptime = 0 ) {
index 5362574..c480aa0 100644 (file)
  * @ingroup Cache
  */
 class WinCacheBagOStuff extends BagOStuff {
-
-       /**
-        * Get a value from the WinCache object cache
-        *
-        * @param string $key Cache key
-        * @param int $casToken [optional] Cas token
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                $val = wincache_ucache_get( $key );
 
                $casToken = $val;
@@ -48,14 +40,6 @@ class WinCacheBagOStuff extends BagOStuff {
                return $val;
        }
 
-       /**
-        * Store a value in the WinCache object cache
-        *
-        * @param string $key Cache key
-        * @param mixed $value Value to store
-        * @param int $expire Expiration time
-        * @return bool
-        */
        public function set( $key, $value, $expire = 0 ) {
                $result = wincache_ucache_set( $key, serialize( $value ), $expire );
 
@@ -64,25 +48,10 @@ class WinCacheBagOStuff extends BagOStuff {
                return ( is_array( $result ) && $result === array() ) || $result;
        }
 
-       /**
-        * Store a value in the WinCache object cache, race condition-safe
-        *
-        * @param int $casToken Cas token
-        * @param string $key Cache key
-        * @param int $value Object to store
-        * @param int $exptime Expiration time
-        * @return bool
-        */
        protected function cas( $casToken, $key, $value, $exptime = 0 ) {
                return wincache_ucache_cas( $key, $casToken, serialize( $value ) );
        }
 
-       /**
-        * Remove a value from the WinCache object cache
-        *
-        * @param string $key Cache key
-        * @return bool
-        */
        public function delete( $key ) {
                wincache_ucache_delete( $key );
 
index cfee923..9dbff6f 100644 (file)
  * @ingroup Cache
  */
 class XCacheBagOStuff extends BagOStuff {
-       /**
-        * Get a value from the XCache object cache
-        *
-        * @param string $key Cache key
-        * @param mixed $casToken Cas token
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                $val = xcache_get( $key );
 
                if ( is_string( $val ) ) {
@@ -51,14 +44,6 @@ class XCacheBagOStuff extends BagOStuff {
                return $val;
        }
 
-       /**
-        * Store a value in the XCache object cache
-        *
-        * @param string $key Cache key
-        * @param mixed $value Object to store
-        * @param int $expire Expiration time
-        * @return bool
-        */
        public function set( $key, $value, $expire = 0 ) {
                if ( !$this->isInteger( $value ) ) {
                        $value = serialize( $value );
@@ -68,12 +53,6 @@ class XCacheBagOStuff extends BagOStuff {
                return true;
        }
 
-       /**
-        * Remove a value from the XCache object cache
-        *
-        * @param string $key Cache key
-        * @return bool
-        */
        public function delete( $key ) {
                xcache_unset( $key );
                return true;
index eeca9b1..e545aa5 100644 (file)
@@ -57,12 +57,7 @@ class MemcachedBagOStuff extends BagOStuff {
                return $params;
        }
 
-       /**
-        * @param string $key
-        * @param mixed $casToken [optional]
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                return $this->client->get( $this->encodeKey( $key ), $casToken );
        }
 
index f2c4928..7e6a4d7 100644 (file)
@@ -115,12 +115,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                $this->client->addServers( $servers );
        }
 
-       /**
-        * @param string $key
-        * @param float $casToken [optional]
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                $this->debugLog( "get($key)" );
                $result = $this->client->get( $this->encodeKey( $key ), null, $casToken );
                $result = $this->checkResult( $key, $result );
@@ -238,11 +233,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                return $result;
        }
 
-       /**
-        * @param array $keys
-        * @return array
-        */
-       public function getMulti( array $keys ) {
+       public function getMulti( array $keys, $flags = 0 ) {
                $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
                $callback = array( $this, 'encodeKey' );
                $result = $this->client->getMulti( array_map( $callback, $keys ) );
index 6fba61b..69792ad 100644 (file)
@@ -57,11 +57,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
                $this->client->set_debug( $debug );
        }
 
-       /**
-        * @param array $keys
-        * @return array
-        */
-       public function getMulti( array $keys ) {
+       public function getMulti( array $keys, $flags = 0 ) {
                $callback = array( $this, 'encodeKey' );
                return $this->client->get_multi( array_map( $callback, $keys ) );
        }
index be54e4d..3a30f83 100644 (file)
@@ -61,14 +61,9 @@ class MultiWriteBagOStuff extends BagOStuff {
                $this->doWrite( 'setDebug', $debug );
        }
 
-       /**
-        * @param string $key
-        * @param mixed $casToken [optional]
-        * @return bool|mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                foreach ( $this->caches as $cache ) {
-                       $value = $cache->get( $key );
+                       $value = $cache->get( $key, null, $flags = 0 );
                        if ( $value !== false ) {
                                return $value;
                        }
index c5850b6..8a99f53 100644 (file)
@@ -29,9 +29,13 @@ use MediaWiki\Logger\LoggerFactory;
  * The word "cache" has two main dictionary meanings, and both
  * are used in this factory class. They are:
  *   - a) A place to store copies or computations on existing data
- *     for higher access speeds (the computer science definition)
+ *        for higher access speeds (the computer science definition)
  *   - b) A place to store lightweight data that is not canonically
- *     stored anywhere else (e.g. a "hoard" of objects)
+ *        stored anywhere else (e.g. a "hoard" of objects)
+ *
+ * The former should always use strongly consistent stores, so callers don't
+ * have to deal with stale reads. The later may be eventually consistent, but
+ * callers can use BagOStuff:READ_LATEST to see the latest available data.
  *
  * @ingroup Cache
  */
@@ -238,9 +242,10 @@ class ObjectCache {
         * In general, this means avoiding updates on idempotent HTTP requests and
         * avoiding an assumption of perfect serializability (or accepting anomalies).
         * Reads may be eventually consistent or data might rollback as nodes flap.
+        * Callers can use BagOStuff:READ_LATEST to see the latest available data.
         *
-        * @since 1.26
         * @return BagOStuff
+        * @since 1.26
         */
        static function getMainStashInstance() {
                global $wgMainStash;
index 11dd660..a9af9b1 100644 (file)
@@ -80,7 +80,7 @@ class RedisBagOStuff extends BagOStuff {
                }
        }
 
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
 
                list( $server, $conn ) = $this->getConnection( $key );
                if ( !$conn ) {
@@ -174,7 +174,7 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
-       public function getMulti( array $keys ) {
+       public function getMulti( array $keys, $flags = 0 ) {
 
                $batches = array();
                $conns = array();
index 3b46249..e08fec9 100644 (file)
@@ -220,12 +220,7 @@ class SqlBagOStuff extends BagOStuff {
                }
        }
 
-       /**
-        * @param string $key
-        * @param mixed $casToken [optional]
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
+       public function get( $key, &$casToken = null, $flags = 0 ) {
                $values = $this->getMulti( array( $key ) );
                if ( array_key_exists( $key, $values ) ) {
                        $casToken = $values[$key];
@@ -234,11 +229,7 @@ class SqlBagOStuff extends BagOStuff {
                return false;
        }
 
-       /**
-        * @param array $keys
-        * @return array
-        */
-       public function getMulti( array $keys ) {
+       public function getMulti( array $keys, $flags = 0 ) {
                $values = array(); // array of (key => value)
 
                $keysByTable = array();