Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Set ttl to specific cache key for Redis Storage Adapter #10

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 90 additions & 2 deletions src/Storage/Adapter/Redis.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,25 @@ protected function internalAddItem(& $normalizedKey, & $value)
}
}

/**
* Internal method to touch an item.
*
* @param string &$normalizedKey Key which will be touched
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalTouchItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
$ttl = $this->getOptions()->getTtl();
return (bool) $redis->expire($this->namespacePrefix . $normalizedKey, $ttl);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}

/**
* Internal method to remove an item.
*
Expand Down Expand Up @@ -426,7 +445,11 @@ protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$minTtl = $this->resourceManager->getMajorVersion($this->resourceId) < 2 ? 0 : 1;

$redisVersion = $this->resourceManager->getMajorVersion($this->resourceId);
$minTtl = version_compare($redisVersion, '2', '<') ? 0 : 1;
$supportedMetadata = version_compare($redisVersion, '2', '>=') ? ['ttl'] : [];

//without serialization redis supports only strings for simple
//get/set methods
$this->capabilities = new Capabilities(
Expand All @@ -443,7 +466,7 @@ protected function internalGetCapabilities()
'object' => false,
'resource' => false,
],
'supportedMetadata' => [],
'supportedMetadata' => $supportedMetadata,
'minTtl' => $minTtl,
'maxTtl' => 0,
'staticTtl' => true,
Expand All @@ -458,4 +481,69 @@ protected function internalGetCapabilities()

return $this->capabilities;
}

/**
* {@inheritDoc}
*
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$redis = $this->getRedisResource();
$metadata = [];

try {
$redisVersion = $this->resourceManager->getMajorVersion($this->resourceId);

// redis >= 2.8
// The command 'pttl' returns -2 if the item does not exist
// and -1 if the item has no associated expire
if (version_compare($redisVersion, '2.8', '>=')) {
$pttl = $redis->pttl($this->namespacePrefix . $normalizedKey);
if ($pttl <= -2) {
return false;
}
$metadata['ttl'] = ($pttl == -1) ? null : $pttl / 1000;

// redis >= 2.6
// The command 'pttl' returns -1 if the item does not exist or the item as no associated expire
} elseif (version_compare($redisVersion, '2.6', '>=')) {
$pttl = $redis->pttl($this->namespacePrefix . $normalizedKey);
if ($pttl <= -1) {
if (!$this->internalHasItem($normalizedKey)) {
return false;
}
$metadata['ttl'] = null;
} else {
$metadata['ttl'] = $pttl / 1000;
}

// redis >= 2
// The command 'pttl' is not supported but 'ttl'
// The command 'ttl' returns 0 if the item does not exist same as if the item is going to be expired
// NOTE: In case of ttl=0 we return false because the item is going to be expired in a very near future
// and then doesn't exist any more
} elseif (version_compare($redisVersion, '2', '>=')) {
$ttl = $redis->ttl($this->namespacePrefix . $normalizedKey);
if ($ttl <= -1) {
if (!$this->internalHasItem($normalizedKey)) {
return false;
}
$metadata['ttl'] = null;
} else {
$metadata['ttl'] = $ttl;
}

// redis < 2
// The commands 'pttl' and 'ttl' are not supported
// but item existence have to be checked
} elseif (!$this->internalHasItem($normalizedKey)) {
return false;
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}

return $metadata;
}
}
16 changes: 16 additions & 0 deletions test/Storage/Adapter/RedisTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,20 @@ public function testOptionsGetSetPassword()
$this->_options->setPassword($password);
$this->assertEquals($password, $this->_options->getPassword(), 'Password was set incorrectly using RedisOptions');
}

public function testTouchItem()
{
$key = 'key';

// no TTL
$this->_storage->getOptions()->setTtl(0);
$this->_storage->setItem($key, 'val');
$this->assertEquals(0, $this->_storage->getMetadata($key)['ttl']);

// touch with a specific TTL will add this TTL
$ttl = 1000;
$this->_storage->getOptions()->setTtl($ttl);
$this->assertTrue($this->_storage->touchItem($key));
$this->assertEquals($ttl, ceil($this->_storage->getMetadata($key)['ttl']));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean with the first line of setTtl was that you expect a specific TTL set (in this case none) and test against this value but the TTL will not be set by the test. You only knows that this will be the default but a default can change.

So please:

$key = 'key';

// no TTL
$this->_storage->getOptions()->setTtl(0);
$this->_storage->setItem($key, 'val');
$this->assertNull($this->_storage->getMetadata($key)['ttl']);

// touch with a specific TTL will add this TTL
$ttl = 1000;
$this->_storage->getOptions()->setTtl($ttl);
$this->assertTrue($this->_storage->touchItem($key));
$this->assertEquals($ttl, ceil($this->_storage->getMetadata($key)['ttl']));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
}