Merge "Fix WatchedItemStore last-seen stashing logic"
[lhc/web/wiklou.git] / includes / libs / objectcache / BagOStuff.php
index a7ef3d5..c439f9b 100644 (file)
@@ -87,18 +87,12 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /** @var int[] Map of (ATTR_* class constant => QOS_* class constant) */
        protected $attrMap = [];
 
-       /** Possible values for getLastError() */
-       const ERR_NONE = 0; // no error
-       const ERR_NO_RESPONSE = 1; // no response
-       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
        const READ_VERIFIED = 2; // promise that caller can tell when keys are stale
        /** Bitfield constants for set()/merge() */
-       const WRITE_SYNC = 1; // synchronously write to all locations for replicated stores
-       const WRITE_CACHE_ONLY = 2; // Only change state of the in-memory cache
+       const WRITE_SYNC = 4; // synchronously write to all locations for replicated stores
+       const WRITE_CACHE_ONLY = 8; // Only change state of the in-memory cache
 
        /**
         * $params include:
@@ -150,7 +144,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @param string $key
         * @param int $ttl Time-to-live (seconds)
         * @param callable $callback Callback that derives the new value
-        * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
+        * @param int $flags Bitfield of BagOStuff::READ_* or BagOStuff::WRITE_* constants [optional]
         * @return mixed The cached value if found or the result of $callback otherwise
         * @since 1.27
         */
@@ -163,7 +157,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        }
                        $value = call_user_func( $callback );
                        if ( $value !== false ) {
-                               $this->set( $key, $value, $ttl );
+                               $this->set( $key, $value, $ttl, $flags );
                        }
                }
 
@@ -262,8 +256,9 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @param string $key
         * @return bool True if the item was deleted or not found, false on failure
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         */
-       abstract public function delete( $key );
+       abstract public function delete( $key, $flags = 0 );
 
        /**
         * Merge changes into the existing cache value (possibly creating a new one)
@@ -272,6 +267,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * (which will be false if not present), and takes the arguments:
         * (this BagOStuff, cache key, current value, TTL).
         * The TTL parameter is reference set to $exptime. It can be overriden in the callback.
+        * If the callback returns false, then the current value will be unchanged (including TTL).
         *
         * @param string $key
         * @param callable $callback Callback method to be executed
@@ -292,9 +288,10 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @param callable $callback Callback method to be executed
         * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
         * @param int $attempts The amount of times to attempt a merge in case of failure
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         * @return bool Success
         */
-       protected function mergeViaCas( $key, $callback, $exptime = 0, $attempts = 10 ) {
+       protected function mergeViaCas( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
                do {
                        $this->clearLastError();
                        $reportDupes = $this->reportDupes;
@@ -320,10 +317,10 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                                $success = true; // do nothing
                        } elseif ( $currentValue === false ) {
                                // Try to create the key, failing if it gets created in the meantime
-                               $success = $this->add( $key, $value, $exptime );
+                               $success = $this->add( $key, $value, $exptime, $flags );
                        } else {
                                // Try to update the key, failing if it gets changed in the meantime
-                               $success = $this->cas( $casToken, $key, $value, $exptime );
+                               $success = $this->cas( $casToken, $key, $value, $exptime, $flags );
                        }
                        if ( $this->getLastError() ) {
                                $this->logger->warning(
@@ -345,10 +342,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @param string $key
         * @param mixed $value
         * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         * @return bool Success
         * @throws Exception
         */
-       protected function cas( $casToken, $key, $value, $exptime = 0 ) {
+       protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
                if ( !$this->lock( $key, 0 ) ) {
                        return false; // non-blocking
                }
@@ -356,7 +354,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                $curCasToken = null; // passed by reference
                $this->getWithToken( $key, $curCasToken, self::READ_LATEST );
                if ( $casToken === $curCasToken ) {
-                       $success = $this->set( $key, $value, $exptime );
+                       $success = $this->set( $key, $value, $exptime, $flags );
                } else {
                        $this->logger->info(
                                __METHOD__ . ' failed due to race condition for {key}.',
@@ -428,13 +426,14 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @param string $key
         * @param int $expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
         * @return bool Success Returns false if there is no key
         * @since 1.28
         */
-       public function changeTTL( $key, $expiry = 0 ) {
+       public function changeTTL( $key, $expiry = 0, $flags = 0 ) {
                $value = $this->get( $key );
 
-               return ( $value === false ) ? false : $this->set( $key, $value, $expiry );
+               return ( $value === false ) ? false : $this->set( $key, $value, $expiry, $flags );
        }
 
        /**
@@ -465,7 +464,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        function () use ( $key, $expiry, $fname ) {
                                $this->clearLastError();
                                if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
-                                       return true; // locked!
+                                       return WaitConditionLoop::CONDITION_REACHED; // locked!
                                } elseif ( $this->getLastError() ) {
                                        $this->logger->warning(
                                                $fname . ' failed due to I/O error for {key}.',
@@ -573,51 +572,66 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
 
        /**
         * Get an associative array containing the item for each of the keys that have items.
-        * @param array $keys List of strings
+        * @param string[] $keys List of keys
         * @param int $flags Bitfield; supports READ_LATEST [optional]
         * @return array
         */
        public function getMulti( array $keys, $flags = 0 ) {
                $res = [];
                foreach ( $keys as $key ) {
-                       $val = $this->get( $key );
+                       $val = $this->get( $key, $flags );
                        if ( $val !== false ) {
                                $res[$key] = $val;
                        }
                }
+
                return $res;
        }
 
        /**
-        * Batch insertion
-        * @param array $data $key => $value assoc array
+        * Batch insertion/replace
+        * @param mixed[] $data Map of (key => value)
         * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
         * @return bool Success
         * @since 1.24
         */
-       public function setMulti( array $data, $exptime = 0 ) {
+       public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
                $res = true;
                foreach ( $data as $key => $value ) {
-                       if ( !$this->set( $key, $value, $exptime ) ) {
+                       if ( !$this->set( $key, $value, $exptime, $flags ) ) {
                                $res = false;
                        }
                }
+
+               return $res;
+       }
+
+       /**
+        * Batch deletion
+        * @param string[] $keys List of keys
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        * @since 1.33
+        */
+       public function deleteMulti( array $keys, $flags = 0 ) {
+               $res = true;
+               foreach ( $keys as $key ) {
+                       $res = $this->delete( $key, $flags ) && $res;
+               }
+
                return $res;
        }
 
        /**
+        * Insertion
         * @param string $key
         * @param mixed $value
         * @param int $exptime
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
         * @return bool Success
         */
-       public function add( $key, $value, $exptime = 0 ) {
-               // @note: avoid lock() here since that method uses *this* method by default
-               if ( $this->get( $key ) === false ) {
-                       return $this->set( $key, $value, $exptime );
-               }
-               return false; // key already set
-       }
+       abstract public function add( $key, $value, $exptime = 0, $flags = 0 );
 
        /**
         * Increase stored value of $key by $value while preserving its TTL
@@ -629,7 +643,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                if ( !$this->lock( $key, 1 ) ) {
                        return false;
                }
-               $n = $this->get( $key );
+               $n = $this->get( $key, self::READ_LATEST );
                if ( $this->isInteger( $n ) ) { // key exists?
                        $n += intval( $value );
                        $this->set( $key, max( 0, $n ) ); // exptime?
@@ -710,7 +724,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * The callbacks may or may not be called ever, in any particular order.
         * They are likely to be invoked when something WRITE_SYNC is used used.
         * They should follow a caching pattern as shown below, so that any code
-        * using the word will get it's result no matter what happens.
+        * using the work will get it's result no matter what happens.
         * @code
         *     $result = null;
         *     $workCallback = function () use ( &$result ) {
@@ -728,24 +742,6 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                $this->busyCallbacks[] = $workCallback;
        }
 
-       /**
-        * Modify a cache update operation array for EventRelayer::notify()
-        *
-        * This is used for relayed writes, e.g. for broadcasting a change
-        * to multiple data-centers. If the array contains a 'val' field
-        * then the command involves setting a key to that value. Note that
-        * for simplicity, 'val' is always a simple scalar value. This method
-        * is used to possibly serialize the value and add any cache-specific
-        * key/values needed for the relayer daemon (e.g. memcached flags).
-        *
-        * @param array $event
-        * @return array
-        * @since 1.26
-        */
-       public function modifySimpleRelayEvent( array $event ) {
-               return $event;
-       }
-
        /**
         * @param string $text
         */
@@ -810,8 +806,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        public function makeKeyInternal( $keyspace, $args ) {
                $key = $keyspace;
                foreach ( $args as $arg ) {
-                       $arg = str_replace( ':', '%3A', $arg );
-                       $key = $key . ':' . $arg;
+                       $key .= ':' . str_replace( ':', '%3A', $arg );
                }
                return strtr( $key, ' ', '_' );
        }