Merge branch 'master' of git://github.com/chrisboulton/php-resque into blocking-list-pop

Conflicts:
	lib/Resque.php
	lib/Resque/RedisCluster.php
	lib/Resque/Worker.php
This commit is contained in:
Ruud Kamphuis 2013-03-11 22:38:30 +01:00
commit 5687c8fe82
41 changed files with 455 additions and 767 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor/

View File

@ -1,5 +1,12 @@
language: php
php:
- 5.2
- 5.3
- 5.4
env:
- REDIS_STANDALONE=0
- REDIS_STANDALONE=1
before_script:
- sh -c "if [ $REDIS_STANDALONE -eq 0 ]; then wget https://github.com/nicolasff/phpredis/archive/2.2.2.zip -O php-redis.zip && unzip php-redis.zip; fi"
- sh -c "if [ $REDIS_STANDALONE -eq 0 ]; then cd phpredis-2.2.2/ && phpize && ./configure && make && make install; fi"
- sh -c "if [ $REDIS_STANDALONE -eq 0 ]; then echo \"extension=redis.so\" >> `php --ini | grep \"Loaded Configuration\" | sed -e \"s|.*:\s*||\"`; fi"
- composer install

View File

@ -1,4 +1,35 @@
## 1.2 (Unreleased) ##
## 1.3 (2013-??-??) - Current Master ##
**Note:** This release introduces backwards incompatible changes with all previous versions of php-resque. Please see below for details.
### Redisent (Redis Library) Replaced with Credis
Redisent has always been the Redis backend for php-resque because of its lightweight nature. Unfortunately, Redisent is largely unmaintained.
[Credis](http://example.com/) is a fork of Redisent, which among other improvements will automatically use the [phpredis](https://github.com/nicolasff/phpredis) native PHP extension if it is available. (you want this for speed, trust me)
php-resque now utilizes Credis for all Redis based operations. Credis automatically required and installed as a Composer dependency.
### Composer Support
Composer support has been improved and is now the recommended method for including php-resque in your project. Details on Composer support can be found in the Getting Started section of the readme.
### Other Improvements/Changes
* **COMPATIBILITY BREAKING**: The bundled worker manager `resque.php` has been moved to `bin/resque`, and is available as `vendor/bin/resque` when php-resque is installed as a Composer package.
* Restructure tests and test bootstrapping. Autoload tests via Composer (install test dependencies with `composer install --dev`)
* Add `SETEX` to list of commands which supply a key as the first argument in Redisent (danhunsaker)
* Fix an issue where a lost connection to Redis could cause an infinite loop (atorres757)
* Add a helper method to `Resque_Redis` to remove the namespace applied to Redis keys (tonypiper)
## 1.2 (2012-10-13) ##
**Note:** This release is largely backwards compatible with php-resque 1.1. The next release will introduce backwards incompatible changes (moving from Redisent to Credis), and will drop compatibility with PHP 5.2.
* Allow alternate redis database to be selected when calling setBackend by supplying a second argument (patrickbajao)
* Use `require_once` when including php-resque after the app has been included in the sample resque.php to prevent include conflicts (andrewjshults)
@ -10,7 +41,17 @@
* Fix lost jobs when there is more than one worker process started by the same parent process (salimane)
* Move include for resque before APP_INCLUDE is loaded in, so that way resque is available for the app
* Avoid working with dirty worker IDs (salimane)
* Allow UNIX socket to be passed to Resque when connecting to Redis (pedroarnal)
* Fix typographical errors in PHP docblocks (chaitanyakuber)
* Set the queue name on job instances when jobs are executed (chaitanyakuber)
* Fix and add tests for Resque_Event::stopListening (ebernhardson)
* Documentation cleanup (maetl)
* Pass queue name to afterEvent callback
* Only declare RedisException if it doesn't already exist (Matt Heath)
* Add support for Composer
* Fix missing and incorrect paths for Resque and Resque_Job_Status classes in demo (jjfrey)
* Disable autoload for the RedisException class_exists call (scragg0x)
* General tidy up of comments and files/folders
## 1.1 (2011-03-27) ##

View File

@ -1,4 +1,4 @@
(c) 2010 Chris Boulton <chris.boulton@interspire.com>
(c) Chris Boulton <chris@bigcommerce.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -37,8 +37,34 @@ pre and post jobs
## Requirements ##
* PHP 5.2+
* PHP 5.3+
* Redis 2.2+
* Optional but Recommended: Composer
## Getting Started ##
The easiest way to work with php-resque is when it's installed as a
Composer package inside your project. Composer isn't strictly
required, but makes life a lot easier.
If you're not familiar with Composer, please see <http://getcomposer.org/>.
1. Add php-resque to your application's composer.json.
{
...
"require": {
"chrisboulton/php-resque": "1.2.x"
},
...
}
2. Run `composer install`.
3. If you haven't already, add the Composer autoload to your project's
initialization file. (example)
require 'vendor/autoload.php';
## Jobs ##
@ -46,8 +72,6 @@ pre and post jobs
Jobs are queued as follows:
require_once 'lib/Resque.php';
// Required if redis is located elsewhere
Resque::setBackend('localhost:6379');
@ -136,8 +160,9 @@ class.
Workers work in the exact same way as the Ruby workers. For complete
documentation on workers, see the original documentation.
A basic "up-and-running" resque.php file is included that sets up a
running worker environment is included in the root directory.
A basic "up-and-running" `bin/resque` file is included that sets up a
running worker environment is included. (`vendor/bin/resque` when installed
via Composer)
The exception to the similarities with the Ruby version of resque is
how a worker is initially setup. To work under all environments,
@ -146,13 +171,17 @@ not having a single environment such as with Ruby, the PHP port makes
To start a worker, it's very similar to the Ruby version:
$ QUEUE=file_serve php resque.php
$ QUEUE=file_serve php bin/resque
It's your responsibility to tell the worker which file to include to get
your application underway. You do so by setting the `APP_INCLUDE` environment
variable:
$ QUEUE=file_serve APP_INCLUDE=../application/init.php php resque.php
$ QUEUE=file_serve APP_INCLUDE=../application/init.php php bin/resque
*Pro tip: Using Composer? More than likely, you don't need to worry about
`APP_INCLUDE`, because hopefully Composer is responsible for autoloading
your application too!*
Getting your application underway also includes telling the worker your job
classes, by means of either an autoloader or including them.
@ -163,8 +192,8 @@ The port supports the same environment variables for logging to STDOUT.
Setting `VERBOSE` will print basic debugging information and `VVERBOSE`
will print detailed information.
$ VERBOSE QUEUE=file_serve php resque.php
$ VVERBOSE QUEUE=file_serve php resque.php
$ VERBOSE QUEUE=file_serve bin/resque
$ VVERBOSE QUEUE=file_serve bin/resque
### Priorities and Queue Lists ###
@ -175,7 +204,7 @@ checked in.
As per the original example:
$ QUEUE=file_serve,warm_cache php resque.php
$ QUEUE=file_serve,warm_cache bin/resque
The `file_serve` queue will always be checked for new jobs on each
iteration before the `warm_cache` queue is checked.
@ -185,14 +214,14 @@ iteration before the `warm_cache` queue is checked.
All queues are supported in the same manner and processed in alphabetical
order:
$ QUEUE=* php resque.php
$ QUEUE=* bin/resque
### Running Multiple Workers ###
Multiple workers ca be launched and automatically worked by supplying
the `COUNT` environment variable:
$ COUNT=5 php resque.php
$ COUNT=5 bin/resque
### Forking ###
@ -257,7 +286,7 @@ It is up to your application to register event listeners. When enqueuing events
in your application, it should be as easy as making sure php-resque is loaded
and calling `Resque_Event::listen`.
When running workers, if you run workers via the default `resque.php` script,
When running workers, if you run workers via the default `bin/resque` script,
your `APP_INCLUDE` script should initialize and register any listeners required
for operation. If you have rolled your own worker manager, then it is again your
responsibility to register listeners.
@ -329,3 +358,16 @@ Called after a job has been queued using the `Resque::enqueue` method. Arguments
* KevBurnsJr
* jmathai
* dceballos
* patrickbajao
* andrewjshults
* warezthebeef
* d11wtq
* hlegius
* salimane
* humancopy
* pedroarnal
* chaitanyakuber
* maetl
* Matt Heath
* jjfrey
* scragg0x

View File

@ -1,8 +0,0 @@
* Write tests for:
* `Resque_Failure`
* `Resque_Failure_Redis`
* Change to preforking worker model
* Clean up /bin and /demo
* Add a way to store arbitrary text in job statuses (for things like progress
indicators)
* Write plugin for Ruby resque that calls setUp and tearDown methods

View File

@ -1 +1,106 @@
#!/bin/sh
#!/usr/bin/env php
<?php
// Find and initialize Composer
$files = array(
__DIR__ . '/../../vendor/autoload.php',
__DIR__ . '/../../../autoload.php',
__DIR__ . '/../../../../autoload.php',
__DIR__ . '/../vendor/autoload.php',
);
$found = false;
foreach ($files as $file) {
if (file_exists($file)) {
require_once $file;
break;
}
}
if (!class_exists('Composer\Autoload\ClassLoader', false)) {
die(
'You need to set up the project dependencies using the following commands:' . PHP_EOL .
'curl -s http://getcomposer.org/installer | php' . PHP_EOL .
'php composer.phar install' . PHP_EOL
);
}
$QUEUE = getenv('QUEUE');
if(empty($QUEUE)) {
die("Set QUEUE env var containing the list of queues to work.\n");
}
$REDIS_BACKEND = getenv('REDIS_BACKEND');
$REDIS_BACKEND_DB = getenv('REDIS_BACKEND_DB');
if(!empty($REDIS_BACKEND)) {
if (empty($REDIS_BACKEND_DB))
Resque::setBackend($REDIS_BACKEND);
else
Resque::setBackend($REDIS_BACKEND, $REDIS_BACKEND_DB);
}
$logLevel = 0;
$LOGGING = getenv('LOGGING');
$VERBOSE = getenv('VERBOSE');
$VVERBOSE = getenv('VVERBOSE');
if(!empty($LOGGING) || !empty($VERBOSE)) {
$logLevel = Resque_Worker::LOG_NORMAL;
}
else if(!empty($VVERBOSE)) {
$logLevel = Resque_Worker::LOG_VERBOSE;
}
$APP_INCLUDE = getenv('APP_INCLUDE');
if($APP_INCLUDE) {
if(!file_exists($APP_INCLUDE)) {
die('APP_INCLUDE ('.$APP_INCLUDE.") does not exist.\n");
}
require_once $APP_INCLUDE;
}
$interval = 5;
$INTERVAL = getenv('INTERVAL');
if(!empty($INTERVAL)) {
$interval = $INTERVAL;
}
$count = 1;
$COUNT = getenv('COUNT');
if(!empty($COUNT) && $COUNT > 1) {
$count = $COUNT;
}
if($count > 1) {
for($i = 0; $i < $count; ++$i) {
$pid = Resque::fork();
if($pid == -1) {
die("Could not fork worker ".$i."\n");
}
// Child, start the worker
else if(!$pid) {
$queues = explode(',', $QUEUE);
$worker = new Resque_Worker($queues);
$worker->logLevel = $logLevel;
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
$worker->work($interval);
break;
}
}
}
// Start a single worker
else {
$queues = explode(',', $QUEUE);
$worker = new Resque_Worker($queues);
$worker->logLevel = $logLevel;
$PIDFILE = getenv('PIDFILE');
if ($PIDFILE) {
file_put_contents($PIDFILE, getmypid()) or
die('Could not write PID information to ' . $PIDFILE);
}
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
$worker->work($interval);
}
?>

View File

@ -11,9 +11,26 @@
"email": "chris@bigcommerce.com"
}
],
"repositories": [
{
"type": "vcs",
"url": "https://github.com/chrisboulton/credis"
}
],
"require": {
"php": ">=5.3.0"
"php": ">=5.3.0",
"colinmollenhour/credis": "dev-master"
},
"suggest": {
"ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.",
"ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available."
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"bin": [
"bin/resque"
],
"autoload": {
"psr-0": {
"Resque": "lib"

53
composer.lock generated Normal file
View File

@ -0,0 +1,53 @@
{
"hash": "d37909ad0ffc11ed4d1e67dcaabe00b2",
"packages": [
{
"name": "colinmollenhour/credis",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/chrisboulton/credis",
"reference": "62c73dd16e08069e3fd8f224cb4a5ddd73db8095"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chrisboulton/credis/zipball/62c73dd16e08069e3fd8f224cb4a5ddd73db8095",
"reference": "62c73dd16e08069e3fd8f224cb4a5ddd73db8095",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2013-01-12 10:15:31",
"type": "library",
"autoload": {
"classmap": [
"Client.php",
"Cluster.php"
]
},
"license": [
"MIT"
],
"authors": [
{
"name": "Colin Mollenhour",
"email": "colin@mollenhour.com"
}
],
"description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
"homepage": "https://github.com/colinmollenhour/credis",
"support": {
"source": "https://github.com/chrisboulton/credis/tree/master"
}
}
],
"packages-dev": null,
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": {
"colinmollenhour/credis": 20
}
}

View File

@ -3,8 +3,8 @@ if(empty($argv[1])) {
die('Specify the ID of a job to monitor the status of.');
}
require '../lib/Resque/Job/Status.php';
require '../lib/Resque.php';
require __DIR__ . '/init.php';
date_default_timezone_set('GMT');
Resque::setBackend('127.0.0.1:6379');

25
demo/init.php Normal file
View File

@ -0,0 +1,25 @@
<?php
// Find and initialize Composer
// NOTE: You should NOT use this when developing against php-resque.
// The autoload code below is specifically for this demo.
$files = array(
__DIR__ . '/../../vendor/autoload.php',
__DIR__ . '/../../../../autoload.php',
__DIR__ . '/../vendor/autoload.php',
);
$found = false;
foreach ($files as $file) {
if (file_exists($file)) {
require_once $file;
break;
}
}
if (!class_exists('Composer\Autoload\ClassLoader', false)) {
die(
'You need to set up the project dependencies using the following commands:' . PHP_EOL .
'curl -s http://getcomposer.org/installer | php' . PHP_EOL .
'php composer.phar install' . PHP_EOL
);
}

View File

@ -3,7 +3,7 @@ if(empty($argv[1])) {
die('Specify the name of a job to add. e.g, php queue.php PHP_Job');
}
require '../lib/Resque.php';
require __DIR__ . '/init.php';
date_default_timezone_set('GMT');
Resque::setBackend('127.0.0.1:6379');

View File

@ -4,5 +4,5 @@ require 'bad_job.php';
require 'job.php';
require 'php_error_job.php';
require '../resque.php';
require '../bin/resque';
?>

View File

@ -9,7 +9,7 @@
check process resque_worker_[QUEUE]
with pidfile /var/run/resque/worker_[QUEUE].pid
start program = "/bin/sh -c 'APP_INCLUDE=[APP_INCLUDE] QUEUE=[QUEUE] VERBOSE=1 PIDFILE=/var/run/resque/worker_[QUEUE].pid nohup php -f [PATH/TO/RESQUE]/resque.php > /var/log/resque/worker_[QUEUE].log &'" as uid [UID] and gid [GID]
start program = "/bin/sh -c 'APP_INCLUDE=[APP_INCLUDE] QUEUE=[QUEUE] VERBOSE=1 PIDFILE=/var/run/resque/worker_[QUEUE].pid nohup php -f [PATH/TO/RESQUE]/bin/resque > /var/log/resque/worker_[QUEUE].log &'" as uid [UID] and gid [GID]
stop program = "/bin/sh -c 'kill -s QUIT `cat /var/run/resque/worker_[QUEUE].pid` && rm -f /var/run/resque/worker_[QUEUE].pid; exit 0;'"
if totalmem is greater than 300 MB for 10 cycles then restart # eating up memory?
group resque_workers

View File

@ -1,22 +0,0 @@
Copyright (c) 2009 Justin Poliey <jdp34@njit.edu>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,67 +0,0 @@
# Redisent
Redisent is a simple, no-nonsense interface to the [Redis](http://code.google.com/p/redis/) key-value store for modest developers.
Due to the way it is implemented, it is flexible and tolerant of changes to the Redis protocol.
## Getting to work
If you're at all familiar with the Redis protocol and PHP objects, you've already mastered Redisent.
All Redisent does is map the Redis protocol to a PHP object, abstract away the nitty-gritty, and make the return values PHP compatible.
require 'redisent.php';
$redis = new Redisent('localhost');
$redis->set('awesome', 'absolutely');
echo sprintf('Is Redisent awesome? %s.\n', $redis->get('awesome'));
You use the exact same command names, and the exact same argument order. **How wonderful.** How about a more complex example?
require 'redisent.php';
$redis = new Redisent('localhost');
$redis->rpush('particles', 'proton');
$redis->rpush('particles', 'electron');
$redis->rpush('particles', 'neutron');
$particles = $redis->lrange('particles', 0, -1);
$particle_count = $redis->llen('particles');
echo "<p>The {$particle_count} particles that make up atoms are:</p>";
echo "<ul>";
foreach ($particles as $particle) {
echo "<li>{$particle}</li>";
}
echo "</ul>";
Be aware that Redis error responses will be wrapped in a RedisException class and thrown, so do be sure to use proper coding techniques.
## Clustering your servers
Redisent also includes a way for developers to fully utilize the scalability of Redis with multiple servers and [consistent hashing](http://en.wikipedia.org/wiki/Consistent_hashing).
Using the RedisentCluster class, you can use Redisent the same way, except that keys will be hashed across multiple servers.
Here is how to set up a cluster:
include 'redisent_cluster.php';
$cluster = new RedisentCluster(array(
array('host' => '127.0.0.1', 'port' => 6379),
array('host' => '127.0.0.1', 'port' => 6380)
));
You can then use Redisent the way you normally would, i.e., `$cluster->set('key', 'value')` or `$cluster->lrange('particles', 0, -1)`.
But what about when you need to use commands that are server specific and do not operate on keys? You can use routing, with the `RedisentCluster::to` method.
To use routing, you need to assign a server an alias in the constructor of the Redis cluster. Aliases are not required on all servers, just the ones you want to be able to access directly.
include 'redisent_cluster.php';
$cluster = new RedisentCluster(array(
'alpha' => array('host' => '127.0.0.1', 'port' => 6379),
array('host' => '127.0.0.1', 'port' => 6380)
));
Now there is an alias of the server running on 127.0.0.1:6379 called **alpha**, and can be interacted with like this:
// get server info
$cluster->to('alpha')->info();
Now you have complete programatic control over your Redis servers.
## About
&copy; 2009 [Justin Poliey](http://justinpoliey.com)

View File

@ -1,150 +0,0 @@
<?php
/**
* Redisent, a Redis interface for the modest
* @author Justin Poliey <jdp34@njit.edu>
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Redisent
*/
define('CRLF', sprintf('%s%s', chr(13), chr(10)));
/**
* Wraps native Redis errors in friendlier PHP exceptions
* Only declared if class doesn't already exist to ensure compatibility with php-redis
*/
if (! class_exists('RedisException')) {
class RedisException extends Exception {
}
}
/**
* Redisent, a Redis interface for the modest among us
*/
class Redisent {
/**
* Socket connection to the Redis server
* @var resource
* @access private
*/
private $__sock;
/**
* Host of the Redis server
* @var string
* @access public
*/
public $host;
/**
* Port on which the Redis server is running
* @var integer
* @access public
*/
public $port;
/**
* Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
* @param string $host The hostname of the Redis server
* @param integer $port The port number of the Redis server
*/
function __construct($host, $port = 6379) {
$this->host = $host;
$this->port = $port;
$this->establishConnection();
}
function establishConnection() {
$this->__sock = fsockopen($this->host, $this->port, $errno, $errstr);
if (!$this->__sock) {
throw new Exception("{$errno} - {$errstr}");
}
}
function __destruct() {
fclose($this->__sock);
}
function __call($name, $args) {
/* Build the Redis unified protocol command */
array_unshift($args, strtoupper($name));
$command = sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array($this, 'formatArgument'), $args), CRLF), CRLF);
/* Open a Redis connection and execute the command */
for ($written = 0; $written < strlen($command); $written += $fwrite) {
$fwrite = fwrite($this->__sock, substr($command, $written));
if ($fwrite === FALSE) {
throw new Exception('Failed to write entire command to stream');
}
}
/* Parse the response based on the reply identifier */
$reply = trim(fgets($this->__sock, 512));
switch (substr($reply, 0, 1)) {
/* Error reply */
case '-':
throw new RedisException(substr(trim($reply), 4));
break;
/* Inline reply */
case '+':
$response = substr(trim($reply), 1);
break;
/* Bulk reply */
case '$':
$response = null;
if ($reply == '$-1') {
break;
}
$read = 0;
$size = substr($reply, 1);
do {
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
$response .= fread($this->__sock, $block_size);
$read += $block_size;
} while ($read < $size);
fread($this->__sock, 2); /* discard crlf */
break;
/* Multi-bulk reply */
case '*':
$count = substr($reply, 1);
if ($count == '-1') {
return null;
}
$response = array();
for ($i = 0; $i < $count; $i++) {
$bulk_head = trim(fgets($this->__sock, 512));
$size = substr($bulk_head, 1);
if ($size == '-1') {
$response[] = null;
}
else {
$read = 0;
$block = "";
do {
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
$block .= fread($this->__sock, $block_size);
$read += $block_size;
} while ($read < $size);
fread($this->__sock, 2); /* discard crlf */
$response[] = $block;
}
}
break;
/* Integer reply */
case ':':
$response = intval(substr(trim($reply), 1));
break;
default:
throw new RedisException("invalid server response: {$reply}");
break;
}
/* Party on */
return $response;
}
private function formatArgument($arg) {
return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
}
}

View File

@ -1,138 +0,0 @@
<?php
/**
* Redisent, a Redis interface for the modest
* @author Justin Poliey <jdp34@njit.edu>
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Redisent
*/
require_once dirname(__FILE__) . '/Redisent.php';
/**
* A generalized Redisent interface for a cluster of Redis servers
*/
class RedisentCluster {
/**
* Collection of Redisent objects attached to Redis servers
* @var array
* @access private
*/
private $redisents;
/**
* Aliases of Redisent objects attached to Redis servers, used to route commands to specific servers
* @see RedisentCluster::to
* @var array
* @access private
*/
private $aliases;
/**
* Hash ring of Redis server nodes
* @var array
* @access private
*/
private $ring;
/**
* Individual nodes of pointers to Redis servers on the hash ring
* @var array
* @access private
*/
private $nodes;
/**
* Number of replicas of each node to make around the hash ring
* @var integer
* @access private
*/
private $replicas = 128;
/**
* The commands that are not subject to hashing
* @var array
* @access private
*/
private $dont_hash = array(
'RANDOMKEY', 'DBSIZE',
'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL',
'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN',
'INFO', 'MONITOR', 'SLAVEOF'
);
/**
* Creates a Redisent interface to a cluster of Redis servers
* @param array $servers The Redis servers in the cluster. Each server should be in the format array('host' => hostname, 'port' => port)
*/
function __construct($servers) {
$this->ring = array();
$this->aliases = array();
foreach ($servers as $alias => $server) {
$this->redisents[] = new Redisent($server['host'], $server['port']);
if (is_string($alias)) {
$this->aliases[$alias] = $this->redisents[count($this->redisents)-1];
}
for ($replica = 1; $replica <= $this->replicas; $replica++) {
$this->ring[crc32($server['host'].':'.$server['port'].'-'.$replica)] = $this->redisents[count($this->redisents)-1];
}
}
ksort($this->ring, SORT_NUMERIC);
$this->nodes = array_keys($this->ring);
}
/**
* Routes a command to a specific Redis server aliased by {$alias}.
* @param string $alias The alias of the Redis server
* @return Redisent The Redisent object attached to the Redis server
*/
function to($alias) {
if (isset($this->aliases[$alias])) {
return $this->aliases[$alias];
}
else {
throw new Exception("That Redisent alias does not exist");
}
}
/* Execute a Redis command on the cluster */
function __call($name, $args) {
/* Pick a server node to send the command to */
$name = strtoupper($name);
if (!in_array($name, $this->dont_hash)) {
$node = $this->nextNode(crc32($args[0]));
$redisent = $this->ring[$node];
}
else {
$redisent = $this->redisents[0];
}
/* Execute the command on the server */
return call_user_func_array(array($redisent, $name), $args);
}
/**
* Routes to the proper server node
* @param integer $needle The hash value of the Redis command
* @return Redisent The Redisent object associated with the hash
*/
private function nextNode($needle) {
$haystack = $this->nodes;
while (count($haystack) > 2) {
$try = floor(count($haystack) / 2);
if ($haystack[$try] == $needle) {
return $needle;
}
if ($needle < $haystack[$try]) {
$haystack = array_slice($haystack, 0, $try + 1);
}
if ($needle > $haystack[$try]) {
$haystack = array_slice($haystack, $try + 1);
}
}
return $haystack[count($haystack)-1];
}
}

View File

@ -1,18 +1,14 @@
<?php
require_once dirname(__FILE__) . '/Resque/Event.php';
require_once dirname(__FILE__) . '/Resque/Exception.php';
/**
* Base Resque class.
*
* @package Resque
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque
{
const VERSION = '1.0';
const VERSION = '1.2';
const DEFAULT_INTERVAL = 5;
@ -32,12 +28,6 @@ class Resque
*/
protected static $redisDatabase = 0;
/**
* @var int PID of current process. Used to detect changes when forking
* and implement "thread" safety to avoid race conditions.
*/
protected static $pid = null;
/**
* Given a host/port combination separated by a colon, set it as
* the redis server that Resque will talk to.
@ -60,15 +50,7 @@ class Resque
*/
public static function redis()
{
// Detect when the PID of the current process has changed (from a fork, etc)
// and force a reconnect to redis.
$pid = getmypid();
if (self::$pid !== $pid) {
self::$redis = null;
self::$pid = $pid;
}
if(!is_null(self::$redis)) {
if (self::$redis !== null) {
return self::$redis;
}
@ -77,24 +59,35 @@ class Resque
$server = 'localhost:6379';
}
if(is_array($server)) {
require_once dirname(__FILE__) . '/Resque/RedisCluster.php';
self::$redis = new Resque_RedisCluster($server);
}
else {
if (strpos($server, 'unix:') === false) {
list($host, $port) = explode(':', $server);
}
else {
$host = $server;
$port = null;
}
require_once dirname(__FILE__) . '/Resque/Redis.php';
self::$redis = new Resque_Redis($host, $port);
self::$redis = new Resque_Redis($server, self::$redisDatabase);
return self::$redis;
}
self::$redis->select(self::$redisDatabase);
return self::$redis;
/**
* fork() helper method for php-resque that handles issues PHP socket
* and phpredis have with passing around sockets between child/parent
* processes.
*
* Will close connection to Redis before forking.
*
* @return int Return vars as per pcntl_fork()
*/
public static function fork()
{
if(!function_exists('pcntl_fork')) {
return -1;
}
// Close the connection to Redis before forking.
// This is a workaround for issues phpredis has.
self::$redis = null;
$pid = pcntl_fork();
if($pid === -1) {
throw new RuntimeException('Unable to fork child worker.');
}
return $pid;
}
/**
@ -156,7 +149,6 @@ class Resque
*/
public static function enqueue($queue, $class, $args = null, $trackStatus = false)
{
require_once dirname(__FILE__) . '/Resque/Job.php';
$result = Resque_Job::create($queue, $class, $args, $trackStatus);
if ($result) {
Resque_Event::trigger('afterEnqueue', array(
@ -177,7 +169,6 @@ class Resque
*/
public static function reserve($queue, $interval = null)
{
require_once dirname(__FILE__) . '/Resque/Job.php';
return Resque_Job::reserve($queue, $interval);
}

View File

@ -3,8 +3,7 @@
* Resque event/plugin system class
*
* @package Resque/Event
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Event

View File

@ -3,8 +3,7 @@
* Resque exception.
*
* @package Resque
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Exception extends Exception

View File

@ -1,12 +1,10 @@
<?php
require_once dirname(__FILE__) . '/Failure/Interface.php';
/**
* Failed Resque job.
*
* @package Resque/Failure
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Failure
@ -38,7 +36,6 @@ class Resque_Failure
public static function getBackend()
{
if(self::$backend === null) {
require dirname(__FILE__) . '/Failure/Redis.php';
self::$backend = 'Resque_Failure_Redis';
}

View File

@ -3,8 +3,7 @@
* Interface that all failure backends should implement.
*
* @package Resque/Failure
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
interface Resque_Failure_Interface

View File

@ -3,8 +3,7 @@
* Redis backend for storing failed Resque jobs.
*
* @package Resque/Failure
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/

View File

@ -1,14 +1,9 @@
<?php
require_once dirname(__FILE__) . '/Event.php';
require_once dirname(__FILE__) . '/Job/Status.php';
require_once dirname(__FILE__) . '/Job/DontPerform.php';
/**
* Resque job.
*
* @package Resque/Job
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Job
@ -209,7 +204,6 @@ class Resque_Job
));
$this->updateStatus(Resque_Job_Status::STATUS_FAILED);
require_once dirname(__FILE__) . '/Failure.php';
Resque_Failure::create(
$this->payload,
$exception,

View File

@ -3,8 +3,7 @@
* Runtime exception class for a job that does not exit cleanly.
*
* @package Resque/Job
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Job_DirtyExitException extends RuntimeException

View File

@ -3,8 +3,7 @@
* Exception to be thrown if a job should not be performed/run.
*
* @package Resque/Job
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Job_DontPerform extends Exception

View File

@ -3,8 +3,7 @@
* Status tracker/information for a job.
*
* @package Resque/Job
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Job_Status

View File

@ -1,26 +1,22 @@
<?php
// Third- party apps may have already loaded Resident from elsewhere
// so lets be careful.
if(!class_exists('Redisent', false)) {
require_once dirname(__FILE__) . '/../Redisent/Redisent.php';
}
/**
* Extended Redisent class used by Resque for all communication with
* redis. Essentially adds namespace support to Redisent.
* Wrap Credis to add namespace support and various helper methods.
*
* @package Resque/Redis
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Redis extends Redisent
class Resque_Redis
{
/**
* Redis namespace
* @var string
*/
private static $defaultNamespace = 'resque:';
private $server;
private $database;
/**
* @var array List of all commands in Redis that supply a key as their
* first argument. Used to prefix keys with the Resque namespace.
@ -34,6 +30,7 @@ class Resque_Redis extends Redisent
'ttl',
'move',
'set',
'setex',
'get',
'getset',
'setnx',
@ -95,6 +92,47 @@ class Resque_Redis extends Redisent
self::$defaultNamespace = $namespace;
}
public function __construct($server, $database = null)
{
$this->server = $server;
$this->database = $database;
if (is_array($this->server)) {
$this->driver = new Credis_Cluster($server);
}
else {
$port = null;
$password = null;
$host = $server;
// If not a UNIX socket path or tcp:// formatted connections string
// assume host:port combination.
if (strpos($server, '/') === false) {
$parts = explode(':', $server);
if (isset($parts[1])) {
$port = $parts[1];
}
$host = $parts[0];
}else if (strpos($server, 'redis://') !== false){
// Redis format is:
// redis://[user]:[password]@[host]:[port]
list($userpwd,$hostport) = explode('@', $server);
$userpwd = substr($userpwd, strpos($userpwd, 'redis://')+8);
list($host, $port) = explode(':', $hostport);
list($user, $password) = explode(':', $userpwd);
}
$this->driver = new Credis_Client($host, $port);
if (isset($password)){
$this->driver->auth($password);
}
}
if ($this->database !== null) {
$this->driver->select($database);
}
}
/**
* Magic method to handle all function requests and prefix key based
* operations with the {self::$defaultNamespace} key prefix.
@ -104,16 +142,30 @@ class Resque_Redis extends Redisent
* @return mixed Return value from Resident::call() based on the command.
*/
public function __call($name, $args) {
$args = func_get_args();
if(in_array($name, $this->keyCommands)) {
$args[1][0] = self::$defaultNamespace . $args[1][0];
$args[0] = self::$defaultNamespace . $args[0];
}
try {
return parent::__call($name, $args[1]);
return $this->driver->__call($name, $args);
}
catch(RedisException $e) {
catch(CredisException $e) {
return false;
}
}
public static function getPrefix()
{
return self::$defaultNamespace;
}
public static function removePrefix($string)
{
$prefix=self::getPrefix();
if (substr($string, 0, strlen($prefix)) == $prefix) {
$string = substr($string, strlen($prefix), strlen($string) );
}
return $string;
}
}
?>

View File

@ -1,119 +0,0 @@
<?php
// Third- party apps may have already loaded Resident from elsewhere
// so lets be careful.
if(!class_exists('RedisentCluster', false)) {
require_once dirname(__FILE__) . '/../Redisent/RedisentCluster.php';
}
/**
* Extended Redisent class used by Resque for all communication with
* redis. Essentially adds namespace support to Redisent.
*
* @package Resque/Redis
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_RedisCluster extends RedisentCluster
{
/**
* Redis namespace
* @var string
*/
private static $defaultNamespace = 'resque:';
/**
* @var array List of all commands in Redis that supply a key as their
* first argument. Used to prefix keys with the Resque namespace.
*/
private $keyCommands = array(
'exists',
'del',
'type',
'keys',
'expire',
'ttl',
'move',
'set',
'get',
'getset',
'setnx',
'incr',
'incrby',
'decrby',
'decrby',
'rpush',
'lpush',
'llen',
'lrange',
'ltrim',
'lindex',
'lset',
'lrem',
'lpop',
'blpop',
'rpop',
'sadd',
'srem',
'spop',
'scard',
'sismember',
'smembers',
'srandmember',
'zadd',
'zrem',
'zrange',
'zrevrange',
'zrangebyscore',
'zcard',
'zscore',
'zremrangebyscore',
'sort'
);
// sinterstore
// sunion
// sunionstore
// sdiff
// sdiffstore
// sinter
// smove
// rename
// rpoplpush
// mget
// msetnx
// mset
// renamenx
/**
* Set Redis namespace (prefix) default: resque
* @param string $namespace
*/
public static function prefix($namespace)
{
if (strpos($namespace, ':') === false) {
$namespace .= ':';
}
self::$defaultNamespace = $namespace;
}
/**
* Magic method to handle all function requests and prefix key based
* operations with the '{self::$defaultNamespace}' key prefix.
*
* @param string $name The name of the method called.
* @param array $args Array of supplied arguments to the method.
* @return mixed Return value from Resident::call() based on the command.
*/
public function __call($name, $args) {
$args = func_get_args();
if(in_array($name, $this->keyCommands)) {
$args[1][0] = self::$defaultNamespace . $args[1][0];
}
try {
return parent::__call($name, $args[1]);
}
catch(RedisException $e) {
return false;
}
}
}
?>

View File

@ -3,8 +3,7 @@
* Resque statistic management (jobs processed, failed, etc)
*
* @package Resque/Stat
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Stat

View File

@ -1,16 +1,10 @@
<?php
require_once dirname(__FILE__) . '/Stat.php';
require_once dirname(__FILE__) . '/Event.php';
require_once dirname(__FILE__) . '/Job.php';
require_once dirname(__FILE__) . '/Job/DirtyExitException.php';
/**
* Resque worker that handles checking queues for jobs, fetching them
* off the queues, running them and handling the result.
*
* @package Resque/Worker
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Worker
@ -190,7 +184,7 @@ class Resque_Worker
Resque_Event::trigger('beforeFork', $job);
$this->workingOn($job);
$this->child = $this->fork();
$this->child = Resque::fork();
// Forked and we're the child. Run the job.
if ($this->child === 0 || $this->child === false) {
@ -292,27 +286,6 @@ class Resque_Worker
return $queues;
}
/**
* Attempt to fork a child process from the parent to run a job in.
*
* Return values are those of pcntl_fork().
*
* @return int -1 if the fork failed, 0 for the forked child, the PID of the child for the parent.
*/
private function fork()
{
if(!function_exists('pcntl_fork')) {
return false;
}
$pid = pcntl_fork();
if($pid === -1) {
throw new RuntimeException('Unable to fork child worker.');
}
return $pid;
}
/**
* Perform necessary actions to start a worker.
*/
@ -480,7 +453,7 @@ class Resque_Worker
*/
public function registerWorker()
{
Resque::redis()->sadd('workers', $this);
Resque::redis()->sadd('workers', (string)$this);
Resque::redis()->set('worker:' . (string)$this . ':started', strftime('%a %b %d %H:%M:%S %Z %Y'));
}
@ -545,16 +518,21 @@ class Resque_Worker
* Output a given log message to STDOUT.
*
* @param string $message Message to output.
* @param int $logLevel The logging level to capture
*/
public function log($message)
public function log($message, $logLevel = self::LOG_NORMAL)
{
if ($logLevel > $this->logLevel) {
return;
}
if ($this->logLevel == self::LOG_NORMAL) {
fwrite(STDOUT, "*** " . $message . "\n");
return;
}
else if($this->logLevel == self::LOG_VERBOSE) {
fwrite(STDOUT, "** [" . strftime('%T %Y-%m-%d') . "] " . $message . "\n");
}
}
/**
* Return an object describing the job this worker is currently working on.

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
bootstrap="test/bootstrap.php"
backupStaticAttributes="false"
colors="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"

View File

@ -1,79 +0,0 @@
<?php
$QUEUE = getenv('QUEUE');
if(empty($QUEUE)) {
die("Set QUEUE env var containing the list of queues to work.\n");
}
require_once 'lib/Resque.php';
require_once 'lib/Resque/Worker.php';
$REDIS_BACKEND = getenv('REDIS_BACKEND');
if(!empty($REDIS_BACKEND)) {
Resque::setBackend($REDIS_BACKEND);
}
$logLevel = 0;
$LOGGING = getenv('LOGGING');
$VERBOSE = getenv('VERBOSE');
$VVERBOSE = getenv('VVERBOSE');
if(!empty($LOGGING) || !empty($VERBOSE)) {
$logLevel = Resque_Worker::LOG_NORMAL;
}
else if(!empty($VVERBOSE)) {
$logLevel = Resque_Worker::LOG_VERBOSE;
}
$APP_INCLUDE = getenv('APP_INCLUDE');
if($APP_INCLUDE) {
if(!file_exists($APP_INCLUDE)) {
die('APP_INCLUDE ('.$APP_INCLUDE.") does not exist.\n");
}
require_once $APP_INCLUDE;
}
$interval = 5;
$INTERVAL = getenv('INTERVAL');
if(!empty($INTERVAL)) {
$interval = $INTERVAL;
}
$count = 1;
$COUNT = getenv('COUNT');
if(!empty($COUNT) && $COUNT > 1) {
$count = $COUNT;
}
if($count > 1) {
for($i = 0; $i < $count; ++$i) {
$pid = pcntl_fork();
if($pid == -1) {
die("Could not fork worker ".$i."\n");
}
// Child, start the worker
else if(!$pid) {
$queues = explode(',', $QUEUE);
$worker = new Resque_Worker($queues);
$worker->logLevel = $logLevel;
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
$worker->work($interval);
break;
}
}
}
// Start a single worker
else {
$queues = explode(',', $QUEUE);
$worker = new Resque_Worker($queues);
$worker->logLevel = $logLevel;
$PIDFILE = getenv('PIDFILE');
if ($PIDFILE) {
file_put_contents($PIDFILE, getmypid()) or
die('Could not write PID information to ' . $PIDFILE);
}
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
$worker->work($interval);
}
?>

View File

@ -1,12 +1,9 @@
<?php
require_once dirname(__FILE__) . '/bootstrap.php';
/**
* Resque_Event tests.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_EventTest extends Resque_Tests_TestCase

View File

@ -1,12 +1,9 @@
<?php
require_once dirname(__FILE__) . '/bootstrap.php';
/**
* Resque_Job_Status tests.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_JobStatusTest extends Resque_Tests_TestCase

View File

@ -1,12 +1,10 @@
<?php
require_once dirname(__FILE__) . '/bootstrap.php';
/**
* Resque_Job tests.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_JobTest extends Resque_Tests_TestCase

View File

@ -1,12 +1,9 @@
<?php
require_once dirname(__FILE__) . '/bootstrap.php';
/**
* Resque_Stat tests.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_StatTest extends Resque_Tests_TestCase

View File

@ -3,8 +3,7 @@
* Resque test case class. Contains setup and teardown methods.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_TestCase extends PHPUnit_Framework_TestCase
@ -16,7 +15,7 @@ class Resque_Tests_TestCase extends PHPUnit_Framework_TestCase
{
$config = file_get_contents(REDIS_CONF);
preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches);
$this->redis = new Redisent('localhost', $matches[1]);
$this->redis = new Credis_Client('localhost', $matches[1]);
// Flush redis
$this->redis->flushAll();

View File

@ -1,12 +1,9 @@
<?php
require_once dirname(__FILE__) . '/bootstrap.php';
/**
* Resque_Worker tests.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Tests_WorkerTest extends Resque_Tests_TestCase

View File

@ -3,26 +3,16 @@
* Resque test bootstrap file - sets up a test environment.
*
* @package Resque/Tests
* @author Chris Boulton <chris.boulton@interspire.com>
* @copyright (c) 2010 Chris Boulton
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
define('CWD', dirname(__FILE__));
define('RESQUE_LIB', CWD . '/../../../lib/');
define('TEST_MISC', realpath(CWD . '/../../misc/'));
$loader = require __DIR__ . '/../vendor/autoload.php';
$loader->add('Resque_Tests', __DIR__);
define('TEST_MISC', realpath(__DIR__ . '/misc/'));
define('REDIS_CONF', TEST_MISC . '/redis.conf');
// Change to the directory this file lives in. This is important, due to
// how we'll be running redis.
require_once CWD . '/TestCase.php';
// Include Resque
require_once RESQUE_LIB . 'Resque.php';
require_once RESQUE_LIB . 'Resque/Worker.php';
require_once RESQUE_LIB . 'Resque/Redis.php';
// Attempt to start our own redis instance for tesitng.
exec('which redis-server', $output, $returnVar);
if($returnVar != 0) {