Skip to content

Commit

Permalink
Merge pull request #47 from josegonzalez/4-predis-support
Browse files Browse the repository at this point in the history
Add PredisEngine. Closes #4.
  • Loading branch information
josegonzalez authored Sep 15, 2016
2 parents 2d7b919 + 273f9cf commit 00404f0
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 19 deletions.
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
"league/event": "~2.0"
},
"require-dev": {
"squizlabs/php_codesniffer": "dev-master",
"pda/pheanstalk": "dev-master",
"iron-io/iron_mq": "dev-master",
"monolog/monolog": "*",
"pda/pheanstalk": "dev-master",
"phpmd/phpmd": "dev-master",
"predis/predis": "~1.0",
"satooshi/php-coveralls": "dev-master",
"phpmd/phpmd": "dev-master"
"squizlabs/php_codesniffer": "dev-master"
},
"suggest": {
"pda/pheanstalk": "Adds support for Beanstalk background workers",
Expand Down
6 changes: 3 additions & 3 deletions docs/supported-systems.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ Queuesadilla supports the following engine engines:
| `BeanstalkEngine` | **Stable** | BeanstalkD |
| `MemoryEngine` | **Stable** | In-memory |
| `MysqlEngine` | **Stable** | Backed by PDO/MySQL |
| `PdoEngine` | **Stable** | |
| `PostgresEngine` | **Stable** | Backed by PDO/Postgres |
| `RedisEngine` | **Stable** | Redis-based |
| `PredisEngine` | **Stable** | Redis-based, requires `nrk/predis` |
| `RedisEngine` | **Stable** | Redis-based, requires `ext-redis` |
| `SynchronousEngine` | **Stable** | Synchronous, in-memory |
| `NullEngine` | **Stable** | Always returns "true" for any operation |
| `IronEngine` | *Unstable* | Needs unit tests |
| `AzureEngine` | Planned | |
| `MemcachedEngine` | Planned | |
| `MongodbEngine` | Planned | |
| `PdoEngine` | Planned | |
| `PredisEngine` | Planned | |
| `RabbitMQEngine` | Planned | |
| `SqsEngine` | Planned | |
47 changes: 47 additions & 0 deletions src/josegonzalez/Queuesadilla/Engine/PredisEngine.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace josegonzalez\Queuesadilla\Engine;

use Exception;
use josegonzalez\Queuesadilla\Engine\RedisEngine;
use Predis\Client;
use Predis\Connection\ConnectionException;

class PredisEngine extends RedisEngine
{
protected $persistentEnabled = false;

/**
* {@inheritDoc}
*/
public function connect()
{
try {
$return = parent::connect();
} catch (Exception $e) {
return false;
}

return !($return === false);
}

protected function evalSha($scriptSha, $item)
{
return (bool)$this->connection()->evalSha(
$scriptSha,
3,
$item['queue'],
rand(),
$item['id']
);
}

protected function redisInstance()
{
return new Client([
'host' => $this->config('host'),
'port' => $this->config('port'),
'timeout' => (int)$this->config('timeout'),
]);
}
}
23 changes: 15 additions & 8 deletions src/josegonzalez/Queuesadilla/Engine/RedisEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

use josegonzalez\Queuesadilla\Engine\Base;
use Redis;
use RedisException;
use Exception;

class RedisEngine extends Base
{
protected $persistentEnabled = true;

protected $baseConfig = [
'database' => null,
'pass' => false,
Expand All @@ -25,7 +27,7 @@ public function connect()
{
$return = false;
$connectMethod = 'connect';
if ($this->config('persistent')) {
if ($this->config('persistent') && $this->persistentEnabled) {
$connectMethod = 'pconnect';
}

Expand All @@ -38,7 +40,7 @@ public function connect()
(int)$this->config('timeout')
);
}
} catch (RedisException $e) {
} catch (Exception $e) {
return false;
}

Expand Down Expand Up @@ -68,11 +70,7 @@ public function acknowledge($item)
return false;
}

return (bool)$this->connection()->evalSha(sha1($script), [
$item['queue'],
rand(),
$item['id'],
], 3);
return $this->evalSha(sha1($script), $item);
}

/**
Expand Down Expand Up @@ -152,6 +150,15 @@ protected function ensureRemoveScript()
return $this->connection()->script('load', $script);
}

protected function evalSha($scriptSha, $item)
{
return (bool)$this->connection()->evalSha($scriptSha, [
$item['queue'],
rand(),
$item['id'],
], 3);
}

protected function getRemoveScript()
{
$script = <<<EOF
Expand Down
233 changes: 233 additions & 0 deletions tests/josegonzalez/Queuesadilla/Engine/PredisEngineTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php

namespace josegonzalez\Queuesadilla\Engine;

use josegonzalez\Queuesadilla\Engine\PredisEngine;
use josegonzalez\Queuesadilla\FixtureData;
use josegonzalez\Queuesadilla\TestCase;
use Psr\Log\NullLogger;
use Exception as RedisException;

class PredisEngineTest extends TestCase
{
public function setUp()
{
$this->url = getenv('REDIS_URL');
$this->config = ['url' => $this->url];
$this->Logger = new NullLogger;
$this->engineClass = 'josegonzalez\Queuesadilla\Engine\PredisEngine';
$this->Engine = $this->mockEngine();
$this->Fixtures = new FixtureData;
$this->clearEngine();
}

public function tearDown()
{
$this->clearEngine();
unset($this->Engine);
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::__construct
*/
public function testConstruct()
{
$Engine = new PredisEngine($this->Logger, []);
$this->assertNotNull($Engine->connection());

$Engine = new PredisEngine($this->Logger, $this->url);
$this->assertNotNull($Engine->connection());

$Engine = new PredisEngine($this->Logger, $this->config);
$this->assertNotNull($Engine->connection());
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::connect
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::redisInstance
*/
public function testConnect()
{
$this->assertTrue($this->Engine->connect());

$config = $this->config;
$Engine = $this->mockEngine(null, $config);
$Engine->config('database', 1);
$this->assertTrue($Engine->connect());

$config = $this->config;
$Engine = $this->mockEngine(null, $config);
$Engine->config('persistent', false);
$this->assertTrue($Engine->connect());
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::connect
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::redisInstance
*/
public function testConnectAuth()
{
$this->markTestIncomplete(
'Predis does not return false on invalid connection'
);

$config = $this->config;
$Engine = $this->mockEngine(null, $config);
$Engine->config('pass', 'some_password');
$this->assertFalse($Engine->connect());
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::connect
*/
public function testConnectionException()
{
$Engine = $this->mockEngine(['redisInstance']);
$Engine->expects($this->once())
->method('redisInstance')
->will($this->throwException(new RedisException));

$this->assertFalse($Engine->connect());
}

/**
* @covers josegonzalez\Queuesadilla\Engine\Base::getJobClass
*/
public function testGetJobClass()
{
$this->assertEquals('\\josegonzalez\\Queuesadilla\\Job\\Base', $this->Engine->getJobClass());
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::acknowledge
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::evalSha
*/
public function testAcknowledge()
{
$this->assertFalse($this->Engine->acknowledge(null));
$this->assertFalse($this->Engine->acknowledge(false));
$this->assertFalse($this->Engine->acknowledge(1));
$this->assertFalse($this->Engine->acknowledge('string'));
$this->assertFalse($this->Engine->acknowledge(['key' => 'value']));
$this->assertFalse($this->Engine->acknowledge($this->Fixtures->default['first']));

$this->assertTrue($this->Engine->push($this->Fixtures->default['first']));
$this->assertTrue($this->Engine->push($this->Fixtures->other['third']));
$this->assertTrue($this->Engine->acknowledge($this->Fixtures->default['first']));
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::reject
*/
public function testReject()
{
$this->assertFalse($this->Engine->reject(null));
$this->assertFalse($this->Engine->reject(false));
$this->assertFalse($this->Engine->reject(1));
$this->assertFalse($this->Engine->reject('string'));
$this->assertFalse($this->Engine->reject(['key' => 'value']));
$this->assertFalse($this->Engine->reject($this->Fixtures->default['first']));

$this->assertTrue($this->Engine->push($this->Fixtures->default['first']));
$this->assertTrue($this->Engine->push($this->Fixtures->other['third']));
$this->assertTrue($this->Engine->reject($this->Fixtures->default['first']));
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::pop
*/
public function testPop()
{
$this->assertNull($this->Engine->pop('default'));
$this->assertTrue($this->Engine->push($this->Fixtures->default['first'], 'default'));
$this->assertEquals($this->Fixtures->default['first'], $this->Engine->pop('default'));
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::push
*/
public function testPush()
{
$this->assertTrue($this->Engine->push($this->Fixtures->default['first'], 'default'));
$this->assertTrue($this->Engine->push($this->Fixtures->default['second'], [
'delay' => 30,
]));
$this->assertTrue($this->Engine->push($this->Fixtures->other['third'], [
'expires_in' => 1,
]));
$this->assertTrue($this->Engine->push($this->Fixtures->default['fourth'], 'default'));

sleep(2);

$pop1 = $this->Engine->pop();
$pop2 = $this->Engine->pop();
$pop3 = $this->Engine->pop();
$pop4 = $this->Engine->pop();

$this->assertNull($pop1['class']);
$this->assertEmpty($pop1['args']);

$this->markTestIncomplete(
'PredisEngine does not yet implement delay or expires_in (tbd sorted sets)'
);

$this->assertEquals('yet_another_function', $pop2['class']);
$this->assertNull($pop3);
$this->assertNull($pop4);
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::pop
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::release
*/
public function testRelease()
{
$this->assertTrue($this->Engine->push($this->Fixtures->default['first'], 'default'));
$this->assertEquals($this->Fixtures->default['first'], $this->Engine->pop('default'));

$this->assertFalse($this->Engine->release(null, 'default'));

$this->assertEquals(1, $this->Engine->release($this->Fixtures->default['second'], 'default'));
$this->assertEquals($this->Fixtures->default['second'], $this->Engine->pop('default'));
}

/**
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::queues
* @covers josegonzalez\Queuesadilla\Engine\PredisEngine::requireQueue
*/
public function testQueues()
{
$this->assertEquals([], $this->Engine->queues());
$this->Engine->push($this->Fixtures->default['first']);
$this->assertEquals(['default'], $this->Engine->queues());

$this->Engine->push($this->Fixtures->other['second'], ['queue' => 'other']);
$queues = $this->Engine->queues();
sort($queues);
$this->assertEquals(['default', 'other'], $queues);

$this->Engine->pop();
$this->Engine->pop();
$queues = $this->Engine->queues();
sort($queues);
$this->assertEquals(['default', 'other'], $queues);
}

protected function clearEngine()
{
$this->Engine->connection()->flushdb();
$this->Engine->connection()->script('flush');
}

protected function mockEngine($methods = null, $config = null)
{
if ($config === null) {
$config = $this->config;
}

return $this->getMockBuilder($this->engineClass)
->setMethods($methods)
->setConstructorArgs([$this->Logger, $config])
->getMock();
}
}
Loading

0 comments on commit 00404f0

Please # to comment.