-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a cache to store UUID -> Fedora 4 path mappings (#41)
* First draft of a cache for UUID -> F4 paths in transactions. * Update interface and implementation methods * New tests for KeyCache * Fixing up Redis * Correct response is +PONG * PSR-2 compliance and function mocking case-sensitivity.
- Loading branch information
Showing
6 changed files
with
358 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
/** | ||
* @file | ||
* Part of the Chullo service | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Islandora\Chullo\KeyCache; | ||
|
||
/** | ||
* Interface for key -> value service to store UUID -> Fedora Paths | ||
* not yet indexed into the triplestore (ie. in a transaction) | ||
* @author Jared Whiklo <[email protected]> | ||
* @since 2016-04-12 | ||
*/ | ||
interface IUuidCache | ||
{ | ||
|
||
/** | ||
* Sets the value for the provided key. | ||
* | ||
* @var string $txID | ||
* The transaction ID. | ||
* @var string $uuid | ||
* The UUID. | ||
* @var string $path | ||
* The Fedora path. | ||
* @var int $expire | ||
* The number of seconds from now to expire. Default to an hour. | ||
* @return bool | ||
* Was key set successful. | ||
*/ | ||
public function set($txID, $uuid, $path, $expire = 3600); | ||
|
||
/** | ||
* Gets the Fedora path for the provided UUID. | ||
* | ||
* @var string $txID | ||
* The transaction ID. | ||
* @var string $uuid | ||
* The UUID | ||
* @return string | ||
* The UUID corresponding to the path in the transaction or NULL | ||
*/ | ||
public function getByUuid($txID, $uuid); | ||
|
||
/** | ||
* Gets the UUID for the provided path. | ||
* | ||
* @var string $txID | ||
* The transaction ID. | ||
* @var string $path | ||
* The key. | ||
* @return string | ||
* The UUID corresponding to the path in the transaction or NULL | ||
*/ | ||
public function getByPath($txID, $path); | ||
|
||
/** | ||
* Delete any pairs related to transaction ID. | ||
* | ||
* @var string $txID | ||
* The transaction ID. | ||
* @return void | ||
*/ | ||
public function delete($txID); | ||
|
||
/** | ||
* Set/reset the transaction to expire in $seconds seconds. | ||
* | ||
* @var string $txID | ||
* The transaction ID. | ||
* @var int $seconds | ||
* The number of seconds from now to expire. | ||
* @return bool | ||
* Whether the expiry was set or not. | ||
*/ | ||
public function extend($txID, $seconds); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
<?php | ||
/** | ||
* @file | ||
* This is part of Chullo service. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Islandora\Chullo\KeyCache; | ||
|
||
/** | ||
* Implementation of the IKeyCache using Redis and phpredis. | ||
* | ||
* @author Jared Whiklo <[email protected]> | ||
* @since 2016-04-12 | ||
*/ | ||
class RedisKeyCache implements IUuidCache | ||
{ | ||
|
||
/** | ||
* @var Redis $redis | ||
* The Redis object. | ||
*/ | ||
protected $redis; | ||
/** | ||
* @var bool $connected | ||
* Are we connected to the Redis server yet. | ||
*/ | ||
private $connected = false; | ||
/** | ||
* @var string $host | ||
* The hostname of the redis server. | ||
*/ | ||
private $host; | ||
/** | ||
* @var int $port | ||
* The port of the redis server. | ||
*/ | ||
private $port; | ||
|
||
/** | ||
* @param \Redis $instance | ||
* An instance of the Redis client | ||
*/ | ||
public function __construct(\Redis $instance, $host, $port) | ||
{ | ||
$this->redis = $instance; | ||
$this->redis->host = $host; | ||
$this->redis->port = $port; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
public function __destruct() | ||
{ | ||
$this->redis->close(); | ||
} | ||
|
||
/** | ||
* Create using a hostname and port. | ||
* | ||
* @param $host | ||
* The hostname of the redis server | ||
* @param $port | ||
* The port of the redis server | ||
* @return RedisKeyCache | ||
*/ | ||
public static function create($host = "localhost", $port = 6379) | ||
{ | ||
$redis = new \Redis(); | ||
$redis->connect($host, $port); | ||
return new RedisKeyCache($redis, $host, $port); | ||
} | ||
|
||
/** | ||
* Check if we are connected to the Redis server. | ||
* | ||
* @return bool Whether we are connected. | ||
*/ | ||
private function isConnected() | ||
{ | ||
try { | ||
$result = $this->redis->ping(); | ||
$connected = ($result == "+PONG"); | ||
} catch (RedisException $e) { | ||
error_log("RedisKeyCache: Error communicating with Redis server - " . $e->getMessage()); | ||
# Try connecting once more. | ||
$this->redis->connect($this->host, $this->port); | ||
$result = $this->redis->ping(); | ||
$connected = ($result == "+PONG"); | ||
} | ||
return $connected; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function set($txID, $uuid, $path, $expire = 3600) | ||
{ | ||
if ($this->isConnected()) { | ||
$result = $this->redis->hSetNx($txID, $uuid, $path); | ||
if ($result) { | ||
$this->redis->expire($txID, $expire); | ||
} | ||
return $result; | ||
} | ||
/** | ||
* TODO: return exceptions?? | ||
*/ | ||
return false; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getByUuid($txId, $uuid) | ||
{ | ||
if ($this->isConnected()) { | ||
return $this->redis->hget($txId, $uuid); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getByPath($txId, $path) | ||
{ | ||
if ($this->isConnected()) { | ||
$hashes = $this->redis->hgetall($txId); | ||
if ($hashes !== false) { | ||
$flipped = array_flip($hashes); | ||
if (array_key_exists($path, $flipped)) { | ||
return $flipped[$path]; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function extend($txId, $expire = 3600) | ||
{ | ||
if ($this->isConnected()) { | ||
return $this->redis->expire($txId, $expire); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function delete($txId) | ||
{ | ||
if ($this->isConnected()) { | ||
return $this->redis->del($txId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?php | ||
|
||
namespace Islandora\Chullo\KeyCache; | ||
|
||
use Islandora\Chullo\Uuid\UuidGenerator; | ||
use Islandora\Chullo\KeyCache\RedisKeyCache; | ||
use Mockery\Adapter\Phpunit\MockeryTestCase; | ||
|
||
class UuidCacheTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
|
||
protected $redis; | ||
|
||
protected $uuid_gen; | ||
|
||
public function setUp() | ||
{ | ||
$this->redis = \Mockery::mock('\Redis'); | ||
$this->redis->shouldReceive('expire')->andReturn(1); | ||
$this->redis->shouldReceive('ping')->andReturn("+PONG"); | ||
$this->redis->shouldReceive('close')->andReturn(null); | ||
|
||
$this->uuid_gen = new UuidGenerator(); | ||
} | ||
|
||
/** | ||
* @covers RedisKeyCache::set | ||
* @group UnitTest | ||
*/ | ||
public function testAddUuidPair() | ||
{ | ||
// Need both to account for OS X and Linux differences. | ||
$this->redis->shouldReceive('hSetNx')->andReturn(1); | ||
$this->redis->shouldReceive('hsetnx')->andReturn(1); | ||
$redis_cache = new RedisKeyCache($this->redis, 'localhost', 6379); | ||
|
||
$transId = "tx:" . $this->uuid_gen->generateV4(); | ||
$uuid = $this->uuid_gen->generateV4(); | ||
$path = "http://localhost:8080/fcrepo/rest/object1"; | ||
|
||
$this->assertEquals(1, $redis_cache->set($transId, $uuid, $path), "Error setting uuid->path hash"); | ||
} | ||
|
||
/** | ||
* @covers RedisKeyCache::getByUuid | ||
* @group UnitTest | ||
*/ | ||
public function testGetByUuid() | ||
{ | ||
$txID = $transId = "tx:" . $this->uuid_gen->generateV4(); | ||
$uuid = $this->uuid_gen->generateV4(); | ||
$path = "http://localhost:8080/fcrepo/rest/object1"; | ||
|
||
// Need both to account for OS X and Linux differences. | ||
$this->redis->shouldReceive('hGet')->with($txID, $uuid)->andReturn($path); | ||
$this->redis->shouldReceive('hget')->with($txID, $uuid)->andReturn($path); | ||
|
||
$redis_cache = new RedisKeyCache($this->redis, 'localhost', 6379); | ||
|
||
$this->assertEquals($path, $redis_cache->getByUuid($txID, $uuid), "Error getting by Uuid"); | ||
} | ||
|
||
/** | ||
* @covers RedisKeyCache::getByPath | ||
* @group UnitTest | ||
*/ | ||
public function testGetByPath() | ||
{ | ||
$txID = $transId = "tx:" . $this->uuid_gen->generateV4(); | ||
|
||
$uuid1 = $this->uuid_gen->generateV4(); | ||
$path1 = "http://localhost:8080/fcrepo/rest/object1"; | ||
|
||
$uuid2 = $this->uuid_gen->generateV4(); | ||
$path2 = "http://localhost:8080/fcrepo/rest/object2"; | ||
|
||
$hashes = array( | ||
$uuid1 => $path1, | ||
$uuid2 => $path2, | ||
); | ||
|
||
// Need both to account for OS X and Linux differences. | ||
$this->redis->shouldReceive('hGetAll')->with($txID)->andReturn($hashes); | ||
$this->redis->shouldReceive('hgetall')->with($txID)->andReturn($hashes); | ||
|
||
$redis_cache = new RedisKeyCache($this->redis, 'localhost', 6379); | ||
|
||
$this->assertEquals($uuid2, $redis_cache->getByPath($txID, $path2), "Error getting by Path"); | ||
} | ||
|
||
/** | ||
* @covers RedisKeyCache::delete | ||
* @group UnitTest | ||
*/ | ||
public function testDelete() | ||
{ | ||
$txID = $transId = "tx:" . $this->uuid_gen->generateV4(); | ||
|
||
$this->redis->shouldReceive('del')->with($txID)->andReturn(1); | ||
|
||
$redis_cache = new RedisKeyCache($this->redis, 'localhost', 6379); | ||
|
||
$this->assertEquals(1, $redis_cache->delete($txID), "Error deleting transaction ID."); | ||
} | ||
|
||
public function tearDown() | ||
{ | ||
\Mockery::close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters