objectcache: Add changeTTL() method
authorAaron Schulz <aschulz@wikimedia.org>
Fri, 12 Aug 2016 02:26:21 +0000 (19:26 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Fri, 12 Aug 2016 02:55:32 +0000 (19:55 -0700)
This can change the TTL without fetching the object
so that keys can be renewed or set to expire soon.

Change-Id: Id1c2c9f89b3445cfa34263057dc5029cbe170833

includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedClient.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/RedisBagOStuff.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php

index 25a5a26..5472e83 100644 (file)
@@ -372,6 +372,20 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                return $success;
        }
 
+       /**
+        * Reset the TTL on a key if it exists
+        *
+        * @param string $key
+        * @param int $expiry
+        * @return bool Success Returns false if there is no key
+        * @since 1.28
+        */
+       public function changeTTL( $key, $expiry = 0 ) {
+               $value = $this->get( $key );
+
+               return ( $value === false ) ? false : $this->set( $key, $value, $expiry );
+       }
+
        /**
         * Acquire an advisory lock on a key string
         *
index ba8c736..5967441 100644 (file)
@@ -79,6 +79,11 @@ class MemcachedBagOStuff extends BagOStuff {
                return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
        }
 
+       public function changeTTL( $key, $exptime = 0 ) {
+               return $this->client->touch( $this->validateKeyEncoding( $key ),
+                       $this->fixExpiry( $exptime ) );
+       }
+
        /**
         * Get the underlying client object. This is provided for debugging
         * purposes.
index 668135d..c3fcab9 100644 (file)
@@ -360,6 +360,48 @@ class MemcachedClient {
                return false;
        }
 
+       /**
+        * Changes the TTL on a key from the server to $time
+        *
+        * @param string $key Key
+        * @param int $time TTL in seconds
+        *
+        * @return bool True on success, false on failure
+        */
+       public function touch( $key, $time = 0 ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               $sock = $this->get_sock( $key );
+               if ( !is_resource( $sock ) ) {
+                       return false;
+               }
+
+               $key = is_array( $key ) ? $key[1] : $key;
+
+               if ( isset( $this->stats['touch'] ) ) {
+                       $this->stats['touch']++;
+               } else {
+                       $this->stats['touch'] = 1;
+               }
+               $cmd = "touch $key $time\r\n";
+               if ( !$this->_fwrite( $sock, $cmd ) ) {
+                       return false;
+               }
+               $res = $this->_fgets( $sock );
+
+               if ( $this->_debug ) {
+                       $this->_debugprint( sprintf( "MemCache: touch %s (%s)", $key, $res ) );
+               }
+
+               if ( $res == "TOUCHED" ) {
+                       return true;
+               }
+
+               return false;
+       }
+
        /**
         * @param string $key
         * @param int $timeout
index 090ace8..bb760bd 100644 (file)
@@ -226,4 +226,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
                return $this->checkResult( false, $result );
        }
+
+       public function changeTTL( $key, $expiry = 0 ) {
+               $this->debugLog( "touch($key)" );
+               $result = $this->client->touch( $key, $expiry );
+               return $this->checkResult( $key, $result );
+       }
 }
index 90508da..c3e0c96 100644 (file)
@@ -272,10 +272,10 @@ class RedisBagOStuff extends BagOStuff {
                if ( !$conn ) {
                        return false;
                }
-               if ( !$conn->exists( $key ) ) {
-                       return null;
-               }
                try {
+                       if ( !$conn->exists( $key ) ) {
+                               return null;
+                       }
                        // @FIXME: on races, the key may have a 0 TTL
                        $result = $conn->incrBy( $key, $value );
                } catch ( RedisException $e ) {
@@ -287,6 +287,24 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
+       public function changeTTL( $key, $expiry = 0 ) {
+               list( $server, $conn ) = $this->getConnection( $key );
+               if ( !$conn ) {
+                       return false;
+               }
+
+               $expiry = $this->convertToRelative( $expiry );
+               try {
+                       $result = $conn->expire( $key, $expiry );
+               } catch ( RedisException $e ) {
+                       $result = false;
+                       $this->handleException( $conn, $e );
+               }
+
+               $this->logRequest( 'expire', $key, $server, $result );
+               return $result;
+       }
+
        public function modifySimpleRelayEvent( array $event ) {
                if ( array_key_exists( 'val', $event ) ) {
                        $event['val'] = serialize( $event['val'] ); // this class uses PHP serialization
index a8beb91..92fb954 100644 (file)
@@ -138,6 +138,20 @@ class BagOStuffTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers BagOStuff::changeTTL
+        */
+       public function testChangeTTL() {
+               $key = wfMemcKey( 'test' );
+               $value = 'meow';
+
+               $this->cache->add( $key, $value );
+               $this->assertTrue( $this->cache->changeTTL( $key, 5 ) );
+               $this->assertEquals( $this->cache->get( $key ), $value );
+               $this->cache->delete( $key );
+               $this->assertFalse( $this->cache->changeTTL( $key, 5 ) );
+       }
+
        /**
         * @covers BagOStuff::add
         */