Merge "Fix some issues with Microsoft SQL Server support"
[lhc/web/wiklou.git] / includes / objectcache / RedisBagOStuff.php
index f54726f..e770b73 100644 (file)
@@ -23,7 +23,7 @@
 class RedisBagOStuff extends BagOStuff {
        /** @var RedisConnectionPool */
        protected $redisPool;
-       /** @var Array List of server names */
+       /** @var array List of server names */
        protected $servers;
        /** @var bool */
        protected $automaticFailover;
@@ -53,6 +53,7 @@ class RedisBagOStuff extends BagOStuff {
         *     consistent hashing algorithm). True by default. This has the
         *     potential to create consistency issues if a server is slow enough to
         *     flap, for example if it is in swap death.
+        * @param array $params
         */
        function __construct( $params ) {
                $redisConf = array( 'serializer' => 'none' ); // manage that in this class
@@ -210,6 +211,59 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
+       /**
+        * @param array $data
+        * @param int $expiry
+        * @return bool
+        */
+       public function setMulti( array $data, $expiry = 0 ) {
+               $section = new ProfileSection( __METHOD__ );
+
+               $batches = array();
+               $conns = array();
+               foreach ( $data as $key => $value ) {
+                       list( $server, $conn ) = $this->getConnection( $key );
+                       if ( !$conn ) {
+                               continue;
+                       }
+                       $conns[$server] = $conn;
+                       $batches[$server][] = $key;
+               }
+
+               $expiry = $this->convertToRelative( $expiry );
+               $result = true;
+               foreach ( $batches as $server => $batchKeys ) {
+                       $conn = $conns[$server];
+                       try {
+                               $conn->multi( Redis::PIPELINE );
+                               foreach ( $batchKeys as $key ) {
+                                       if ( $expiry ) {
+                                               $conn->setex( $key, $expiry, $this->serialize( $data[$key] ) );
+                                       } else {
+                                               $conn->set( $key, $this->serialize( $data[$key] ) );
+                                       }
+                               }
+                               $batchResult = $conn->exec();
+                               if ( $batchResult === false ) {
+                                       $this->debug( "setMulti request to $server failed" );
+                                       continue;
+                               }
+                               foreach ( $batchResult as $value ) {
+                                       if ( $value === false ) {
+                                               $result = false;
+                                       }
+                               }
+                       } catch ( RedisException $e ) {
+                               $this->handleException( $server, $conn, $e );
+                               $result = false;
+                       }
+               }
+
+               return $result;
+       }
+
+
+
        public function add( $key, $value, $expiry = 0 ) {
                $section = new ProfileSection( __METHOD__ );
 
@@ -244,6 +298,9 @@ class RedisBagOStuff extends BagOStuff {
         * command. But we are constrained by the memcached-like interface to
         * return null in that case. Once the key exists, further increments are
         * atomic.
+        * @param string $key
+        * @param int $value
+        * @param bool|mixed
         */
        public function incr( $key, $value = 1 ) {
                $section = new ProfileSection( __METHOD__ );
@@ -286,7 +343,7 @@ class RedisBagOStuff extends BagOStuff {
 
        /**
         * Get a Redis object with a connection suitable for fetching the specified key
-        * @return Array (server, RedisConnRef) or (false, false)
+        * @return array (server, RedisConnRef) or (false, false)
         */
        protected function getConnection( $key ) {
                if ( count( $this->servers ) === 1 ) {
@@ -305,11 +362,13 @@ class RedisBagOStuff extends BagOStuff {
                                return array( $server, $conn );
                        }
                }
+               $this->setLastError( BagOStuff::ERR_UNREACHABLE );
                return array( false, false );
        }
 
        /**
         * Log a fatal error
+        * @param string $msg
         */
        protected function logError( $msg ) {
                wfDebugLog( 'redis', "Redis error: $msg" );
@@ -320,13 +379,20 @@ class RedisBagOStuff extends BagOStuff {
         * and protocol errors. Sometimes it also closes the connection, sometimes
         * not. The safest response for us is to explicitly destroy the connection
         * object and let it be reopened during the next request.
+        * @param RedisConnRef $conn
+        * @param Exception $e
         */
        protected function handleException( RedisConnRef $conn, $e ) {
+               $this->setLastError( BagOStuff::ERR_UNEXPECTED );
                $this->redisPool->handleError( $conn, $e );
        }
 
        /**
         * Send information about a single request to the debug log
+        * @param string $method
+        * @param string $key
+        * @param string $server
+        * @param bool $result
         */
        public function logRequest( $method, $key, $server, $result ) {
                $this->debug( "$method $key on $server: " .