From 55f6639c2ea7071f7f590760502fe5b1838db8ca Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Thu, 11 Aug 2016 19:26:21 -0700 Subject: [PATCH] objectcache: Add changeTTL() method 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 | 14 +++++++ .../libs/objectcache/MemcachedBagOStuff.php | 5 +++ includes/libs/objectcache/MemcachedClient.php | 42 +++++++++++++++++++ .../objectcache/MemcachedPeclBagOStuff.php | 6 +++ includes/objectcache/RedisBagOStuff.php | 24 +++++++++-- .../libs/objectcache/BagOStuffTest.php | 14 +++++++ 6 files changed, 102 insertions(+), 3 deletions(-) diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php index 25a5a2628e..5472e837d4 100644 --- a/includes/libs/objectcache/BagOStuff.php +++ b/includes/libs/objectcache/BagOStuff.php @@ -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 * diff --git a/includes/libs/objectcache/MemcachedBagOStuff.php b/includes/libs/objectcache/MemcachedBagOStuff.php index ba8c7363fc..596744107c 100644 --- a/includes/libs/objectcache/MemcachedBagOStuff.php +++ b/includes/libs/objectcache/MemcachedBagOStuff.php @@ -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. diff --git a/includes/libs/objectcache/MemcachedClient.php b/includes/libs/objectcache/MemcachedClient.php index 668135d070..c3fcab94bd 100644 --- a/includes/libs/objectcache/MemcachedClient.php +++ b/includes/libs/objectcache/MemcachedClient.php @@ -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 diff --git a/includes/objectcache/MemcachedPeclBagOStuff.php b/includes/objectcache/MemcachedPeclBagOStuff.php index 090ace8538..bb760bd383 100644 --- a/includes/objectcache/MemcachedPeclBagOStuff.php +++ b/includes/objectcache/MemcachedPeclBagOStuff.php @@ -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 ); + } } diff --git a/includes/objectcache/RedisBagOStuff.php b/includes/objectcache/RedisBagOStuff.php index 90508dac5f..c3e0c96b75 100644 --- a/includes/objectcache/RedisBagOStuff.php +++ b/includes/objectcache/RedisBagOStuff.php @@ -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 diff --git a/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php index a8beb91518..92fb95418c 100644 --- a/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php +++ b/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php @@ -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 */ -- 2.20.1