From dd7c878b7a995b0de3777036fc4f251009784f53 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 19 Feb 2017 00:01:01 +1100 Subject: [PATCH 1/6] Hide notice logs, with the new attribute debug --- lib/Resque/Log.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/Resque/Log.php b/lib/Resque/Log.php index ce279cc6..f3be169c 100644 --- a/lib/Resque/Log.php +++ b/lib/Resque/Log.php @@ -10,8 +10,11 @@ class Resque_Log extends Psr\Log\AbstractLogger { public $verbose; - public function __construct($verbose = false) { + public $debug; + + public function __construct($verbose = false, $debug = false) { $this->verbose = $verbose; + $this->debug = $debug; } /** @@ -32,12 +35,14 @@ public function log($level, $message, array $context = array()) return; } - if (!($level === Psr\Log\LogLevel::INFO || $level === Psr\Log\LogLevel::DEBUG)) { - fwrite( - STDOUT, - '[' . $level . '] ' . $this->interpolate($message, $context) . PHP_EOL - ); - } + if($this->debug) { + if (!($level === Psr\Log\LogLevel::INFO || $level === Psr\Log\LogLevel::DEBUG)) { + fwrite( + STDOUT, + '[' . $level . '] ' . $this->interpolate($message, $context) . PHP_EOL + ); + } + } } /** From 4c188f8b1928a2bd630e981c427f03b5e5b87b01 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 19 Feb 2017 00:02:04 +1100 Subject: [PATCH 2/6] ignore folder of the phpstorm --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a793d958..0e094d22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor/ *.swp phpunit.xml +.idea From eb61fdc0c97c3bece522a8dae2b4bac80f6c123b Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 19 Feb 2017 00:10:07 +1100 Subject: [PATCH 3/6] updated composer --- composer.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 28a0bf58..21d78b26 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,18 @@ { - "name": "chrisboulton/php-resque", + "name": "spiritdead/php-resque", "type": "library", "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", "keywords": ["job", "background", "redis", "resque"], - "homepage": "http://www.github.com/chrisboulton/php-resque/", + "homepage": "http://www.github.com/spiritdead/php-resque/", "license": "MIT", "authors": [ { "name": "Chris Boulton", "email": "chris@bigcommerce.com" + }, + { + "name": "Carlos Rodriguez", + "email": "mundofreakuc@gmail.com" } ], "repositories": [ From 8bfaf74a8b07ff8dd8c0957962db242eb003acde Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 20 Feb 2017 21:15:42 +1100 Subject: [PATCH 4/6] updated syntax to php 5.6, reformat code to PSR1/PSR-2, fixed problem when the server is windows and the the PID --- bin/resque | 66 +- build.xml | 28 +- composer.json | 91 ++- demo/bad_job.php | 9 +- demo/check_status.php | 16 +- demo/init.php | 28 +- demo/job.php | 11 +- demo/long_job.php | 9 +- demo/php_error_job.php | 9 +- demo/queue.php | 22 +- extras/sample-plugin.php | 86 +- lib/Resque.php | 733 ++++++++--------- lib/Resque/Event.php | 157 ++-- lib/Resque/Exception.php | 7 +- lib/Resque/Failure.php | 86 +- lib/Resque/Failure/Interface.php | 25 +- lib/Resque/Failure/Redis.php | 50 +- lib/Resque/Job.php | 530 ++++++------ lib/Resque/Job/DirtyExitException.php | 7 +- lib/Resque/Job/DontCreate.php | 7 +- lib/Resque/Job/DontPerform.php | 7 +- lib/Resque/Job/FactoryInterface.php | 14 +- lib/Resque/Job/Status.php | 269 +++---- lib/Resque/JobInterface.php | 8 +- lib/Resque/Log.php | 94 +-- lib/Resque/Redis.php | 476 ++++++----- lib/Resque/RedisException.php | 8 +- lib/Resque/Stat.php | 91 +-- lib/Resque/Worker.php | 1064 +++++++++++++------------ phpunit.xml.dist | 38 +- test/Resque/Tests/EventTest.php | 384 ++++----- test/Resque/Tests/JobStatusTest.php | 187 ++--- test/Resque/Tests/JobTest.php | 855 ++++++++++---------- test/Resque/Tests/LogTest.php | 41 +- test/Resque/Tests/RedisTest.php | 441 +++++----- test/Resque/Tests/StatTest.php | 73 +- test/Resque/Tests/TestCase.php | 37 +- test/Resque/Tests/WorkerTest.php | 530 ++++++------ test/bootstrap.php | 156 ++-- 39 files changed, 3430 insertions(+), 3320 deletions(-) diff --git a/bin/resque b/bin/resque index 1d604851..8dd96ee1 100755 --- a/bin/resque +++ b/bin/resque @@ -20,13 +20,13 @@ foreach ($files as $file) { 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 + 'curl -s http://getcomposer.org/installer | php' . PHP_EOL . + 'php composer.phar install' . PHP_EOL ); } $QUEUE = getenv('QUEUE'); -if(empty($QUEUE)) { +if (empty($QUEUE)) { die("Set QUEUE env var containing the list of queues to work.\n"); } @@ -40,28 +40,30 @@ $REDIS_BACKEND = getenv('REDIS_BACKEND'); // A redis database number $REDIS_BACKEND_DB = getenv('REDIS_BACKEND_DB'); -if(!empty($REDIS_BACKEND)) { - if (empty($REDIS_BACKEND_DB)) +if (!empty($REDIS_BACKEND)) { + if (empty($REDIS_BACKEND_DB)) { Resque::setBackend($REDIS_BACKEND); - else + } else { Resque::setBackend($REDIS_BACKEND, $REDIS_BACKEND_DB); + } } $logLevel = false; $LOGGING = getenv('LOGGING'); $VERBOSE = getenv('VERBOSE'); $VVERBOSE = getenv('VVERBOSE'); -if(!empty($LOGGING) || !empty($VERBOSE)) { - $logLevel = true; -} -else if(!empty($VVERBOSE)) { +if (!empty($LOGGING) || !empty($VERBOSE)) { $logLevel = true; +} else { + if (!empty($VVERBOSE)) { + $logLevel = true; + } } $APP_INCLUDE = getenv('APP_INCLUDE'); -if($APP_INCLUDE) { - if(!file_exists($APP_INCLUDE)) { - die('APP_INCLUDE ('.$APP_INCLUDE.") does not exist.\n"); +if ($APP_INCLUDE) { + if (!file_exists($APP_INCLUDE)) { + die('APP_INCLUDE (' . $APP_INCLUDE . ") does not exist.\n"); } require_once $APP_INCLUDE; @@ -73,45 +75,45 @@ if (!isset($logger) || !is_object($logger)) { $logger = new Resque_Log($logLevel); } -$BLOCKING = getenv('BLOCKING') !== FALSE; +$BLOCKING = getenv('BLOCKING') !== false; $interval = 5; $INTERVAL = getenv('INTERVAL'); -if(!empty($INTERVAL)) { +if (!empty($INTERVAL)) { $interval = $INTERVAL; } $count = 1; $COUNT = getenv('COUNT'); -if(!empty($COUNT) && $COUNT > 1) { +if (!empty($COUNT) && $COUNT > 1) { $count = $COUNT; } $PREFIX = getenv('PREFIX'); -if(!empty($PREFIX)) { +if (!empty($PREFIX)) { $logger->log(Psr\Log\LogLevel::INFO, 'Prefix set to {prefix}', array('prefix' => $PREFIX)); Resque_Redis::prefix($PREFIX); } -if($count > 1) { - for($i = 0; $i < $count; ++$i) { +if ($count > 1) { + for ($i = 0; $i < $count; ++$i) { $pid = Resque::fork(); - if($pid === false || $pid === -1) { + if ($pid === false || $pid === -1) { $logger->log(Psr\Log\LogLevel::EMERGENCY, 'Could not fork worker {count}', array('count' => $i)); die(); - } - // Child, start the worker - else if(!$pid) { - $queues = explode(',', $QUEUE); - $worker = new Resque_Worker($queues); - $worker->setLogger($logger); - $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); - $worker->work($interval, $BLOCKING); - break; + } // Child, start the worker + else { + if (!$pid) { + $queues = explode(',', $QUEUE); + $worker = new Resque_Worker($queues); + $worker->setLogger($logger); + $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); + $worker->work($interval, $BLOCKING); + break; + } } } -} -// Start a single worker +} // Start a single worker else { $queues = explode(',', $QUEUE); $worker = new Resque_Worker($queues); @@ -120,7 +122,7 @@ else { $PIDFILE = getenv('PIDFILE'); if ($PIDFILE) { file_put_contents($PIDFILE, getmypid()) or - die('Could not write PID information to ' . $PIDFILE); + die('Could not write PID information to ' . $PIDFILE); } $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); diff --git a/build.xml b/build.xml index 7289999c..bb1a1b53 100644 --- a/build.xml +++ b/build.xml @@ -1,17 +1,17 @@ - - - - - - - - - - + + + + + + + + + - - - + --coverage-html ${basedir}/build/coverage"/> + + + \ No newline at end of file diff --git a/composer.json b/composer.json index 21d78b26..ab6dcb91 100644 --- a/composer.json +++ b/composer.json @@ -1,45 +1,50 @@ { - "name": "spiritdead/php-resque", - "type": "library", - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "keywords": ["job", "background", "redis", "resque"], - "homepage": "http://www.github.com/spiritdead/php-resque/", - "license": "MIT", - "authors": [ - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com" - }, - { - "name": "Carlos Rodriguez", - "email": "mundofreakuc@gmail.com" - } - ], - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/chrisboulton/credis" - } - ], - "require": { - "php": ">=5.3.0", - "ext-pcntl": "*", - "colinmollenhour/credis": "~1.7", - "psr/log": "1.0.0" - }, - "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" - } - } + "name": "spiritdead/php-resque", + "type": "library", + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "keywords": [ + "job", + "background", + "redis", + "resque" + ], + "homepage": "http://www.github.com/spiritdead/php-resque/", + "license": "MIT", + "authors": [ + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com" + }, + { + "name": "Carlos Rodriguez", + "email": "mundofreakuc@gmail.com" + } + ], + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/chrisboulton/credis" + } + ], + "require": { + "php": ">=5.3.0", + "ext-pcntl": "*", + "colinmollenhour/credis": "~1.7", + "psr/log": "1.0.0" + }, + "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" + } + } } diff --git a/demo/bad_job.php b/demo/bad_job.php index cd719cc2..83721b0f 100644 --- a/demo/bad_job.php +++ b/demo/bad_job.php @@ -1,8 +1,9 @@ isTracking()) { - die("Resque is not tracking the status of this job.\n"); +if (!$status->isTracking()) { + die("Resque is not tracking the status of this job.\n"); } -echo "Tracking status of ".$argv[1].". Press [break] to stop.\n\n"; -while(true) { - fwrite(STDOUT, "Status of ".$argv[1]." is: ".$status->get()."\n"); - sleep(1); +echo "Tracking status of " . $argv[1] . ". Press [break] to stop.\n\n"; +while (true) { + fwrite(STDOUT, "Status of " . $argv[1] . " is: " . $status->get() . "\n"); + sleep(1); } \ No newline at end of file diff --git a/demo/init.php b/demo/init.php index 9078bcda..a0828ac8 100644 --- a/demo/init.php +++ b/demo/init.php @@ -2,24 +2,24 @@ // 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', -); +$files = [ + __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 (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 - ); + 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 + ); } \ No newline at end of file diff --git a/demo/job.php b/demo/job.php index ddda8932..d861ee9c 100644 --- a/demo/job.php +++ b/demo/job.php @@ -1,10 +1,11 @@ '); - sleep(1); - fwrite(STDOUT, 'Job ended!' . PHP_EOL); - } + sleep(1); + fwrite(STDOUT, 'Job ended!' . PHP_EOL); + } } \ No newline at end of file diff --git a/demo/long_job.php b/demo/long_job.php index 1cfe5cb0..47d0a2df 100644 --- a/demo/long_job.php +++ b/demo/long_job.php @@ -1,8 +1,9 @@ time(), - 'array' => array( - 'test' => 'test', - ), -); +$args = [ + 'time' => time(), + 'array' => [ + 'test' => 'test' + ] +]; if (empty($argv[2])) { - $jobId = Resque::enqueue('default', $argv[1], $args, true); + $jobId = Resque::enqueue('default', $argv[1], $args, true); } else { - $jobId = Resque::enqueue($argv[1], $argv[2], $args, true); + $jobId = Resque::enqueue($argv[1], $argv[2], $args, true); } -echo "Queued job ".$jobId."\n\n"; +echo "Queued job " . $jobId . "\n\n"; diff --git a/extras/sample-plugin.php b/extras/sample-plugin.php index e70dffde..0779bf41 100644 --- a/extras/sample-plugin.php +++ b/extras/sample-plugin.php @@ -1,49 +1,49 @@ queues(false)) . "\n"; - } - - public static function beforeFork($job) - { - echo "Just about to fork to run " . $job; - } - - public static function afterFork($job) - { - echo "Forked to run " . $job . ". This is the child process.\n"; - } - - public static function beforePerform($job) - { - echo "Cancelling " . $job . "\n"; - // throw new Resque_Job_DontPerform; - } - - public static function afterPerform($job) - { - echo "Just performed " . $job . "\n"; - } - - public static function onFailure($exception, $job) - { - echo $job . " threw an exception:\n" . $exception; - } + public static function afterEnqueue($class, $arguments) + { + echo "Job was queued for " . $class . ". Arguments:"; + print_r($arguments); + } + + public static function beforeFirstFork($worker) + { + echo "Worker started. Listening on queues: " . implode(', ', $worker->queues(false)) . "\n"; + } + + public static function beforeFork($job) + { + echo "Just about to fork to run " . $job; + } + + public static function afterFork($job) + { + echo "Forked to run " . $job . ". This is the child process.\n"; + } + + public static function beforePerform($job) + { + echo "Cancelling " . $job . "\n"; + // throw new Resque_Job_DontPerform; + } + + public static function afterPerform($job) + { + echo "Just performed " . $job . "\n"; + } + + public static function onFailure($exception, $job) + { + echo $job . " threw an exception:\n" . $exception; + } } \ No newline at end of file diff --git a/lib/Resque.php b/lib/Resque.php index d03b2ecf..5e0e4530 100644 --- a/lib/Resque.php +++ b/lib/Resque.php @@ -1,379 +1,380 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque { - const VERSION = '1.2'; + const VERSION = '1.2'; const DEFAULT_INTERVAL = 5; - /** - * @var Resque_Redis Instance of Resque_Redis that talks to redis. - */ - public static $redis = null; - - /** - * @var mixed Host/port conbination separated by a colon, or a nested - * array of server swith host/port pairs - */ - protected static $redisServer = null; - - /** - * @var int ID of Redis database to select. - */ - protected static $redisDatabase = 0; - - /** - * Given a host/port combination separated by a colon, set it as - * the redis server that Resque will talk to. - * - * @param mixed $server Host/port combination separated by a colon, DSN-formatted URI, or - * a callable that receives the configured database ID - * and returns a Resque_Redis instance, or - * a nested array of servers with host/port pairs. - * @param int $database - */ - public static function setBackend($server, $database = 0) - { - self::$redisServer = $server; - self::$redisDatabase = $database; - self::$redis = null; - } - - /** - * Return an instance of the Resque_Redis class instantiated for Resque. - * - * @return Resque_Redis Instance of Resque_Redis. - */ - public static function redis() - { - if (self::$redis !== null) { - return self::$redis; - } - - if (is_callable(self::$redisServer)) { - self::$redis = call_user_func(self::$redisServer, self::$redisDatabase); - } else { - self::$redis = new Resque_Redis(self::$redisServer, 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(). False if pcntl_fork is unavailable - */ - public static function fork() - { - if(!function_exists('pcntl_fork')) { - return false; - } - - // 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; - } - - /** - * Push a job to the end of a specific queue. If the queue does not - * exist, then create it as well. - * - * @param string $queue The name of the queue to add the job to. - * @param array $item Job description as an array to be JSON encoded. - */ - public static function push($queue, $item) - { - $encodedItem = json_encode($item); - if ($encodedItem === false) { - return false; - } - self::redis()->sadd('queues', $queue); - $length = self::redis()->rpush('queue:' . $queue, $encodedItem); - if ($length < 1) { - return false; - } - return true; - } - - /** - * Pop an item off the end of the specified queue, decode it and - * return it. - * - * @param string $queue The name of the queue to fetch an item from. - * @return array Decoded item from the queue. - */ - public static function pop($queue) - { + /** + * @var Resque_Redis Instance of Resque_Redis that talks to redis. + */ + public static $redis = null; + + /** + * @var mixed Host/port conbination separated by a colon, or a nested + * array of server swith host/port pairs + */ + protected static $redisServer = null; + + /** + * @var int ID of Redis database to select. + */ + protected static $redisDatabase = 0; + + /** + * Given a host/port combination separated by a colon, set it as + * the redis server that Resque will talk to. + * + * @param mixed $server Host/port combination separated by a colon, DSN-formatted URI, or + * a callable that receives the configured database ID + * and returns a Resque_Redis instance, or + * a nested array of servers with host/port pairs. + * @param int $database + */ + public static function setBackend($server, $database = 0) + { + self::$redisServer = $server; + self::$redisDatabase = $database; + self::$redis = null; + } + + /** + * Return an instance of the Resque_Redis class instantiated for Resque. + * + * @return Resque_Redis Instance of Resque_Redis. + */ + public static function redis() + { + if (self::$redis !== null) { + return self::$redis; + } + + if (is_callable(self::$redisServer)) { + self::$redis = call_user_func(self::$redisServer, self::$redisDatabase); + } else { + self::$redis = new Resque_Redis(self::$redisServer, 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(). False if pcntl_fork is unavailable + */ + public static function fork() + { + if (!function_exists('pcntl_fork')) { + return false; + } + + // 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; + } + + /** + * Push a job to the end of a specific queue. If the queue does not + * exist, then create it as well. + * + * @param string $queue The name of the queue to add the job to. + * @param array $item Job description as an array to be JSON encoded. + */ + public static function push($queue, $item) + { + $encodedItem = json_encode($item); + if ($encodedItem === false) { + return false; + } + self::redis()->sadd('queues', $queue); + $length = self::redis()->rpush('queue:' . $queue, $encodedItem); + if ($length < 1) { + return false; + } + return true; + } + + /** + * Pop an item off the end of the specified queue, decode it and + * return it. + * + * @param string $queue The name of the queue to fetch an item from. + * @return array Decoded item from the queue. + */ + public static function pop($queue) + { $item = self::redis()->lpop('queue:' . $queue); - if(!$item) { - return; - } - - return json_decode($item, true); - } - - /** - * Remove items of the specified queue - * - * @param string $queue The name of the queue to fetch an item from. - * @param array $items - * @return integer number of deleted items - */ - public static function dequeue($queue, $items = Array()) - { - if(count($items) > 0) { - return self::removeItems($queue, $items); - } else { - return self::removeList($queue); - } - } - - /** - * Remove specified queue - * - * @param string $queue The name of the queue to remove. - * @return integer Number of deleted items - */ - public static function removeQueue($queue) - { - $num = self::removeList($queue); - self::redis()->srem('queues', $queue); - return $num; - } - - /** - * Pop an item off the end of the specified queues, using blocking list pop, - * decode it and return it. - * - * @param array $queues - * @param int $timeout - * @return null|array Decoded item from the queue. - */ - public static function blpop(array $queues, $timeout) - { - $list = array(); - foreach($queues AS $queue) { - $list[] = 'queue:' . $queue; - } - - $item = self::redis()->blpop($list, (int)$timeout); - - if(!$item) { - return; - } - - /** - * Normally the Resque_Redis class returns queue names without the prefix - * But the blpop is a bit different. It returns the name as prefix:queue:name - * So we need to strip off the prefix:queue: part - */ - $queue = substr($item[0], strlen(self::redis()->getPrefix() . 'queue:')); - - return array( - 'queue' => $queue, - 'payload' => json_decode($item[1], true) - ); - } - - /** - * Return the size (number of pending jobs) of the specified queue. - * - * @param string $queue name of the queue to be checked for pending jobs - * - * @return int The size of the queue. - */ - public static function size($queue) - { - return self::redis()->llen('queue:' . $queue); - } - - /** - * Create a new job and save it to the specified queue. - * - * @param string $queue The name of the queue to place the job in. - * @param string $class The name of the class that contains the code to execute the job. - * @param array $args Any optional arguments that should be passed when the job is executed. - * @param boolean $trackStatus Set to true to be able to monitor the status of a job. - * - * @return string|boolean Job ID when the job was created, false if creation was cancelled due to beforeEnqueue - */ - public static function enqueue($queue, $class, $args = null, $trackStatus = false) - { - $id = Resque::generateJobId(); - $hookParams = array( - 'class' => $class, - 'args' => $args, - 'queue' => $queue, - 'id' => $id, - ); - try { - Resque_Event::trigger('beforeEnqueue', $hookParams); - } - catch(Resque_Job_DontCreate $e) { - return false; - } - - Resque_Job::create($queue, $class, $args, $trackStatus, $id); - Resque_Event::trigger('afterEnqueue', $hookParams); - - return $id; - } - - /** - * Reserve and return the next available job in the specified queue. - * - * @param string $queue Queue to fetch next available job from. - * @return Resque_Job Instance of Resque_Job to be processed, false if none or error. - */ - public static function reserve($queue) - { - return Resque_Job::reserve($queue); - } - - /** - * Get an array of all known queues. - * - * @return array Array of queues. - */ - public static function queues() - { - $queues = self::redis()->smembers('queues'); - if(!is_array($queues)) { - $queues = array(); - } - return $queues; - } - - /** - * Remove Items from the queue - * Safely moving each item to a temporary queue before processing it - * If the Job matches, counts otherwise puts it in a requeue_queue - * which at the end eventually be copied back into the original queue - * - * @private - * - * @param string $queue The name of the queue - * @param array $items - * @return integer number of deleted items - */ - private static function removeItems($queue, $items = Array()) - { - $counter = 0; - $originalQueue = 'queue:'. $queue; - $tempQueue = $originalQueue. ':temp:'. time(); - $requeueQueue = $tempQueue. ':requeue'; - - // move each item from original queue to temp queue and process it - $finished = false; - while (!$finished) { - $string = self::redis()->rpoplpush($originalQueue, self::redis()->getPrefix() . $tempQueue); - - if (!empty($string)) { - if(self::matchItem($string, $items)) { - self::redis()->rpop($tempQueue); - $counter++; - } else { - self::redis()->rpoplpush($tempQueue, self::redis()->getPrefix() . $requeueQueue); - } - } else { - $finished = true; - } - } - - // move back from temp queue to original queue - $finished = false; - while (!$finished) { - $string = self::redis()->rpoplpush($requeueQueue, self::redis()->getPrefix() .$originalQueue); - if (empty($string)) { - $finished = true; - } - } - - // remove temp queue and requeue queue - self::redis()->del($requeueQueue); - self::redis()->del($tempQueue); - - return $counter; - } - - /** - * matching item - * item can be ['class'] or ['class' => 'id'] or ['class' => {:foo => 1, :bar => 2}] - * @private - * - * @params string $string redis result in json - * @params $items - * - * @return (bool) - */ - private static function matchItem($string, $items) - { - $decoded = json_decode($string, true); - - foreach($items as $key => $val) { - # class name only ex: item[0] = ['class'] - if (is_numeric($key)) { - if($decoded['class'] == $val) { - return true; - } - # class name with args , example: item[0] = ['class' => {'foo' => 1, 'bar' => 2}] - } elseif (is_array($val)) { - $decodedArgs = (array)$decoded['args'][0]; - if ($decoded['class'] == $key && - count($decodedArgs) > 0 && count(array_diff($decodedArgs, $val)) == 0) { - return true; - } - # class name with ID, example: item[0] = ['class' => 'id'] - } else { - if ($decoded['class'] == $key && $decoded['id'] == $val) { - return true; - } - } - } - return false; - } - - /** - * Remove List - * - * @private - * - * @params string $queue the name of the queue - * @return integer number of deleted items belongs to this list - */ - private static function removeList($queue) - { - $counter = self::size($queue); - $result = self::redis()->del('queue:' . $queue); - return ($result == 1) ? $counter : 0; - } - - /* - * Generate an identifier to attach to a job for status tracking. - * - * @return string - */ - public static function generateJobId() - { - return md5(uniqid('', true)); - } + if (!$item) { + return; + } + + return json_decode($item, true); + } + + /** + * Remove items of the specified queue + * + * @param string $queue The name of the queue to fetch an item from. + * @param array $items + * @return integer number of deleted items + */ + public static function dequeue($queue, $items = []) + { + if (count($items) > 0) { + return self::removeItems($queue, $items); + } else { + return self::removeList($queue); + } + } + + /** + * Remove specified queue + * + * @param string $queue The name of the queue to remove. + * @return integer Number of deleted items + */ + public static function removeQueue($queue) + { + $num = self::removeList($queue); + self::redis()->srem('queues', $queue); + return $num; + } + + /** + * Pop an item off the end of the specified queues, using blocking list pop, + * decode it and return it. + * + * @param array $queues + * @param int $timeout + * @return null|array Decoded item from the queue. + */ + public static function blpop(array $queues, $timeout) + { + $list = []; + foreach ($queues AS $queue) { + $list[] = 'queue:' . $queue; + } + + $item = self::redis()->blpop($list, (int)$timeout); + + if (!$item) { + return null; + } + + /** + * Normally the Resque_Redis class returns queue names without the prefix + * But the blpop is a bit different. It returns the name as prefix:queue:name + * So we need to strip off the prefix:queue: part + */ + $queue = substr($item[0], strlen(self::redis()->getPrefix() . 'queue:')); + + return [ + 'queue' => $queue, + 'payload' => json_decode($item[1], true) + ]; + } + + /** + * Return the size (number of pending jobs) of the specified queue. + * + * @param string $queue name of the queue to be checked for pending jobs + * + * @return int The size of the queue. + */ + public static function size($queue) + { + return self::redis()->llen('queue:' . $queue); + } + + /** + * Create a new job and save it to the specified queue. + * + * @param string $queue The name of the queue to place the job in. + * @param string $class The name of the class that contains the code to execute the job. + * @param array $args Any optional arguments that should be passed when the job is executed. + * @param boolean $trackStatus Set to true to be able to monitor the status of a job. + * + * @return string|boolean Job ID when the job was created, false if creation was cancelled due to beforeEnqueue + */ + public static function enqueue($queue, $class, $args = null, $trackStatus = false) + { + $id = Resque::generateJobId(); + $hookParams = [ + 'class' => $class, + 'args' => $args, + 'queue' => $queue, + 'id' => $id, + ]; + try { + Resque_Event::trigger('beforeEnqueue', $hookParams); + } catch (Resque_Job_DontCreate $e) { + return false; + } + + Resque_Job::create($queue, $class, $args, $trackStatus, $id); + Resque_Event::trigger('afterEnqueue', $hookParams); + + return $id; + } + + /** + * Reserve and return the next available job in the specified queue. + * + * @param string $queue Queue to fetch next available job from. + * @return Resque_Job Instance of Resque_Job to be processed, false if none or error. + */ + public static function reserve($queue) + { + return Resque_Job::reserve($queue); + } + + /** + * Get an array of all known queues. + * + * @return array Array of queues. + */ + public static function queues() + { + $queues = self::redis()->smembers('queues'); + if (!is_array($queues)) { + $queues = []; + } + return $queues; + } + + /** + * Remove Items from the queue + * Safely moving each item to a temporary queue before processing it + * If the Job matches, counts otherwise puts it in a requeue_queue + * which at the end eventually be copied back into the original queue + * + * @private + * + * @param string $queue The name of the queue + * @param array $items + * @return integer number of deleted items + */ + private static function removeItems($queue, $items = []) + { + $counter = 0; + $originalQueue = 'queue:' . $queue; + $tempQueue = $originalQueue . ':temp:' . time(); + $requeueQueue = $tempQueue . ':requeue'; + + // move each item from original queue to temp queue and process it + $finished = false; + while (!$finished) { + $string = self::redis()->rpoplpush($originalQueue, self::redis()->getPrefix() . $tempQueue); + + if (!empty($string)) { + if (self::matchItem($string, $items)) { + self::redis()->rpop($tempQueue); + $counter++; + } else { + self::redis()->rpoplpush($tempQueue, self::redis()->getPrefix() . $requeueQueue); + } + } else { + $finished = true; + } + } + + // move back from temp queue to original queue + $finished = false; + while (!$finished) { + $string = self::redis()->rpoplpush($requeueQueue, self::redis()->getPrefix() . $originalQueue); + if (empty($string)) { + $finished = true; + } + } + + // remove temp queue and requeue queue + self::redis()->del($requeueQueue); + self::redis()->del($tempQueue); + + return $counter; + } + + /** + * matching item + * item can be ['class'] or ['class' => 'id'] or ['class' => {:foo => 1, :bar => 2}] + * @private + * + * @params string $string redis result in json + * @params $items + * + * @return (bool) + */ + private static function matchItem($string, $items) + { + $decoded = json_decode($string, true); + + foreach ($items as $key => $val) { + # class name only ex: item[0] = ['class'] + if (is_numeric($key)) { + if ($decoded['class'] == $val) { + return true; + } + # class name with args , example: item[0] = ['class' => {'foo' => 1, 'bar' => 2}] + } elseif (is_array($val)) { + $decodedArgs = (array)$decoded['args'][0]; + if ($decoded['class'] == $key && + count($decodedArgs) > 0 && count(array_diff($decodedArgs, $val)) == 0 + ) { + return true; + } + # class name with ID, example: item[0] = ['class' => 'id'] + } else { + if ($decoded['class'] == $key && $decoded['id'] == $val) { + return true; + } + } + } + return false; + } + + /** + * Remove List + * + * @private + * + * @params string $queue the name of the queue + * @return integer number of deleted items belongs to this list + */ + private static function removeList($queue) + { + $counter = self::size($queue); + $result = self::redis()->del('queue:' . $queue); + return ($result == 1) ? $counter : 0; + } + + /* + * Generate an identifier to attach to a job for status tracking. + * + * @return string + */ + public static function generateJobId() + { + return md5(uniqid('', true)); + } } diff --git a/lib/Resque/Event.php b/lib/Resque/Event.php index 20072ff9..b570ff45 100644 --- a/lib/Resque/Event.php +++ b/lib/Resque/Event.php @@ -1,88 +1,89 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Event + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Event { - /** - * @var array Array containing all registered callbacks, indexked by event name. - */ - private static $events = array(); + /** + * @var array Array containing all registered callbacks, indexked by event name. + */ + private static $events = []; + + /** + * Raise a given event with the supplied data. + * + * @param string $event Name of event to be raised. + * @param mixed $data Optional, any data that should be passed to each callback. + * @return true + */ + public static function trigger($event, $data = null) + { + if (!is_array($data)) { + $data = [$data]; + } + + if (empty(self::$events[$event])) { + return true; + } + + foreach (self::$events[$event] as $callback) { + if (!is_callable($callback)) { + continue; + } + call_user_func_array($callback, $data); + } + + return true; + } + + /** + * Listen in on a given event to have a specified callback fired. + * + * @param string $event Name of event to listen on. + * @param mixed $callback Any callback callable by call_user_func_array. + * @return true + */ + public static function listen($event, $callback) + { + if (!isset(self::$events[$event])) { + self::$events[$event] = []; + } + + self::$events[$event][] = $callback; + return true; + } + + /** + * Stop a given callback from listening on a specific event. + * + * @param string $event Name of event. + * @param mixed $callback The callback as defined when listen() was called. + * @return true + */ + public static function stopListening($event, $callback) + { + if (!isset(self::$events[$event])) { + return true; + } + + $key = array_search($callback, self::$events[$event]); + if ($key !== false) { + unset(self::$events[$event][$key]); + } - /** - * Raise a given event with the supplied data. - * - * @param string $event Name of event to be raised. - * @param mixed $data Optional, any data that should be passed to each callback. - * @return true - */ - public static function trigger($event, $data = null) - { - if (!is_array($data)) { - $data = array($data); - } + return true; + } - if (empty(self::$events[$event])) { - return true; - } - - foreach (self::$events[$event] as $callback) { - if (!is_callable($callback)) { - continue; - } - call_user_func_array($callback, $data); - } - - return true; - } - - /** - * Listen in on a given event to have a specified callback fired. - * - * @param string $event Name of event to listen on. - * @param mixed $callback Any callback callable by call_user_func_array. - * @return true - */ - public static function listen($event, $callback) - { - if (!isset(self::$events[$event])) { - self::$events[$event] = array(); - } - - self::$events[$event][] = $callback; - return true; - } - - /** - * Stop a given callback from listening on a specific event. - * - * @param string $event Name of event. - * @param mixed $callback The callback as defined when listen() was called. - * @return true - */ - public static function stopListening($event, $callback) - { - if (!isset(self::$events[$event])) { - return true; - } - - $key = array_search($callback, self::$events[$event]); - if ($key !== false) { - unset(self::$events[$event][$key]); - } - - return true; - } - - /** - * Call all registered listeners. - */ - public static function clearListeners() - { - self::$events = array(); - } + /** + * Call all registered listeners. + */ + public static function clearListeners() + { + self::$events = []; + } } diff --git a/lib/Resque/Exception.php b/lib/Resque/Exception.php index 01217c38..a20e2683 100644 --- a/lib/Resque/Exception.php +++ b/lib/Resque/Exception.php @@ -1,10 +1,11 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Exception extends Exception { diff --git a/lib/Resque/Failure.php b/lib/Resque/Failure.php index deb678f9..e0e36fbc 100644 --- a/lib/Resque/Failure.php +++ b/lib/Resque/Failure.php @@ -3,54 +3,54 @@ /** * Failed Resque job. * - * @package Resque/Failure - * @author Chris Boulton - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Failure + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Failure { - /** - * @var string Class name representing the backend to pass failed jobs off to. - */ - private static $backend; + /** + * @var string Class name representing the backend to pass failed jobs off to. + */ + private static $backend; - /** - * Create a new failed job on the backend. - * - * @param object $payload The contents of the job that has just failed. - * @param \Exception $exception The exception generated when the job failed to run. - * @param \Resque_Worker $worker Instance of Resque_Worker that was running this job when it failed. - * @param string $queue The name of the queue that this job was fetched from. - */ - public static function create($payload, Exception $exception, Resque_Worker $worker, $queue) - { - $backend = self::getBackend(); - new $backend($payload, $exception, $worker, $queue); - } + /** + * Create a new failed job on the backend. + * + * @param object $payload The contents of the job that has just failed. + * @param \Exception $exception The exception generated when the job failed to run. + * @param \Resque_Worker $worker Instance of Resque_Worker that was running this job when it failed. + * @param string $queue The name of the queue that this job was fetched from. + */ + public static function create($payload, Exception $exception, Resque_Worker $worker, $queue) + { + $backend = self::getBackend(); + new $backend($payload, $exception, $worker, $queue); + } - /** - * Return an instance of the backend for saving job failures. - * - * @return object Instance of backend object. - */ - public static function getBackend() - { - if(self::$backend === null) { - self::$backend = 'Resque_Failure_Redis'; - } + /** + * Return an instance of the backend for saving job failures. + * + * @return object Instance of backend object. + */ + public static function getBackend() + { + if (self::$backend === null) { + self::$backend = 'Resque_Failure_Redis'; + } - return self::$backend; - } + return self::$backend; + } - /** - * Set the backend to use for raised job failures. The supplied backend - * should be the name of a class to be instantiated when a job fails. - * It is your responsibility to have the backend class loaded (or autoloaded) - * - * @param string $backend The class name of the backend to pipe failures to. - */ - public static function setBackend($backend) - { - self::$backend = $backend; - } + /** + * Set the backend to use for raised job failures. The supplied backend + * should be the name of a class to be instantiated when a job fails. + * It is your responsibility to have the backend class loaded (or autoloaded) + * + * @param string $backend The class name of the backend to pipe failures to. + */ + public static function setBackend($backend) + { + self::$backend = $backend; + } } \ No newline at end of file diff --git a/lib/Resque/Failure/Interface.php b/lib/Resque/Failure/Interface.php index 74de9e7b..d6da0e25 100644 --- a/lib/Resque/Failure/Interface.php +++ b/lib/Resque/Failure/Interface.php @@ -1,20 +1,21 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Failure + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ interface Resque_Failure_Interface { - /** - * Initialize a failed job class and save it (where appropriate). - * - * @param object $payload Object containing details of the failed job. - * @param object $exception Instance of the exception that was thrown by the failed job. - * @param object $worker Instance of Resque_Worker that received the job. - * @param string $queue The name of the queue the job was fetched from. - */ - public function __construct($payload, $exception, $worker, $queue); + /** + * Initialize a failed job class and save it (where appropriate). + * + * @param object $payload Object containing details of the failed job. + * @param object $exception Instance of the exception that was thrown by the failed job. + * @param object $worker Instance of Resque_Worker that received the job. + * @param string $queue The name of the queue the job was fetched from. + */ + public function __construct($payload, $exception, $worker, $queue); } diff --git a/lib/Resque/Failure/Redis.php b/lib/Resque/Failure/Redis.php index 69d68724..6f342e30 100644 --- a/lib/Resque/Failure/Redis.php +++ b/lib/Resque/Failure/Redis.php @@ -1,33 +1,33 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Failure + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ - class Resque_Failure_Redis implements Resque_Failure_Interface { - /** - * Initialize a failed job class and save it (where appropriate). - * - * @param object $payload Object containing details of the failed job. - * @param object $exception Instance of the exception that was thrown by the failed job. - * @param object $worker Instance of Resque_Worker that received the job. - * @param string $queue The name of the queue the job was fetched from. - */ - public function __construct($payload, $exception, $worker, $queue) - { - $data = new stdClass; - $data->failed_at = strftime('%a %b %d %H:%M:%S %Z %Y'); - $data->payload = $payload; - $data->exception = get_class($exception); - $data->error = $exception->getMessage(); - $data->backtrace = explode("\n", $exception->getTraceAsString()); - $data->worker = (string)$worker; - $data->queue = $queue; - $data = json_encode($data); - Resque::redis()->rpush('failed', $data); - } + /** + * Initialize a failed job class and save it (where appropriate). + * + * @param object $payload Object containing details of the failed job. + * @param object $exception Instance of the exception that was thrown by the failed job. + * @param object $worker Instance of Resque_Worker that received the job. + * @param string $queue The name of the queue the job was fetched from. + */ + public function __construct($payload, $exception, $worker, $queue) + { + $data = new stdClass; + $data->failed_at = strftime('%a %b %d %H:%M:%S %Z %Y'); + $data->payload = $payload; + $data->exception = get_class($exception); + $data->error = $exception->getMessage(); + $data->backtrace = explode("\n", $exception->getTraceAsString()); + $data->worker = (string)$worker; + $data->queue = $queue; + $data = json_encode($data); + Resque::redis()->rpush('failed', $data); + } } diff --git a/lib/Resque/Job.php b/lib/Resque/Job.php index 8508f766..c7674adf 100755 --- a/lib/Resque/Job.php +++ b/lib/Resque/Job.php @@ -1,280 +1,278 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Job + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Job { - /** - * @var string The name of the queue that this job belongs to. - */ - public $queue; - - /** - * @var Resque_Worker Instance of the Resque worker running this job. - */ - public $worker; - - /** - * @var array Array containing details of the job. - */ - public $payload; - - /** - * @var object|Resque_JobInterface Instance of the class performing work for this job. - */ - private $instance; - - /** - * @var Resque_Job_FactoryInterface - */ - private $jobFactory; - - /** - * Instantiate a new instance of a job. - * - * @param string $queue The queue that the job belongs to. - * @param array $payload array containing details of the job. - */ - public function __construct($queue, $payload) - { - $this->queue = $queue; - $this->payload = $payload; - } - - /** - * Create a new job and save it to the specified queue. - * - * @param string $queue The name of the queue to place the job in. - * @param string $class The name of the class that contains the code to execute the job. - * @param array $args Any optional arguments that should be passed when the job is executed. - * @param boolean $monitor Set to true to be able to monitor the status of a job. - * @param string $id Unique identifier for tracking the job. Generated if not supplied. - * - * @return string - * @throws \InvalidArgumentException - */ - public static function create($queue, $class, $args = null, $monitor = false, $id = null) - { - if (is_null($id)) { - $id = Resque::generateJobId(); - } - - if($args !== null && !is_array($args)) { - throw new InvalidArgumentException( - 'Supplied $args must be an array.' - ); - } - Resque::push($queue, array( - 'class' => $class, - 'args' => array($args), - 'id' => $id, - 'queue_time' => microtime(true), - )); - - if($monitor) { - Resque_Job_Status::create($id); - } - - return $id; - } - - /** - * Find the next available job from the specified queue and return an - * instance of Resque_Job for it. - * - * @param string $queue The name of the queue to check for a job in. - * @return false|object Null when there aren't any waiting jobs, instance of Resque_Job when a job was found. - */ - public static function reserve($queue) - { - $payload = Resque::pop($queue); - if(!is_array($payload)) { - return false; - } - - return new Resque_Job($queue, $payload); - } - - /** - * Find the next available job from the specified queues using blocking list pop - * and return an instance of Resque_Job for it. - * - * @param array $queues - * @param int $timeout - * @return false|object Null when there aren't any waiting jobs, instance of Resque_Job when a job was found. - */ - public static function reserveBlocking(array $queues, $timeout = null) - { - $item = Resque::blpop($queues, $timeout); - - if(!is_array($item)) { - return false; - } - - return new Resque_Job($item['queue'], $item['payload']); - } - - /** - * Update the status of the current job. - * - * @param int $status Status constant from Resque_Job_Status indicating the current status of a job. - */ - public function updateStatus($status) - { - if(empty($this->payload['id'])) { - return; - } - - $statusInstance = new Resque_Job_Status($this->payload['id']); - $statusInstance->update($status); - } - - /** - * Return the status of the current job. - * - * @return int The status of the job as one of the Resque_Job_Status constants. - */ - public function getStatus() - { - $status = new Resque_Job_Status($this->payload['id']); - return $status->get(); - } - - /** - * Get the arguments supplied to this job. - * - * @return array Array of arguments. - */ - public function getArguments() - { - if (!isset($this->payload['args'])) { - return array(); - } - - return $this->payload['args'][0]; - } - - /** - * Get the instantiated object for this job that will be performing work. - * @return Resque_JobInterface Instance of the object that this job belongs to. - * @throws Resque_Exception - */ - public function getInstance() - { - if (!is_null($this->instance)) { - return $this->instance; - } + /** + * @var string The name of the queue that this job belongs to. + */ + public $queue; + + /** + * @var Resque_Worker Instance of the Resque worker running this job. + */ + public $worker; + + /** + * @var array Array containing details of the job. + */ + public $payload; + + /** + * @var object|Resque_JobInterface Instance of the class performing work for this job. + */ + private $instance; + + /** + * @var Resque_Job_FactoryInterface + */ + private $jobFactory; + + /** + * Instantiate a new instance of a job. + * + * @param string $queue The queue that the job belongs to. + * @param array $payload array containing details of the job. + */ + public function __construct($queue, $payload) + { + $this->queue = $queue; + $this->payload = $payload; + } + + /** + * Create a new job and save it to the specified queue. + * + * @param string $queue The name of the queue to place the job in. + * @param string $class The name of the class that contains the code to execute the job. + * @param array $args Any optional arguments that should be passed when the job is executed. + * @param boolean $monitor Set to true to be able to monitor the status of a job. + * @param string $id Unique identifier for tracking the job. Generated if not supplied. + * + * @return string + * @throws \InvalidArgumentException + */ + public static function create($queue, $class, $args = null, $monitor = false, $id = null) + { + if (is_null($id)) { + $id = Resque::generateJobId(); + } + + if ($args !== null && !is_array($args)) { + throw new InvalidArgumentException( + 'Supplied $args must be an array.' + ); + } + Resque::push($queue, [ + 'class' => $class, + 'args' => [$args], + 'id' => $id, + 'queue_time' => microtime(true) + ]); + + if ($monitor) { + Resque_Job_Status::create($id); + } + + return $id; + } + + /** + * Find the next available job from the specified queue and return an + * instance of Resque_Job for it. + * + * @param string $queue The name of the queue to check for a job in. + * @return false|object Null when there aren't any waiting jobs, instance of Resque_Job when a job was found. + */ + public static function reserve($queue) + { + $payload = Resque::pop($queue); + if (!is_array($payload)) { + return false; + } + + return new Resque_Job($queue, $payload); + } + + /** + * Find the next available job from the specified queues using blocking list pop + * and return an instance of Resque_Job for it. + * + * @param array $queues + * @param int $timeout + * @return false|object Null when there aren't any waiting jobs, instance of Resque_Job when a job was found. + */ + public static function reserveBlocking(array $queues, $timeout = null) + { + $item = Resque::blpop($queues, $timeout); + + if (!is_array($item)) { + return false; + } + + return new Resque_Job($item['queue'], $item['payload']); + } + + /** + * Update the status of the current job. + * + * @param int $status Status constant from Resque_Job_Status indicating the current status of a job. + */ + public function updateStatus($status) + { + if (empty($this->payload['id'])) { + return; + } + + $statusInstance = new Resque_Job_Status($this->payload['id']); + $statusInstance->update($status); + } + + /** + * Return the status of the current job. + * + * @return int The status of the job as one of the Resque_Job_Status constants. + */ + public function getStatus() + { + $status = new Resque_Job_Status($this->payload['id']); + return $status->get(); + } + + /** + * Get the arguments supplied to this job. + * + * @return array Array of arguments. + */ + public function getArguments() + { + if (!isset($this->payload['args'])) { + return []; + } + + return $this->payload['args'][0]; + } + + /** + * Get the instantiated object for this job that will be performing work. + * @return Resque_JobInterface Instance of the object that this job belongs to. + * @throws Resque_Exception + */ + public function getInstance() + { + if (!is_null($this->instance)) { + return $this->instance; + } $this->instance = $this->getJobFactory()->create($this->payload['class'], $this->getArguments(), $this->queue); $this->instance->job = $this; return $this->instance; - } - - /** - * Actually execute a job by calling the perform method on the class - * associated with the job with the supplied arguments. - * - * @return bool - * @throws Resque_Exception When the job's class could not be found or it does not contain a perform method. - */ - public function perform() - { - try { - Resque_Event::trigger('beforePerform', $this); - - $instance = $this->getInstance(); - if(method_exists($instance, 'setUp')) { - $instance->setUp(); - } - - $instance->perform(); - - if(method_exists($instance, 'tearDown')) { - $instance->tearDown(); - } - - Resque_Event::trigger('afterPerform', $this); - } - // beforePerform/setUp have said don't perform this job. Return. - catch(Resque_Job_DontPerform $e) { - return false; - } - - return true; - } - - /** - * Mark the current job as having failed. - * - * @param $exception - */ - public function fail($exception) - { - Resque_Event::trigger('onFailure', array( - 'exception' => $exception, - 'job' => $this, - )); - - $this->updateStatus(Resque_Job_Status::STATUS_FAILED); - Resque_Failure::create( - $this->payload, - $exception, - $this->worker, - $this->queue - ); - Resque_Stat::incr('failed'); - Resque_Stat::incr('failed:' . $this->worker); - } - - /** - * Re-queue the current job. - * @return string - */ - public function recreate() - { - $status = new Resque_Job_Status($this->payload['id']); - $monitor = false; - if($status->isTracking()) { - $monitor = true; - } - - return self::create($this->queue, $this->payload['class'], $this->getArguments(), $monitor); - } - - /** - * Generate a string representation used to describe the current job. - * - * @return string The string representation of the job. - */ - public function __toString() - { - $name = array( - 'Job{' . $this->queue .'}' - ); - if(!empty($this->payload['id'])) { - $name[] = 'ID: ' . $this->payload['id']; - } - $name[] = $this->payload['class']; - if(!empty($this->payload['args'])) { - $name[] = json_encode($this->payload['args']); - } - return '(' . implode(' | ', $name) . ')'; - } - - /** - * @param Resque_Job_FactoryInterface $jobFactory - * @return Resque_Job - */ - public function setJobFactory(Resque_Job_FactoryInterface $jobFactory) - { - $this->jobFactory = $jobFactory; - - return $this; - } + } + + /** + * Actually execute a job by calling the perform method on the class + * associated with the job with the supplied arguments. + * + * @return bool + * @throws Resque_Exception When the job's class could not be found or it does not contain a perform method. + */ + public function perform() + { + try { + Resque_Event::trigger('beforePerform', $this); + + $instance = $this->getInstance(); + if (method_exists($instance, 'setUp')) { + $instance->setUp(); + } + + $instance->perform(); + + if (method_exists($instance, 'tearDown')) { + $instance->tearDown(); + } + + Resque_Event::trigger('afterPerform', $this); + } // beforePerform/setUp have said don't perform this job. Return. + catch (Resque_Job_DontPerform $e) { + return false; + } + + return true; + } + + /** + * Mark the current job as having failed. + * + * @param $exception + */ + public function fail($exception) + { + Resque_Event::trigger('onFailure', [ + 'exception' => $exception, + 'job' => $this + ]); + + $this->updateStatus(Resque_Job_Status::STATUS_FAILED); + Resque_Failure::create( + $this->payload, + $exception, + $this->worker, + $this->queue + ); + Resque_Stat::incr('failed'); + Resque_Stat::incr('failed:' . $this->worker); + } + + /** + * Re-queue the current job. + * @return string + */ + public function recreate() + { + $status = new Resque_Job_Status($this->payload['id']); + $monitor = false; + if ($status->isTracking()) { + $monitor = true; + } + + return self::create($this->queue, $this->payload['class'], $this->getArguments(), $monitor); + } + + /** + * Generate a string representation used to describe the current job. + * + * @return string The string representation of the job. + */ + public function __toString() + { + $name = ['Job{' . $this->queue . '}']; + if (!empty($this->payload['id'])) { + $name[] = 'ID: ' . $this->payload['id']; + } + $name[] = $this->payload['class']; + if (!empty($this->payload['args'])) { + $name[] = json_encode($this->payload['args']); + } + return '(' . implode(' | ', $name) . ')'; + } + + /** + * @param Resque_Job_FactoryInterface $jobFactory + * @return Resque_Job + */ + public function setJobFactory(Resque_Job_FactoryInterface $jobFactory) + { + $this->jobFactory = $jobFactory; + + return $this; + } /** * @return Resque_Job_FactoryInterface diff --git a/lib/Resque/Job/DirtyExitException.php b/lib/Resque/Job/DirtyExitException.php index 108e0613..82a9232d 100644 --- a/lib/Resque/Job/DirtyExitException.php +++ b/lib/Resque/Job/DirtyExitException.php @@ -1,10 +1,11 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Job + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Job_DirtyExitException extends RuntimeException { diff --git a/lib/Resque/Job/DontCreate.php b/lib/Resque/Job/DontCreate.php index 31c33cdc..2e24964f 100644 --- a/lib/Resque/Job/DontCreate.php +++ b/lib/Resque/Job/DontCreate.php @@ -1,10 +1,11 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Job + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Job_DontCreate extends Exception { diff --git a/lib/Resque/Job/DontPerform.php b/lib/Resque/Job/DontPerform.php index 553327ff..c0cdef95 100644 --- a/lib/Resque/Job/DontPerform.php +++ b/lib/Resque/Job/DontPerform.php @@ -1,10 +1,11 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Job + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Job_DontPerform extends Exception { diff --git a/lib/Resque/Job/FactoryInterface.php b/lib/Resque/Job/FactoryInterface.php index b8c102cf..a1203e14 100644 --- a/lib/Resque/Job/FactoryInterface.php +++ b/lib/Resque/Job/FactoryInterface.php @@ -2,11 +2,11 @@ interface Resque_Job_FactoryInterface { - /** - * @param $className - * @param array $args - * @param $queue - * @return Resque_JobInterface - */ - public function create($className, $args, $queue); + /** + * @param $className + * @param array $args + * @param $queue + * @return Resque_JobInterface + */ + public function create($className, $args, $queue); } diff --git a/lib/Resque/Job/Status.php b/lib/Resque/Job/Status.php index 00fc40c5..61b5f9f9 100644 --- a/lib/Resque/Job/Status.php +++ b/lib/Resque/Job/Status.php @@ -1,142 +1,143 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Job + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Job_Status { - const STATUS_WAITING = 1; - const STATUS_RUNNING = 2; - const STATUS_FAILED = 3; - const STATUS_COMPLETE = 4; - - /** - * @var string The ID of the job this status class refers back to. - */ - private $id; - - /** - * @var mixed Cache variable if the status of this job is being monitored or not. - * True/false when checked at least once or null if not checked yet. - */ - private $isTracking = null; - - /** - * @var array Array of statuses that are considered final/complete. - */ - private static $completeStatuses = array( - self::STATUS_FAILED, - self::STATUS_COMPLETE - ); - - /** - * Setup a new instance of the job monitor class for the supplied job ID. - * - * @param string $id The ID of the job to manage the status for. - */ - public function __construct($id) - { - $this->id = $id; - } - - /** - * Create a new status monitor item for the supplied job ID. Will create - * all necessary keys in Redis to monitor the status of a job. - * - * @param string $id The ID of the job to monitor the status of. - */ - public static function create($id) - { - $statusPacket = array( - 'status' => self::STATUS_WAITING, - 'updated' => time(), - 'started' => time(), - ); - Resque::redis()->set('job:' . $id . ':status', json_encode($statusPacket)); - } - - /** - * Check if we're actually checking the status of the loaded job status - * instance. - * - * @return boolean True if the status is being monitored, false if not. - */ - public function isTracking() - { - if($this->isTracking === false) { - return false; - } - - if(!Resque::redis()->exists((string)$this)) { - $this->isTracking = false; - return false; - } - - $this->isTracking = true; - return true; - } - - /** - * Update the status indicator for the current job with a new status. - * - * @param int The status of the job (see constants in Resque_Job_Status) - */ - public function update($status) - { - if(!$this->isTracking()) { - return; - } - - $statusPacket = array( - 'status' => $status, - 'updated' => time(), - ); - Resque::redis()->set((string)$this, json_encode($statusPacket)); - - // Expire the status for completed jobs after 24 hours - if(in_array($status, self::$completeStatuses)) { - Resque::redis()->expire((string)$this, 86400); - } - } - - /** - * Fetch the status for the job being monitored. - * - * @return mixed False if the status is not being monitored, otherwise the status as - * as an integer, based on the Resque_Job_Status constants. - */ - public function get() - { - if(!$this->isTracking()) { - return false; - } - - $statusPacket = json_decode(Resque::redis()->get((string)$this), true); - if(!$statusPacket) { - return false; - } - - return $statusPacket['status']; - } - - /** - * Stop tracking the status of a job. - */ - public function stop() - { - Resque::redis()->del((string)$this); - } - - /** - * Generate a string representation of this object. - * - * @return string String representation of the current job status class. - */ - public function __toString() - { - return 'job:' . $this->id . ':status'; - } + const STATUS_WAITING = 1; + const STATUS_RUNNING = 2; + const STATUS_FAILED = 3; + const STATUS_COMPLETE = 4; + + /** + * @var string The ID of the job this status class refers back to. + */ + private $id; + + /** + * @var mixed Cache variable if the status of this job is being monitored or not. + * True/false when checked at least once or null if not checked yet. + */ + private $isTracking = null; + + /** + * @var array Array of statuses that are considered final/complete. + */ + private static $completeStatuses = [ + self::STATUS_FAILED, + self::STATUS_COMPLETE + ]; + + /** + * Setup a new instance of the job monitor class for the supplied job ID. + * + * @param string $id The ID of the job to manage the status for. + */ + public function __construct($id) + { + $this->id = $id; + } + + /** + * Create a new status monitor item for the supplied job ID. Will create + * all necessary keys in Redis to monitor the status of a job. + * + * @param string $id The ID of the job to monitor the status of. + */ + public static function create($id) + { + $statusPacket = [ + 'status' => self::STATUS_WAITING, + 'updated' => time(), + 'started' => time() + ]; + Resque::redis()->set('job:' . $id . ':status', json_encode($statusPacket)); + } + + /** + * Check if we're actually checking the status of the loaded job status + * instance. + * + * @return boolean True if the status is being monitored, false if not. + */ + public function isTracking() + { + if ($this->isTracking === false) { + return false; + } + + if (!Resque::redis()->exists((string)$this)) { + $this->isTracking = false; + return false; + } + + $this->isTracking = true; + return true; + } + + /** + * Update the status indicator for the current job with a new status. + * + * @param int The status of the job (see constants in Resque_Job_Status) + */ + public function update($status) + { + if (!$this->isTracking()) { + return; + } + + $statusPacket = [ + 'status' => $status, + 'updated' => time() + ]; + Resque::redis()->set((string)$this, json_encode($statusPacket)); + + // Expire the status for completed jobs after 24 hours + if (in_array($status, self::$completeStatuses)) { + Resque::redis()->expire((string)$this, 86400); + } + } + + /** + * Fetch the status for the job being monitored. + * + * @return mixed False if the status is not being monitored, otherwise the status as + * as an integer, based on the Resque_Job_Status constants. + */ + public function get() + { + if (!$this->isTracking()) { + return false; + } + + $statusPacket = json_decode(Resque::redis()->get((string)$this), true); + if (!$statusPacket) { + return false; + } + + return $statusPacket['status']; + } + + /** + * Stop tracking the status of a job. + */ + public function stop() + { + Resque::redis()->del((string)$this); + } + + /** + * Generate a string representation of this object. + * + * @return string String representation of the current job status class. + */ + public function __toString() + { + return 'job:' . $this->id . ':status'; + } } diff --git a/lib/Resque/JobInterface.php b/lib/Resque/JobInterface.php index be5891db..f31281dd 100644 --- a/lib/Resque/JobInterface.php +++ b/lib/Resque/JobInterface.php @@ -2,8 +2,8 @@ interface Resque_JobInterface { - /** - * @return bool - */ - public function perform(); + /** + * @return bool + */ + public function perform(); } diff --git a/lib/Resque/Log.php b/lib/Resque/Log.php index f3be169c..b9518a25 100644 --- a/lib/Resque/Log.php +++ b/lib/Resque/Log.php @@ -1,41 +1,43 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Stat + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ -class Resque_Log extends Psr\Log\AbstractLogger +class Resque_Log extends Psr\Log\AbstractLogger { - public $verbose; + public $verbose; public $debug; - public function __construct($verbose = false, $debug = false) { - $this->verbose = $verbose; + public function __construct($verbose = false, $debug = false) + { + $this->verbose = $verbose; $this->debug = $debug; - } + } - /** - * Logs with an arbitrary level. - * - * @param mixed $level PSR-3 log level constant, or equivalent string - * @param string $message Message to log, may contain a { placeholder } - * @param array $context Variables to replace { placeholder } - * @return null - */ - public function log($level, $message, array $context = array()) - { - if ($this->verbose) { - fwrite( - STDOUT, - '[' . $level . '] [' . strftime('%T %Y-%m-%d') . '] ' . $this->interpolate($message, $context) . PHP_EOL - ); - return; - } + /** + * Logs with an arbitrary level. + * + * @param mixed $level PSR-3 log level constant, or equivalent string + * @param string $message Message to log, may contain a { placeholder } + * @param array $context Variables to replace { placeholder } + * @return null + */ + public function log($level, $message, array $context = []) + { + if ($this->verbose) { + fwrite( + STDOUT, + '[' . $level . '] [' . strftime('%T %Y-%m-%d') . '] ' . $this->interpolate($message, $context) . PHP_EOL + ); + return; + } - if($this->debug) { + if ($this->debug) { if (!($level === Psr\Log\LogLevel::INFO || $level === Psr\Log\LogLevel::DEBUG)) { fwrite( STDOUT, @@ -43,25 +45,25 @@ public function log($level, $message, array $context = array()) ); } } - } + } + + /** + * Fill placeholders with the provided context + * @author Jordi Boggiano j.boggiano@seld.be + * + * @param string $message Message to be logged + * @param array $context Array of variables to use in message + * @return string + */ + public function interpolate($message, array $context = []) + { + // build a replacement array with braces around the context keys + $replace = []; + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } - /** - * Fill placeholders with the provided context - * @author Jordi Boggiano j.boggiano@seld.be - * - * @param string $message Message to be logged - * @param array $context Array of variables to use in message - * @return string - */ - public function interpolate($message, array $context = array()) - { - // build a replacement array with braces around the context keys - $replace = array(); - foreach ($context as $key => $val) { - $replace['{' . $key . '}'] = $val; - } - - // interpolate replacement values into the message and return - return strtr($message, $replace); - } + // interpolate replacement values into the message and return + return strtr($message, $replace); + } } diff --git a/lib/Resque/Redis.php b/lib/Resque/Redis.php index 153bd40e..79fe0907 100644 --- a/lib/Resque/Redis.php +++ b/lib/Resque/Redis.php @@ -1,270 +1,268 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Redis + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Redis { - /** - * Redis namespace - * @var string - */ - private static $defaultNamespace = 'resque:'; + /** + * Redis namespace + * @var string + */ + private static $defaultNamespace = 'resque:'; - /** - * A default host to connect to - */ - const DEFAULT_HOST = 'localhost'; + /** + * A default host to connect to + */ + const DEFAULT_HOST = 'localhost'; - /** - * The default Redis port - */ - const DEFAULT_PORT = 6379; + /** + * The default Redis port + */ + const DEFAULT_PORT = 6379; - /** - * The default Redis Database number - */ - const DEFAULT_DATABASE = 0; + /** + * The default Redis Database number + */ + const DEFAULT_DATABASE = 0; - /** - * @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', - 'setex', - 'get', - 'getset', - 'setnx', - 'incr', - 'incrby', - 'decr', - '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', - 'rename', - 'rpoplpush' - ); - // sinterstore - // sunion - // sunionstore - // sdiff - // sdiffstore - // sinter - // smove - // mget - // msetnx - // mset - // renamenx + /** + * @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 = [ + 'exists', + 'del', + 'type', + 'keys', + 'expire', + 'ttl', + 'move', + 'set', + 'setex', + 'get', + 'getset', + 'setnx', + 'incr', + 'incrby', + 'decr', + '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', + 'rename', + 'rpoplpush' + ]; + // sinterstore + // sunion + // sunionstore + // sdiff + // sdiffstore + // sinter + // smove + // mget + // msetnx + // mset + // renamenx - /** - * Set Redis namespace (prefix) default: resque - * @param string $namespace - */ - public static function prefix($namespace) - { - if (substr($namespace, -1) !== ':' && $namespace != '') { - $namespace .= ':'; - } - self::$defaultNamespace = $namespace; - } + /** + * Set Redis namespace (prefix) default: resque + * @param string $namespace + */ + public static function prefix($namespace) + { + if (substr($namespace, -1) !== ':' && $namespace != '') { + $namespace .= ':'; + } + self::$defaultNamespace = $namespace; + } - /** - * @param string|array $server A DSN or array - * @param int $database A database number to select. However, if we find a valid database number in the DSN the - * DSN-supplied value will be used instead and this parameter is ignored. - * @param object $client Optional Credis_Cluster or Credis_Client instance instantiated by you - */ + /** + * @param string|array $server A DSN or array + * @param int $database A database number to select. However, if we find a valid database number in the DSN the + * DSN-supplied value will be used instead and this parameter is ignored. + * @param object $client Optional Credis_Cluster or Credis_Client instance instantiated by you + */ public function __construct($server, $database = null, $client = null) - { - try { - if (is_array($server)) { - $this->driver = new Credis_Cluster($server); - } - else if (is_object($client)) { - $this->driver = $client; - } - else { - list($host, $port, $dsnDatabase, $user, $password, $options) = self::parseDsn($server); - // $user is not used, only $password + { + try { + if (is_array($server)) { + $this->driver = new Credis_Cluster($server); + } else { + if (is_object($client)) { + $this->driver = $client; + } else { + list($host, $port, $dsnDatabase, $user, $password, $options) = self::parseDsn($server); + // $user is not used, only $password - // Look for known Credis_Client options - $timeout = isset($options['timeout']) ? intval($options['timeout']) : null; - $persistent = isset($options['persistent']) ? $options['persistent'] : ''; - $maxRetries = isset($options['max_connect_retries']) ? $options['max_connect_retries'] : 0; + // Look for known Credis_Client options + $timeout = isset($options['timeout']) ? intval($options['timeout']) : null; + $persistent = isset($options['persistent']) ? $options['persistent'] : ''; + $maxRetries = isset($options['max_connect_retries']) ? $options['max_connect_retries'] : 0; - $this->driver = new Credis_Client($host, $port, $timeout, $persistent); - $this->driver->setMaxConnectRetries($maxRetries); - if ($password){ - $this->driver->auth($password); - } + $this->driver = new Credis_Client($host, $port, $timeout, $persistent); + $this->driver->setMaxConnectRetries($maxRetries); + if ($password) { + $this->driver->auth($password); + } - // If we have found a database in our DSN, use it instead of the `$database` - // value passed into the constructor. - if ($dsnDatabase !== false) { - $database = $dsnDatabase; - } - } + // If we have found a database in our DSN, use it instead of the `$database` + // value passed into the constructor. + if ($dsnDatabase !== false) { + $database = $dsnDatabase; + } + } + } - if ($database !== null) { - $this->driver->select($database); - } - } - catch(CredisException $e) { - throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); - } - } + if ($database !== null) { + $this->driver->select($database); + } + } catch (CredisException $e) { + throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); + } + } - /** - * Parse a DSN string, which can have one of the following formats: - * - * - host:port - * - redis://user:pass@host:port/db?option1=val1&option2=val2 - * - tcp://user:pass@host:port/db?option1=val1&option2=val2 - * - unix:///path/to/redis.sock - * - * Note: the 'user' part of the DSN is not used. - * - * @param string $dsn A DSN string - * @return array An array of DSN compotnents, with 'false' values for any unknown components. e.g. - * [host, port, db, user, pass, options] - */ - public static function parseDsn($dsn) - { - if ($dsn == '') { - // Use a sensible default for an empty DNS string - $dsn = 'redis://' . self::DEFAULT_HOST; - } - if(substr($dsn, 0, 7) === 'unix://') { - return array( - $dsn, - null, - false, - null, - null, - null, - ); - } - $parts = parse_url($dsn); + /** + * Parse a DSN string, which can have one of the following formats: + * + * - host:port + * - redis://user:pass@host:port/db?option1=val1&option2=val2 + * - tcp://user:pass@host:port/db?option1=val1&option2=val2 + * - unix:///path/to/redis.sock + * + * Note: the 'user' part of the DSN is not used. + * + * @param string $dsn A DSN string + * @return array An array of DSN compotnents, with 'false' values for any unknown components. e.g. + * [host, port, db, user, pass, options] + */ + public static function parseDsn($dsn) + { + if ($dsn == '') { + // Use a sensible default for an empty DNS string + $dsn = 'redis://' . self::DEFAULT_HOST; + } + if (substr($dsn, 0, 7) === 'unix://') { + return [ + $dsn, + null, + false, + null, + null, + null + ]; + } + $parts = parse_url($dsn); - // Check the URI scheme - $validSchemes = array('redis', 'tcp'); - if (isset($parts['scheme']) && ! in_array($parts['scheme'], $validSchemes)) { - throw new \InvalidArgumentException("Invalid DSN. Supported schemes are " . implode(', ', $validSchemes)); - } + // Check the URI scheme + $validSchemes = ['redis', 'tcp']; + if (isset($parts['scheme']) && !in_array($parts['scheme'], $validSchemes)) { + throw new \InvalidArgumentException("Invalid DSN. Supported schemes are " . implode(', ', $validSchemes)); + } - // Allow simple 'hostname' format, which `parse_url` treats as a path, not host. - if ( ! isset($parts['host']) && isset($parts['path'])) { - $parts['host'] = $parts['path']; - unset($parts['path']); - } + // Allow simple 'hostname' format, which `parse_url` treats as a path, not host. + if (!isset($parts['host']) && isset($parts['path'])) { + $parts['host'] = $parts['path']; + unset($parts['path']); + } - // Extract the port number as an integer - $port = isset($parts['port']) ? intval($parts['port']) : self::DEFAULT_PORT; + // Extract the port number as an integer + $port = isset($parts['port']) ? intval($parts['port']) : self::DEFAULT_PORT; - // Get the database from the 'path' part of the URI - $database = false; - if (isset($parts['path'])) { - // Strip non-digit chars from path - $database = intval(preg_replace('/[^0-9]/', '', $parts['path'])); - } + // Get the database from the 'path' part of the URI + $database = false; + if (isset($parts['path'])) { + // Strip non-digit chars from path + $database = intval(preg_replace('/[^0-9]/', '', $parts['path'])); + } - // Extract any 'user' and 'pass' values - $user = isset($parts['user']) ? $parts['user'] : false; - $pass = isset($parts['pass']) ? $parts['pass'] : false; + // Extract any 'user' and 'pass' values + $user = isset($parts['user']) ? $parts['user'] : false; + $pass = isset($parts['pass']) ? $parts['pass'] : false; - // Convert the query string into an associative array - $options = array(); - if (isset($parts['query'])) { - // Parse the query string into an array - parse_str($parts['query'], $options); - } + // Convert the query string into an associative array + $options = []; + if (isset($parts['query'])) { + // Parse the query string into an array + parse_str($parts['query'], $options); + } - return array( - $parts['host'], - $port, - $database, - $user, - $pass, - $options, - ); - } + return [ + $parts['host'], + $port, + $database, + $user, + $pass, + $options + ]; + } - /** - * 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) - { - if (in_array($name, $this->keyCommands)) { - if (is_array($args[0])) { - foreach ($args[0] AS $i => $v) { - $args[0][$i] = self::$defaultNamespace . $v; - } - } - else { - $args[0] = self::$defaultNamespace . $args[0]; - } - } - try { - return $this->driver->__call($name, $args); - } - catch (CredisException $e) { - throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); - } - } + /** + * 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) + { + if (in_array($name, $this->keyCommands)) { + if (is_array($args[0])) { + foreach ($args[0] AS $i => $v) { + $args[0][$i] = self::$defaultNamespace . $v; + } + } else { + $args[0] = self::$defaultNamespace . $args[0]; + } + } + try { + return $this->driver->__call($name, $args); + } catch (CredisException $e) { + throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); + } + } - public static function getPrefix() - { - return self::$defaultNamespace; - } + public static function getPrefix() + { + return self::$defaultNamespace; + } - public static function removePrefix($string) - { - $prefix=self::getPrefix(); + public static function removePrefix($string) + { + $prefix = self::getPrefix(); - if (substr($string, 0, strlen($prefix)) == $prefix) { - $string = substr($string, strlen($prefix), strlen($string) ); - } - return $string; - } + if (substr($string, 0, strlen($prefix)) == $prefix) { + $string = substr($string, strlen($prefix), strlen($string)); + } + return $string; + } } diff --git a/lib/Resque/RedisException.php b/lib/Resque/RedisException.php index ca654a00..86e36dc1 100644 --- a/lib/Resque/RedisException.php +++ b/lib/Resque/RedisException.php @@ -1,12 +1,14 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_RedisException extends Resque_Exception { } + ?> \ No newline at end of file diff --git a/lib/Resque/Stat.php b/lib/Resque/Stat.php index bc00c636..e0af08e1 100644 --- a/lib/Resque/Stat.php +++ b/lib/Resque/Stat.php @@ -1,56 +1,57 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Stat + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Stat { - /** - * Get the value of the supplied statistic counter for the specified statistic. - * - * @param string $stat The name of the statistic to get the stats for. - * @return mixed Value of the statistic. - */ - public static function get($stat) - { - return (int)Resque::redis()->get('stat:' . $stat); - } + /** + * Get the value of the supplied statistic counter for the specified statistic. + * + * @param string $stat The name of the statistic to get the stats for. + * @return mixed Value of the statistic. + */ + public static function get($stat) + { + return (int)Resque::redis()->get('stat:' . $stat); + } - /** - * Increment the value of the specified statistic by a certain amount (default is 1) - * - * @param string $stat The name of the statistic to increment. - * @param int $by The amount to increment the statistic by. - * @return boolean True if successful, false if not. - */ - public static function incr($stat, $by = 1) - { - return (bool)Resque::redis()->incrby('stat:' . $stat, $by); - } + /** + * Increment the value of the specified statistic by a certain amount (default is 1) + * + * @param string $stat The name of the statistic to increment. + * @param int $by The amount to increment the statistic by. + * @return boolean True if successful, false if not. + */ + public static function incr($stat, $by = 1) + { + return (bool)Resque::redis()->incrby('stat:' . $stat, $by); + } - /** - * Decrement the value of the specified statistic by a certain amount (default is 1) - * - * @param string $stat The name of the statistic to decrement. - * @param int $by The amount to decrement the statistic by. - * @return boolean True if successful, false if not. - */ - public static function decr($stat, $by = 1) - { - return (bool)Resque::redis()->decrby('stat:' . $stat, $by); - } + /** + * Decrement the value of the specified statistic by a certain amount (default is 1) + * + * @param string $stat The name of the statistic to decrement. + * @param int $by The amount to decrement the statistic by. + * @return boolean True if successful, false if not. + */ + public static function decr($stat, $by = 1) + { + return (bool)Resque::redis()->decrby('stat:' . $stat, $by); + } - /** - * Delete a statistic with the given name. - * - * @param string $stat The name of the statistic to delete. - * @return boolean True if successful, false if not. - */ - public static function clear($stat) - { - return (bool)Resque::redis()->del('stat:' . $stat); - } + /** + * Delete a statistic with the given name. + * + * @param string $stat The name of the statistic to delete. + * @return boolean True if successful, false if not. + */ + public static function clear($stat) + { + return (bool)Resque::redis()->del('stat:' . $stat); + } } \ No newline at end of file diff --git a/lib/Resque/Worker.php b/lib/Resque/Worker.php index 04714c1c..6a0ae6a1 100644 --- a/lib/Resque/Worker.php +++ b/lib/Resque/Worker.php @@ -5,51 +5,51 @@ * 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 - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Worker + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Worker { - /** - * @var LoggerInterface Logging object that impliments the PSR-3 LoggerInterface - */ - public $logger; - - /** - * @var array Array of all associated queues for this worker. - */ - private $queues = array(); - - /** - * @var string The hostname of this worker. - */ - private $hostname; - - /** - * @var boolean True if on the next iteration, the worker should shutdown. - */ - private $shutdown = false; - - /** - * @var boolean True if this worker is paused. - */ - private $paused = false; - - /** - * @var string String identifying this worker. - */ - private $id; - - /** - * @var Resque_Job Current job, if any, being processed by this worker. - */ - private $currentJob = null; - - /** - * @var int Process ID of child worker processes. - */ - private $child = null; + /** + * @var LoggerInterface Logging object that impliments the PSR-3 LoggerInterface + */ + public $logger; + + /** + * @var array Array of all associated queues for this worker. + */ + private $queues = []; + + /** + * @var string The hostname of this worker. + */ + private $hostname; + + /** + * @var boolean True if on the next iteration, the worker should shutdown. + */ + private $shutdown = false; + + /** + * @var boolean True if this worker is paused. + */ + private $paused = false; + + /** + * @var string String identifying this worker. + */ + private $id; + + /** + * @var Resque_Job Current job, if any, being processed by this worker. + */ + private $currentJob = null; + + /** + * @var int Process ID of child worker processes. + */ + private $child = null; /** * Instantiate a new worker, given a list of queues that it should be working @@ -66,497 +66,505 @@ public function __construct($queues) { $this->logger = new Resque_Log(); - if(!is_array($queues)) { - $queues = array($queues); + if (!is_array($queues)) { + $queues = [$queues]; } $this->queues = $queues; $this->hostname = php_uname('n'); - $this->id = $this->hostname . ':'.getmypid() . ':' . implode(',', $this->queues); + $this->id = $this->hostname . ':' . getmypid() . ':' . implode(',', $this->queues); + } + + /** + * Return all workers known to Resque as instantiated instances. + * @return array + */ + public static function all() + { + $workers = Resque::redis()->smembers('workers'); + if (!is_array($workers)) { + $workers = []; + } + + $instances = []; + foreach ($workers as $workerId) { + $instances[] = self::find($workerId); + } + return $instances; + } + + /** + * Given a worker ID, check if it is registered/valid. + * + * @param string $workerId ID of the worker. + * @return boolean True if the worker exists, false if not. + */ + public static function exists($workerId) + { + return (bool)Resque::redis()->sismember('workers', $workerId); + } + + /** + * Given a worker ID, find it and return an instantiated worker class for it. + * + * @param string $workerId The ID of the worker. + * @return Resque_Worker Instance of the worker. False if the worker does not exist. + */ + public static function find($workerId) + { + if (!self::exists($workerId) || false === strpos($workerId, ":")) { + return false; + } + + list($hostname, $pid, $queues) = explode(':', $workerId, 3); + $queues = explode(',', $queues); + $worker = new self($queues); + $worker->setId($workerId); + return $worker; + } + + /** + * Set the ID of this worker to a given ID string. + * + * @param string $workerId ID for the worker. + */ + public function setId($workerId) + { + $this->id = $workerId; + } + + /** + * The primary loop for a worker which when called on an instance starts + * the worker's life cycle. + * + * Queues are checked every $interval (seconds) for new jobs. + * + * @param int $interval How often to check for new jobs across the queues. + */ + public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) + { + $this->updateProcLine('Starting'); + $this->startup(); + + while (true) { + if ($this->shutdown) { + break; + } + + // Attempt to find and reserve a job + $job = false; + if (!$this->paused) { + if ($blocking === true) { + $this->logger->log(Psr\Log\LogLevel::INFO, 'Starting blocking with timeout of {interval}', + ['interval' => $interval]); + $this->updateProcLine('Waiting for ' . implode(',', + $this->queues) . ' with blocking timeout ' . $interval); + } else { + $this->updateProcLine('Waiting for ' . implode(',', $this->queues) . ' with interval ' . $interval); + } + + $job = $this->reserve($blocking, $interval); + } + + if (!$job) { + // For an interval of 0, break now - helps with unit testing etc + if ($interval == 0) { + break; + } + + if ($blocking === false) { + // If no job was found, we sleep for $interval before continuing and checking again + $this->logger->log(Psr\Log\LogLevel::INFO, 'Sleeping for {interval}', ['interval' => $interval]); + if ($this->paused) { + $this->updateProcLine('Paused'); + } else { + $this->updateProcLine('Waiting for ' . implode(',', $this->queues)); + } + + usleep($interval * 1000000); + } + + continue; + } + + $this->logger->log(Psr\Log\LogLevel::NOTICE, 'Starting work on {job}', ['job' => $job]); + Resque_Event::trigger('beforeFork', $job); + $this->workingOn($job); + + $this->child = Resque::fork(); + + // Forked and we're the child. Run the job. + if ($this->child === 0 || $this->child === false) { + $status = 'Processing ' . $job->queue . ' since ' . strftime('%F %T'); + $this->updateProcLine($status); + $this->logger->log(Psr\Log\LogLevel::INFO, $status); + $this->perform($job); + if ($this->child === 0) { + exit(0); + } + } + + if ($this->child > 0) { + // Parent process, sit and wait + $status = 'Forked ' . $this->child . ' at ' . strftime('%F %T'); + $this->updateProcLine($status); + $this->logger->log(Psr\Log\LogLevel::INFO, $status); + + // Wait until the child process finishes before continuing + pcntl_wait($status); + $exitStatus = pcntl_wexitstatus($status); + if ($exitStatus !== 0) { + $job->fail(new Resque_Job_DirtyExitException( + 'Job exited with exit code ' . $exitStatus + )); + } + } + + $this->child = null; + $this->doneWorking(); + } + + $this->unregisterWorker(); + } + + /** + * Process a single job. + * + * @param Resque_Job $job The job to be processed. + */ + public function perform(Resque_Job $job) + { + try { + Resque_Event::trigger('afterFork', $job); + $job->perform(); + } catch (Exception $e) { + $this->logger->log(Psr\Log\LogLevel::CRITICAL, '{job} has failed {stack}', ['job' => $job, 'stack' => $e]); + $job->fail($e); + return; + } + + $job->updateStatus(Resque_Job_Status::STATUS_COMPLETE); + $this->logger->log(Psr\Log\LogLevel::NOTICE, '{job} has finished', ['job' => $job]); + } + + /** + * @param bool $blocking + * @param int $timeout + * @return object|boolean Instance of Resque_Job if a job is found, false if not. + */ + public function reserve($blocking = false, $timeout = null) + { + $queues = $this->queues(); + if (!is_array($queues)) { + return; + } + + if ($blocking === true) { + $job = Resque_Job::reserveBlocking($queues, $timeout); + if ($job) { + $this->logger->log(Psr\Log\LogLevel::INFO, 'Found job on {queue}', ['queue' => $job->queue]); + return $job; + } + } else { + foreach ($queues as $queue) { + $this->logger->log(Psr\Log\LogLevel::INFO, 'Checking {queue} for jobs', ['queue' => $queue]); + $job = Resque_Job::reserve($queue); + if ($job) { + $this->logger->log(Psr\Log\LogLevel::INFO, 'Found job on {queue}', ['queue' => $job->queue]); + return $job; + } + } + } + + return false; + } + + /** + * Return an array containing all of the queues that this worker should use + * when searching for jobs. + * + * If * is found in the list of queues, every queue will be searched in + * alphabetic order. (@see $fetch) + * + * @param boolean $fetch If true, and the queue is set to *, will fetch + * all queue names from redis. + * @return array Array of associated queues. + */ + public function queues($fetch = true) + { + if (!in_array('*', $this->queues) || $fetch == false) { + return $this->queues; + } + + $queues = Resque::queues(); + sort($queues); + return $queues; + } + + /** + * Perform necessary actions to start a worker. + */ + private function startup() + { + $this->registerSigHandlers(); + $this->pruneDeadWorkers(); + Resque_Event::trigger('beforeFirstFork', $this); + $this->registerWorker(); + } + + /** + * On supported systems (with the PECL proctitle module installed), update + * the name of the currently running process to indicate the current state + * of a worker. + * + * @param string $status The updated process title. + */ + private function updateProcLine($status) + { + $processTitle = 'resque-worker-' . $this . ' ' . Resque::VERSION . ': ' . $status; + if (function_exists('cli_set_process_title') && PHP_OS !== 'Darwin') { + cli_set_process_title($processTitle); + } else { + if (function_exists('setproctitle')) { + setproctitle($processTitle); + } + } } - /** - * Return all workers known to Resque as instantiated instances. - * @return array - */ - public static function all() - { - $workers = Resque::redis()->smembers('workers'); - if(!is_array($workers)) { - $workers = array(); - } - - $instances = array(); - foreach($workers as $workerId) { - $instances[] = self::find($workerId); - } - return $instances; - } - - /** - * Given a worker ID, check if it is registered/valid. - * - * @param string $workerId ID of the worker. - * @return boolean True if the worker exists, false if not. - */ - public static function exists($workerId) - { - return (bool)Resque::redis()->sismember('workers', $workerId); - } - - /** - * Given a worker ID, find it and return an instantiated worker class for it. - * - * @param string $workerId The ID of the worker. - * @return Resque_Worker Instance of the worker. False if the worker does not exist. - */ - public static function find($workerId) - { - if(!self::exists($workerId) || false === strpos($workerId, ":")) { - return false; - } - - list($hostname, $pid, $queues) = explode(':', $workerId, 3); - $queues = explode(',', $queues); - $worker = new self($queues); - $worker->setId($workerId); - return $worker; - } - - /** - * Set the ID of this worker to a given ID string. - * - * @param string $workerId ID for the worker. - */ - public function setId($workerId) - { - $this->id = $workerId; - } - - /** - * The primary loop for a worker which when called on an instance starts - * the worker's life cycle. - * - * Queues are checked every $interval (seconds) for new jobs. - * - * @param int $interval How often to check for new jobs across the queues. - */ - public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) - { - $this->updateProcLine('Starting'); - $this->startup(); - - while(true) { - if($this->shutdown) { - break; - } - - // Attempt to find and reserve a job - $job = false; - if(!$this->paused) { - if($blocking === true) { - $this->logger->log(Psr\Log\LogLevel::INFO, 'Starting blocking with timeout of {interval}', array('interval' => $interval)); - $this->updateProcLine('Waiting for ' . implode(',', $this->queues) . ' with blocking timeout ' . $interval); - } else { - $this->updateProcLine('Waiting for ' . implode(',', $this->queues) . ' with interval ' . $interval); - } - - $job = $this->reserve($blocking, $interval); - } - - if(!$job) { - // For an interval of 0, break now - helps with unit testing etc - if($interval == 0) { - break; - } - - if($blocking === false) - { - // If no job was found, we sleep for $interval before continuing and checking again - $this->logger->log(Psr\Log\LogLevel::INFO, 'Sleeping for {interval}', array('interval' => $interval)); - if($this->paused) { - $this->updateProcLine('Paused'); - } - else { - $this->updateProcLine('Waiting for ' . implode(',', $this->queues)); - } - - usleep($interval * 1000000); - } - - continue; - } - - $this->logger->log(Psr\Log\LogLevel::NOTICE, 'Starting work on {job}', array('job' => $job)); - Resque_Event::trigger('beforeFork', $job); - $this->workingOn($job); - - $this->child = Resque::fork(); - - // Forked and we're the child. Run the job. - if ($this->child === 0 || $this->child === false) { - $status = 'Processing ' . $job->queue . ' since ' . strftime('%F %T'); - $this->updateProcLine($status); - $this->logger->log(Psr\Log\LogLevel::INFO, $status); - $this->perform($job); - if ($this->child === 0) { - exit(0); - } - } - - if($this->child > 0) { - // Parent process, sit and wait - $status = 'Forked ' . $this->child . ' at ' . strftime('%F %T'); - $this->updateProcLine($status); - $this->logger->log(Psr\Log\LogLevel::INFO, $status); - - // Wait until the child process finishes before continuing - pcntl_wait($status); - $exitStatus = pcntl_wexitstatus($status); - if($exitStatus !== 0) { - $job->fail(new Resque_Job_DirtyExitException( - 'Job exited with exit code ' . $exitStatus - )); - } - } - - $this->child = null; - $this->doneWorking(); - } - - $this->unregisterWorker(); - } - - /** - * Process a single job. - * - * @param Resque_Job $job The job to be processed. - */ - public function perform(Resque_Job $job) - { - try { - Resque_Event::trigger('afterFork', $job); - $job->perform(); - } - catch(Exception $e) { - $this->logger->log(Psr\Log\LogLevel::CRITICAL, '{job} has failed {stack}', array('job' => $job, 'stack' => $e)); - $job->fail($e); - return; - } - - $job->updateStatus(Resque_Job_Status::STATUS_COMPLETE); - $this->logger->log(Psr\Log\LogLevel::NOTICE, '{job} has finished', array('job' => $job)); - } - - /** - * @param bool $blocking - * @param int $timeout - * @return object|boolean Instance of Resque_Job if a job is found, false if not. - */ - public function reserve($blocking = false, $timeout = null) - { - $queues = $this->queues(); - if(!is_array($queues)) { - return; - } - - if($blocking === true) { - $job = Resque_Job::reserveBlocking($queues, $timeout); - if($job) { - $this->logger->log(Psr\Log\LogLevel::INFO, 'Found job on {queue}', array('queue' => $job->queue)); - return $job; - } - } else { - foreach($queues as $queue) { - $this->logger->log(Psr\Log\LogLevel::INFO, 'Checking {queue} for jobs', array('queue' => $queue)); - $job = Resque_Job::reserve($queue); - if($job) { - $this->logger->log(Psr\Log\LogLevel::INFO, 'Found job on {queue}', array('queue' => $job->queue)); - return $job; - } - } - } - - return false; - } - - /** - * Return an array containing all of the queues that this worker should use - * when searching for jobs. - * - * If * is found in the list of queues, every queue will be searched in - * alphabetic order. (@see $fetch) - * - * @param boolean $fetch If true, and the queue is set to *, will fetch - * all queue names from redis. - * @return array Array of associated queues. - */ - public function queues($fetch = true) - { - if(!in_array('*', $this->queues) || $fetch == false) { - return $this->queues; - } - - $queues = Resque::queues(); - sort($queues); - return $queues; - } - - /** - * Perform necessary actions to start a worker. - */ - private function startup() - { - $this->registerSigHandlers(); - $this->pruneDeadWorkers(); - Resque_Event::trigger('beforeFirstFork', $this); - $this->registerWorker(); - } - - /** - * On supported systems (with the PECL proctitle module installed), update - * the name of the currently running process to indicate the current state - * of a worker. - * - * @param string $status The updated process title. - */ - private function updateProcLine($status) - { - $processTitle = 'resque-' . Resque::VERSION . ': ' . $status; - if(function_exists('cli_set_process_title') && PHP_OS !== 'Darwin') { - cli_set_process_title($processTitle); - } - else if(function_exists('setproctitle')) { - setproctitle($processTitle); - } - } - - /** - * Register signal handlers that a worker should respond to. - * - * TERM: Shutdown immediately and stop processing jobs. - * INT: Shutdown immediately and stop processing jobs. - * QUIT: Shutdown after the current job finishes processing. - * USR1: Kill the forked child immediately and continue processing jobs. - */ - private function registerSigHandlers() - { - if(!function_exists('pcntl_signal')) { - return; - } - - pcntl_signal(SIGTERM, array($this, 'shutDownNow')); - pcntl_signal(SIGINT, array($this, 'shutDownNow')); - pcntl_signal(SIGQUIT, array($this, 'shutdown')); - pcntl_signal(SIGUSR1, array($this, 'killChild')); - pcntl_signal(SIGUSR2, array($this, 'pauseProcessing')); - pcntl_signal(SIGCONT, array($this, 'unPauseProcessing')); - $this->logger->log(Psr\Log\LogLevel::DEBUG, 'Registered signals'); - } - - /** - * Signal handler callback for USR2, pauses processing of new jobs. - */ - public function pauseProcessing() - { - $this->logger->log(Psr\Log\LogLevel::NOTICE, 'USR2 received; pausing job processing'); - $this->paused = true; - } - - /** - * Signal handler callback for CONT, resumes worker allowing it to pick - * up new jobs. - */ - public function unPauseProcessing() - { - $this->logger->log(Psr\Log\LogLevel::NOTICE, 'CONT received; resuming job processing'); - $this->paused = false; - } - - /** - * Schedule a worker for shutdown. Will finish processing the current job - * and when the timeout interval is reached, the worker will shut down. - */ - public function shutdown() - { - $this->shutdown = true; - $this->logger->log(Psr\Log\LogLevel::NOTICE, 'Shutting down'); - } - - /** - * Force an immediate shutdown of the worker, killing any child jobs - * currently running. - */ - public function shutdownNow() - { - $this->shutdown(); - $this->killChild(); - } - - /** - * Kill a forked child job immediately. The job it is processing will not - * be completed. - */ - public function killChild() - { - if(!$this->child) { - $this->logger->log(Psr\Log\LogLevel::DEBUG, 'No child to kill.'); - return; - } - - $this->logger->log(Psr\Log\LogLevel::INFO, 'Killing child at {child}', array('child' => $this->child)); - if(exec('ps -o pid,state -p ' . $this->child, $output, $returnCode) && $returnCode != 1) { - $this->logger->log(Psr\Log\LogLevel::DEBUG, 'Child {child} found, killing.', array('child' => $this->child)); - posix_kill($this->child, SIGKILL); - $this->child = null; - } - else { - $this->logger->log(Psr\Log\LogLevel::INFO, 'Child {child} not found, restarting.', array('child' => $this->child)); - $this->shutdown(); - } - } - - /** - * Look for any workers which should be running on this server and if - * they're not, remove them from Redis. - * - * This is a form of garbage collection to handle cases where the - * server may have been killed and the Resque workers did not die gracefully - * and therefore leave state information in Redis. - */ - public function pruneDeadWorkers() - { - $workerPids = $this->workerPids(); - $workers = self::all(); - foreach($workers as $worker) { - if (is_object($worker)) { - list($host, $pid, $queues) = explode(':', (string)$worker, 3); - if($host != $this->hostname || in_array($pid, $workerPids) || $pid == getmypid()) { - continue; - } - $this->logger->log(Psr\Log\LogLevel::INFO, 'Pruning dead worker: {worker}', array('worker' => (string)$worker)); - $worker->unregisterWorker(); - } - } - } - - /** - * Return an array of process IDs for all of the Resque workers currently - * running on this machine. - * - * @return array Array of Resque worker process IDs. - */ - public function workerPids() - { - $pids = array(); - exec('ps -A -o pid,command | grep [r]esque', $cmdOutput); - foreach($cmdOutput as $line) { - list($pids[],) = explode(' ', trim($line), 2); - } - return $pids; - } - - /** - * Register this worker in Redis. - */ - public function registerWorker() - { - Resque::redis()->sadd('workers', (string)$this); - Resque::redis()->set('worker:' . (string)$this . ':started', strftime('%a %b %d %H:%M:%S %Z %Y')); - } - - /** - * Unregister this worker in Redis. (shutdown etc) - */ - public function unregisterWorker() - { - if(is_object($this->currentJob)) { - $this->currentJob->fail(new Resque_Job_DirtyExitException); - } - - $id = (string)$this; - Resque::redis()->srem('workers', $id); - Resque::redis()->del('worker:' . $id); - Resque::redis()->del('worker:' . $id . ':started'); - Resque_Stat::clear('processed:' . $id); - Resque_Stat::clear('failed:' . $id); - } - - /** - * Tell Redis which job we're currently working on. - * - * @param object $job Resque_Job instance containing the job we're working on. - */ - public function workingOn(Resque_Job $job) - { - $job->worker = $this; - $this->currentJob = $job; - $job->updateStatus(Resque_Job_Status::STATUS_RUNNING); - $data = json_encode(array( - 'queue' => $job->queue, - 'run_at' => strftime('%a %b %d %H:%M:%S %Z %Y'), - 'payload' => $job->payload - )); - Resque::redis()->set('worker:' . $job->worker, $data); - } - - /** - * Notify Redis that we've finished working on a job, clearing the working - * state and incrementing the job stats. - */ - public function doneWorking() - { - $this->currentJob = null; - Resque_Stat::incr('processed'); - Resque_Stat::incr('processed:' . (string)$this); - Resque::redis()->del('worker:' . (string)$this); - } - - /** - * Generate a string representation of this worker. - * - * @return string String identifier for this worker instance. - */ - public function __toString() - { - return $this->id; - } - - /** - * Return an object describing the job this worker is currently working on. - * - * @return object Object with details of current job. - */ - public function job() - { - $job = Resque::redis()->get('worker:' . $this); - if(!$job) { - return array(); - } - else { - return json_decode($job, true); - } - } - - /** - * Get a statistic belonging to this worker. - * - * @param string $stat Statistic to fetch. - * @return int Statistic value. - */ - public function getStat($stat) - { - return Resque_Stat::get($stat . ':' . $this); - } - - /** - * Inject the logging object into the worker - * - * @param Psr\Log\LoggerInterface $logger - */ - public function setLogger(Psr\Log\LoggerInterface $logger) - { - $this->logger = $logger; - } + /** + * Register signal handlers that a worker should respond to. + * + * TERM: Shutdown immediately and stop processing jobs. + * INT: Shutdown immediately and stop processing jobs. + * QUIT: Shutdown after the current job finishes processing. + * USR1: Kill the forked child immediately and continue processing jobs. + */ + private function registerSigHandlers() + { + if (!function_exists('pcntl_signal')) { + return; + } + + pcntl_signal(SIGTERM, [$this, 'shutDownNow']); + pcntl_signal(SIGINT, [$this, 'shutDownNow']); + pcntl_signal(SIGQUIT, [$this, 'shutdown']); + pcntl_signal(SIGUSR1, [$this, 'killChild']); + pcntl_signal(SIGUSR2, [$this, 'pauseProcessing']); + pcntl_signal(SIGCONT, [$this, 'unPauseProcessing']); + $this->logger->log(Psr\Log\LogLevel::DEBUG, 'Registered signals'); + } + + /** + * Signal handler callback for USR2, pauses processing of new jobs. + */ + public function pauseProcessing() + { + $this->logger->log(Psr\Log\LogLevel::NOTICE, 'USR2 received; pausing job processing'); + $this->paused = true; + } + + /** + * Signal handler callback for CONT, resumes worker allowing it to pick + * up new jobs. + */ + public function unPauseProcessing() + { + $this->logger->log(Psr\Log\LogLevel::NOTICE, 'CONT received; resuming job processing'); + $this->paused = false; + } + + /** + * Schedule a worker for shutdown. Will finish processing the current job + * and when the timeout interval is reached, the worker will shut down. + */ + public function shutdown() + { + $this->shutdown = true; + $this->logger->log(Psr\Log\LogLevel::NOTICE, 'Shutting down'); + } + + /** + * Force an immediate shutdown of the worker, killing any child jobs + * currently running. + */ + public function shutdownNow() + { + $this->shutdown(); + $this->killChild(); + } + + /** + * Kill a forked child job immediately. The job it is processing will not + * be completed. + */ + public function killChild() + { + if (!$this->child) { + $this->logger->log(Psr\Log\LogLevel::DEBUG, 'No child to kill.'); + return; + } + + $this->logger->log(Psr\Log\LogLevel::INFO, 'Killing child at {child}', ['child' => $this->child]); + if (exec('ps -o pid,state -p ' . $this->child, $output, $returnCode) && $returnCode != 1) { + $this->logger->log(Psr\Log\LogLevel::DEBUG, 'Child {child} found, killing.', ['child' => $this->child]); + posix_kill($this->child, SIGKILL); + $this->child = null; + } else { + $this->logger->log(Psr\Log\LogLevel::INFO, 'Child {child} not found, restarting.', + ['child' => $this->child]); + $this->shutdown(); + } + } + + /** + * Look for any workers which should be running on this server and if + * they're not, remove them from Redis. + * + * This is a form of garbage collection to handle cases where the + * server may have been killed and the Resque workers did not die gracefully + * and therefore leave state information in Redis. + */ + public function pruneDeadWorkers() + { + $workerPids = $this->workerPids(); + $workers = self::all(); + foreach ($workers as $worker) { + if (is_object($worker)) { + list($host, $pid, $queues) = explode(':', (string)$worker, 3); + if ($host != $this->hostname || in_array($pid, $workerPids) || $pid == getmypid()) { + continue; + } + $this->logger->log(Psr\Log\LogLevel::INFO, 'Pruning dead worker: {worker}', + ['worker' => (string)$worker]); + $worker->unregisterWorker(); + } + } + } + + /** + * Return an array of process IDs for all of the Resque workers currently + * running on this machine. + * + * @return array Array of Resque worker process IDs. + */ + public function workerPids() + { + $pids = []; + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + exec('tasklist /v /fi "PID gt 1" /fo csv', $cmdOutput); + foreach ($cmdOutput as $line) { + $a = explode(',', $line); + list(, $pids[],) = str_replace('"', '', explode(',', trim($line), 3)); + } + } else { + exec('ps -A -o pid,command | grep [r]esque', $cmdOutput); + foreach ($cmdOutput as $line) { + list($pids[],) = explode(' ', trim($line), 2); + } + } + return $pids; + } + + /** + * Register this worker in Redis. + */ + public function registerWorker() + { + Resque::redis()->sadd('workers', (string)$this); + Resque::redis()->set('worker:' . (string)$this . ':started', strftime('%a %b %d %H:%M:%S %Z %Y')); + } + + /** + * Unregister this worker in Redis. (shutdown etc) + */ + public function unregisterWorker() + { + if (is_object($this->currentJob)) { + $this->currentJob->fail(new Resque_Job_DirtyExitException); + } + + $id = (string)$this; + Resque::redis()->srem('workers', $id); + Resque::redis()->del('worker:' . $id); + Resque::redis()->del('worker:' . $id . ':started'); + Resque_Stat::clear('processed:' . $id); + Resque_Stat::clear('failed:' . $id); + } + + /** + * Tell Redis which job we're currently working on. + * + * @param object $job Resque_Job instance containing the job we're working on. + */ + public function workingOn(Resque_Job $job) + { + $job->worker = $this; + $this->currentJob = $job; + $job->updateStatus(Resque_Job_Status::STATUS_RUNNING); + $data = json_encode([ + 'queue' => $job->queue, + 'run_at' => strftime('%a %b %d %H:%M:%S %Z %Y'), + 'payload' => $job->payload + ]); + Resque::redis()->set('worker:' . $job->worker, $data); + } + + /** + * Notify Redis that we've finished working on a job, clearing the working + * state and incrementing the job stats. + */ + public function doneWorking() + { + $this->currentJob = null; + Resque_Stat::incr('processed'); + Resque_Stat::incr('processed:' . (string)$this); + Resque::redis()->del('worker:' . (string)$this); + } + + /** + * Generate a string representation of this worker. + * + * @return string String identifier for this worker instance. + */ + public function __toString() + { + return $this->id; + } + + /** + * Return an object describing the job this worker is currently working on. + * + * @return object Object with details of current job. + */ + public function job() + { + $job = Resque::redis()->get('worker:' . $this); + if (!$job) { + return []; + } else { + return json_decode($job, true); + } + } + + /** + * Get a statistic belonging to this worker. + * + * @param string $stat Statistic to fetch. + * @return int Statistic value. + */ + public function getStat($stat) + { + return Resque_Stat::get($stat . ':' . $this); + } + + /** + * Inject the logging object into the worker + * + * @param Psr\Log\LoggerInterface $logger + */ + public function setLogger(Psr\Log\LoggerInterface $logger) + { + $this->logger = $logger; + } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 61d2d7b3..8b7667fc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,24 +1,24 @@ - - - ./test/Resque/ - - + + + ./test/Resque/ + + - - - ./lib/Resque/ - - + + + ./lib/Resque/ + + \ No newline at end of file diff --git a/test/Resque/Tests/EventTest.php b/test/Resque/Tests/EventTest.php index 6e102cf4..060755ef 100644 --- a/test/Resque/Tests/EventTest.php +++ b/test/Resque/Tests/EventTest.php @@ -1,199 +1,201 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_EventTest extends Resque_Tests_TestCase { - private $callbacksHit = array(); - - public function setUp() - { - Test_Job::$called = false; - - // Register a worker to test with - $this->worker = new Resque_Worker('jobs'); - $this->worker->setLogger(new Resque_Log()); - $this->worker->registerWorker(); - } - - public function tearDown() - { - Resque_Event::clearListeners(); - $this->callbacksHit = array(); - } - - public function getEventTestJob() - { - $payload = array( - 'class' => 'Test_Job', - 'args' => array( - array('somevar'), - ), - ); - $job = new Resque_Job('jobs', $payload); - $job->worker = $this->worker; - return $job; - } - - public function eventCallbackProvider() - { - return array( - array('beforePerform', 'beforePerformEventCallback'), - array('afterPerform', 'afterPerformEventCallback'), - array('afterFork', 'afterForkEventCallback'), - ); - } - - /** - * @dataProvider eventCallbackProvider - */ - public function testEventCallbacksFire($event, $callback) - { - Resque_Event::listen($event, array($this, $callback)); - - $job = $this->getEventTestJob(); - $this->worker->perform($job); - $this->worker->work(0); - - $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called'); - } - - public function testBeforeForkEventCallbackFires() - { - $event = 'beforeFork'; - $callback = 'beforeForkEventCallback'; - - Resque_Event::listen($event, array($this, $callback)); - Resque::enqueue('jobs', 'Test_Job', array( - 'somevar' - )); - $job = $this->getEventTestJob(); - $this->worker->work(0); - $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called'); - } - - public function testBeforeEnqueueEventCallbackFires() - { - $event = 'beforeEnqueue'; - $callback = 'beforeEnqueueEventCallback'; - - Resque_Event::listen($event, array($this, $callback)); - Resque::enqueue('jobs', 'Test_Job', array( - 'somevar' - )); - $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called'); - } - - public function testBeforePerformEventCanStopWork() - { - $callback = 'beforePerformEventDontPerformCallback'; - Resque_Event::listen('beforePerform', array($this, $callback)); - - $job = $this->getEventTestJob(); - - $this->assertFalse($job->perform()); - $this->assertContains($callback, $this->callbacksHit, $callback . ' callback was not called'); - $this->assertFalse(Test_Job::$called, 'Job was still performed though Resque_Job_DontPerform was thrown'); - } - - public function testBeforeEnqueueEventStopsJobCreation() - { - $callback = 'beforeEnqueueEventDontCreateCallback'; - Resque_Event::listen('beforeEnqueue', array($this, $callback)); - Resque_Event::listen('afterEnqueue', array($this, 'afterEnqueueEventCallback')); - - $result = Resque::enqueue('test_job', 'TestClass'); - $this->assertContains($callback, $this->callbacksHit, $callback . ' callback was not called'); - $this->assertNotContains('afterEnqueueEventCallback', $this->callbacksHit, 'afterEnqueue was still called, even though it should not have been'); - $this->assertFalse($result); - } - - public function testAfterEnqueueEventCallbackFires() - { - $callback = 'afterEnqueueEventCallback'; - $event = 'afterEnqueue'; - - Resque_Event::listen($event, array($this, $callback)); - Resque::enqueue('jobs', 'Test_Job', array( - 'somevar' - )); - $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called'); - } - - public function testStopListeningRemovesListener() - { - $callback = 'beforePerformEventCallback'; - $event = 'beforePerform'; - - Resque_Event::listen($event, array($this, $callback)); - Resque_Event::stopListening($event, array($this, $callback)); - - $job = $this->getEventTestJob(); - $this->worker->perform($job); - $this->worker->work(0); - - $this->assertNotContains($callback, $this->callbacksHit, - $event . ' callback (' . $callback .') was called though Resque_Event::stopListening was called' - ); - } - - public function beforePerformEventDontPerformCallback($instance) - { - $this->callbacksHit[] = __FUNCTION__; - throw new Resque_Job_DontPerform; - } - - public function beforeEnqueueEventDontCreateCallback($queue, $class, $args, $track = false) - { - $this->callbacksHit[] = __FUNCTION__; - throw new Resque_Job_DontCreate; - } - - public function assertValidEventCallback($function, $job) - { - $this->callbacksHit[] = $function; - if (!$job instanceof Resque_Job) { - $this->fail('Callback job argument is not an instance of Resque_Job'); - } - $args = $job->getArguments(); - $this->assertEquals($args[0], 'somevar'); - } - - public function afterEnqueueEventCallback($class, $args) - { - $this->callbacksHit[] = __FUNCTION__; - $this->assertEquals('Test_Job', $class); - $this->assertEquals(array( - 'somevar', - ), $args); - } - - public function beforeEnqueueEventCallback($job) - { - $this->callbacksHit[] = __FUNCTION__; - } - - public function beforePerformEventCallback($job) - { - $this->assertValidEventCallback(__FUNCTION__, $job); - } - - public function afterPerformEventCallback($job) - { - $this->assertValidEventCallback(__FUNCTION__, $job); - } - - public function beforeForkEventCallback($job) - { - $this->assertValidEventCallback(__FUNCTION__, $job); - } - - public function afterForkEventCallback($job) - { - $this->assertValidEventCallback(__FUNCTION__, $job); - } + private $callbacksHit = []; + + public function setUp() + { + Test_Job::$called = false; + + // Register a worker to test with + $this->worker = new Resque_Worker('jobs'); + $this->worker->setLogger(new Resque_Log()); + $this->worker->registerWorker(); + } + + public function tearDown() + { + Resque_Event::clearListeners(); + $this->callbacksHit = []; + } + + public function getEventTestJob() + { + $payload = [ + 'class' => 'Test_Job', + 'args' => [ + ['somevar'] + ], + ]; + $job = new Resque_Job('jobs', $payload); + $job->worker = $this->worker; + return $job; + } + + public function eventCallbackProvider() + { + return [ + ['beforePerform', 'beforePerformEventCallback'], + ['afterPerform', 'afterPerformEventCallback'], + ['afterFork', 'afterForkEventCallback'] + ]; + } + + /** + * @dataProvider eventCallbackProvider + */ + public function testEventCallbacksFire($event, $callback) + { + Resque_Event::listen($event, [$this, $callback]); + + $job = $this->getEventTestJob(); + $this->worker->perform($job); + $this->worker->work(0); + + $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback . ') was not called'); + } + + public function testBeforeForkEventCallbackFires() + { + $event = 'beforeFork'; + $callback = 'beforeForkEventCallback'; + + Resque_Event::listen($event, [$this, $callback]); + Resque::enqueue('jobs', 'Test_Job', [ + 'somevar' + ]); + $job = $this->getEventTestJob(); + $this->worker->work(0); + $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback . ') was not called'); + } + + public function testBeforeEnqueueEventCallbackFires() + { + $event = 'beforeEnqueue'; + $callback = 'beforeEnqueueEventCallback'; + + Resque_Event::listen($event, [$this, $callback]); + Resque::enqueue('jobs', 'Test_Job', [ + 'somevar' + ]); + $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback . ') was not called'); + } + + public function testBeforePerformEventCanStopWork() + { + $callback = 'beforePerformEventDontPerformCallback'; + Resque_Event::listen('beforePerform', [$this, $callback]); + + $job = $this->getEventTestJob(); + + $this->assertFalse($job->perform()); + $this->assertContains($callback, $this->callbacksHit, $callback . ' callback was not called'); + $this->assertFalse(Test_Job::$called, 'Job was still performed though Resque_Job_DontPerform was thrown'); + } + + public function testBeforeEnqueueEventStopsJobCreation() + { + $callback = 'beforeEnqueueEventDontCreateCallback'; + Resque_Event::listen('beforeEnqueue', [$this, $callback]); + Resque_Event::listen('afterEnqueue', [$this, 'afterEnqueueEventCallback']); + + $result = Resque::enqueue('test_job', 'TestClass'); + $this->assertContains($callback, $this->callbacksHit, $callback . ' callback was not called'); + $this->assertNotContains('afterEnqueueEventCallback', $this->callbacksHit, + 'afterEnqueue was still called, even though it should not have been'); + $this->assertFalse($result); + } + + public function testAfterEnqueueEventCallbackFires() + { + $callback = 'afterEnqueueEventCallback'; + $event = 'afterEnqueue'; + + Resque_Event::listen($event, [$this, $callback]); + Resque::enqueue('jobs', 'Test_Job', [ + 'somevar' + ]); + $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback . ') was not called'); + } + + public function testStopListeningRemovesListener() + { + $callback = 'beforePerformEventCallback'; + $event = 'beforePerform'; + + Resque_Event::listen($event, [$this, $callback]); + Resque_Event::stopListening($event, [$this, $callback]); + + $job = $this->getEventTestJob(); + $this->worker->perform($job); + $this->worker->work(0); + + $this->assertNotContains($callback, $this->callbacksHit, + $event . ' callback (' . $callback . ') was called though Resque_Event::stopListening was called' + ); + } + + public function beforePerformEventDontPerformCallback($instance) + { + $this->callbacksHit[] = __FUNCTION__; + throw new Resque_Job_DontPerform; + } + + public function beforeEnqueueEventDontCreateCallback($queue, $class, $args, $track = false) + { + $this->callbacksHit[] = __FUNCTION__; + throw new Resque_Job_DontCreate; + } + + public function assertValidEventCallback($function, $job) + { + $this->callbacksHit[] = $function; + if (!$job instanceof Resque_Job) { + $this->fail('Callback job argument is not an instance of Resque_Job'); + } + $args = $job->getArguments(); + $this->assertEquals($args[0], 'somevar'); + } + + public function afterEnqueueEventCallback($class, $args) + { + $this->callbacksHit[] = __FUNCTION__; + $this->assertEquals('Test_Job', $class); + $this->assertEquals([ + 'somevar' + ], $args); + } + + public function beforeEnqueueEventCallback($job) + { + $this->callbacksHit[] = __FUNCTION__; + } + + public function beforePerformEventCallback($job) + { + $this->assertValidEventCallback(__FUNCTION__, $job); + } + + public function afterPerformEventCallback($job) + { + $this->assertValidEventCallback(__FUNCTION__, $job); + } + + public function beforeForkEventCallback($job) + { + $this->assertValidEventCallback(__FUNCTION__, $job); + } + + public function afterForkEventCallback($job) + { + $this->assertValidEventCallback(__FUNCTION__, $job); + } } diff --git a/test/Resque/Tests/JobStatusTest.php b/test/Resque/Tests/JobStatusTest.php index d751c37f..79e5c6cd 100644 --- a/test/Resque/Tests/JobStatusTest.php +++ b/test/Resque/Tests/JobStatusTest.php @@ -1,10 +1,11 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_JobStatusTest extends Resque_Tests_TestCase { @@ -13,94 +14,94 @@ class Resque_Tests_JobStatusTest extends Resque_Tests_TestCase */ protected $worker; - public function setUp() - { - parent::setUp(); - - // Register a worker to test with - $this->worker = new Resque_Worker('jobs'); - $this->worker->setLogger(new Resque_Log()); - } - - public function testJobStatusCanBeTracked() - { - $token = Resque::enqueue('jobs', 'Test_Job', null, true); - $status = new Resque_Job_Status($token); - $this->assertTrue($status->isTracking()); - } - - public function testJobStatusIsReturnedViaJobInstance() - { - $token = Resque::enqueue('jobs', 'Test_Job', null, true); - $job = Resque_Job::reserve('jobs'); - $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $job->getStatus()); - } - - public function testQueuedJobReturnsQueuedStatus() - { - $token = Resque::enqueue('jobs', 'Test_Job', null, true); - $status = new Resque_Job_Status($token); - $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $status->get()); - } - - public function testRunningJobReturnsRunningStatus() - { - $token = Resque::enqueue('jobs', 'Failing_Job', null, true); - $job = $this->worker->reserve(); - $this->worker->workingOn($job); - $status = new Resque_Job_Status($token); - $this->assertEquals(Resque_Job_Status::STATUS_RUNNING, $status->get()); - } - - public function testFailedJobReturnsFailedStatus() - { - $token = Resque::enqueue('jobs', 'Failing_Job', null, true); - $this->worker->work(0); - $status = new Resque_Job_Status($token); - $this->assertEquals(Resque_Job_Status::STATUS_FAILED, $status->get()); - } - - public function testCompletedJobReturnsCompletedStatus() - { - $token = Resque::enqueue('jobs', 'Test_Job', null, true); - $this->worker->work(0); - $status = new Resque_Job_Status($token); - $this->assertEquals(Resque_Job_Status::STATUS_COMPLETE, $status->get()); - } - - public function testStatusIsNotTrackedWhenToldNotTo() - { - $token = Resque::enqueue('jobs', 'Test_Job', null, false); - $status = new Resque_Job_Status($token); - $this->assertFalse($status->isTracking()); - } - - public function testStatusTrackingCanBeStopped() - { - Resque_Job_Status::create('test'); - $status = new Resque_Job_Status('test'); - $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $status->get()); - $status->stop(); - $this->assertFalse($status->get()); - } - - public function testRecreatedJobWithTrackingStillTracksStatus() - { - $originalToken = Resque::enqueue('jobs', 'Test_Job', null, true); - $job = $this->worker->reserve(); - - // Mark this job as being worked on to ensure that the new status is still - // waiting. - $this->worker->workingOn($job); - - // Now recreate it - $newToken = $job->recreate(); - - // Make sure we've got a new job returned - $this->assertNotEquals($originalToken, $newToken); - - // Now check the status of the new job - $newJob = Resque_Job::reserve('jobs'); - $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $newJob->getStatus()); - } + public function setUp() + { + parent::setUp(); + + // Register a worker to test with + $this->worker = new Resque_Worker('jobs'); + $this->worker->setLogger(new Resque_Log()); + } + + public function testJobStatusCanBeTracked() + { + $token = Resque::enqueue('jobs', 'Test_Job', null, true); + $status = new Resque_Job_Status($token); + $this->assertTrue($status->isTracking()); + } + + public function testJobStatusIsReturnedViaJobInstance() + { + $token = Resque::enqueue('jobs', 'Test_Job', null, true); + $job = Resque_Job::reserve('jobs'); + $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $job->getStatus()); + } + + public function testQueuedJobReturnsQueuedStatus() + { + $token = Resque::enqueue('jobs', 'Test_Job', null, true); + $status = new Resque_Job_Status($token); + $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $status->get()); + } + + public function testRunningJobReturnsRunningStatus() + { + $token = Resque::enqueue('jobs', 'Failing_Job', null, true); + $job = $this->worker->reserve(); + $this->worker->workingOn($job); + $status = new Resque_Job_Status($token); + $this->assertEquals(Resque_Job_Status::STATUS_RUNNING, $status->get()); + } + + public function testFailedJobReturnsFailedStatus() + { + $token = Resque::enqueue('jobs', 'Failing_Job', null, true); + $this->worker->work(0); + $status = new Resque_Job_Status($token); + $this->assertEquals(Resque_Job_Status::STATUS_FAILED, $status->get()); + } + + public function testCompletedJobReturnsCompletedStatus() + { + $token = Resque::enqueue('jobs', 'Test_Job', null, true); + $this->worker->work(0); + $status = new Resque_Job_Status($token); + $this->assertEquals(Resque_Job_Status::STATUS_COMPLETE, $status->get()); + } + + public function testStatusIsNotTrackedWhenToldNotTo() + { + $token = Resque::enqueue('jobs', 'Test_Job', null, false); + $status = new Resque_Job_Status($token); + $this->assertFalse($status->isTracking()); + } + + public function testStatusTrackingCanBeStopped() + { + Resque_Job_Status::create('test'); + $status = new Resque_Job_Status('test'); + $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $status->get()); + $status->stop(); + $this->assertFalse($status->get()); + } + + public function testRecreatedJobWithTrackingStillTracksStatus() + { + $originalToken = Resque::enqueue('jobs', 'Test_Job', null, true); + $job = $this->worker->reserve(); + + // Mark this job as being worked on to ensure that the new status is still + // waiting. + $this->worker->workingOn($job); + + // Now recreate it + $newToken = $job->recreate(); + + // Make sure we've got a new job returned + $this->assertNotEquals($originalToken, $newToken); + + // Now check the status of the new job + $newJob = Resque_Job::reserve('jobs'); + $this->assertEquals(Resque_Job_Status::STATUS_WAITING, $newJob->getStatus()); + } } \ No newline at end of file diff --git a/test/Resque/Tests/JobTest.php b/test/Resque/Tests/JobTest.php index fb55d13b..04ad3a65 100644 --- a/test/Resque/Tests/JobTest.php +++ b/test/Resque/Tests/JobTest.php @@ -3,444 +3,445 @@ /** * Resque_Job tests. * - * @package Resque/Tests - * @author Chris Boulton - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_JobTest extends Resque_Tests_TestCase { - protected $worker; - - public function setUp() - { - parent::setUp(); - - // Register a worker to test with - $this->worker = new Resque_Worker('jobs'); - $this->worker->setLogger(new Resque_Log()); - $this->worker->registerWorker(); - } - - public function testJobCanBeQueued() - { - $this->assertTrue((bool)Resque::enqueue('jobs', 'Test_Job')); - } - - /** - * @expectedException Resque_RedisException - */ - public function testRedisErrorThrowsExceptionOnJobCreation() - { - $mockCredis = $this->getMockBuilder('Credis_Client') - ->setMethods(['connect', '__call']) - ->getMock(); - $mockCredis->expects($this->any())->method('__call') - ->will($this->throwException(new CredisException('failure'))); - - Resque::setBackend(function($database) use ($mockCredis) { - return new Resque_Redis('localhost:6379', $database, $mockCredis); - }); - Resque::enqueue('jobs', 'This is a test'); - } - - public function testQeueuedJobCanBeReserved() - { - Resque::enqueue('jobs', 'Test_Job'); - - $job = Resque_Job::reserve('jobs'); - if($job == false) { - $this->fail('Job could not be reserved.'); - } - $this->assertEquals('jobs', $job->queue); - $this->assertEquals('Test_Job', $job->payload['class']); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testObjectArgumentsCannotBePassedToJob() - { - $args = new stdClass; - $args->test = 'somevalue'; - Resque::enqueue('jobs', 'Test_Job', $args); - } - - public function testQueuedJobReturnsExactSamePassedInArguments() - { - $args = array( - 'int' => 123, - 'numArray' => array( - 1, - 2, - ), - 'assocArray' => array( - 'key1' => 'value1', - 'key2' => 'value2' - ), - ); - Resque::enqueue('jobs', 'Test_Job', $args); - $job = Resque_Job::reserve('jobs'); - - $this->assertEquals($args, $job->getArguments()); - } - - public function testAfterJobIsReservedItIsRemoved() - { - Resque::enqueue('jobs', 'Test_Job'); - Resque_Job::reserve('jobs'); - $this->assertFalse(Resque_Job::reserve('jobs')); - } - - public function testRecreatedJobMatchesExistingJob() - { - $args = array( - 'int' => 123, - 'numArray' => array( - 1, - 2, - ), - 'assocArray' => array( - 'key1' => 'value1', - 'key2' => 'value2' - ), - ); - - Resque::enqueue('jobs', 'Test_Job', $args); - $job = Resque_Job::reserve('jobs'); - - // Now recreate it - $job->recreate(); - - $newJob = Resque_Job::reserve('jobs'); - $this->assertEquals($job->payload['class'], $newJob->payload['class']); - $this->assertEquals($job->getArguments(), $newJob->getArguments()); - } - - - public function testFailedJobExceptionsAreCaught() - { - $payload = array( - 'class' => 'Failing_Job', - 'args' => null - ); - $job = new Resque_Job('jobs', $payload); - $job->worker = $this->worker; - - $this->worker->perform($job); - - $this->assertEquals(1, Resque_Stat::get('failed')); - $this->assertEquals(1, Resque_Stat::get('failed:'.$this->worker)); - } - - /** - * @expectedException Resque_Exception - */ - public function testJobWithoutPerformMethodThrowsException() - { - Resque::enqueue('jobs', 'Test_Job_Without_Perform_Method'); - $job = $this->worker->reserve(); - $job->worker = $this->worker; - $job->perform(); - } - - /** - * @expectedException Resque_Exception - */ - public function testInvalidJobThrowsException() - { - Resque::enqueue('jobs', 'Invalid_Job'); - $job = $this->worker->reserve(); - $job->worker = $this->worker; - $job->perform(); - } - - public function testJobWithSetUpCallbackFiresSetUp() - { - $payload = array( - 'class' => 'Test_Job_With_SetUp', - 'args' => array( - 'somevar', - 'somevar2', - ), - ); - $job = new Resque_Job('jobs', $payload); - $job->perform(); - - $this->assertTrue(Test_Job_With_SetUp::$called); - } - - public function testJobWithTearDownCallbackFiresTearDown() - { - $payload = array( - 'class' => 'Test_Job_With_TearDown', - 'args' => array( - 'somevar', - 'somevar2', - ), - ); - $job = new Resque_Job('jobs', $payload); - $job->perform(); - - $this->assertTrue(Test_Job_With_TearDown::$called); - } - - public function testNamespaceNaming() { - $fixture = array( - array('test' => 'more:than:one:with:', 'assertValue' => 'more:than:one:with:'), - array('test' => 'more:than:one:without', 'assertValue' => 'more:than:one:without:'), - array('test' => 'resque', 'assertValue' => 'resque:'), - array('test' => 'resque:', 'assertValue' => 'resque:'), - ); - - foreach($fixture as $item) { - Resque_Redis::prefix($item['test']); - $this->assertEquals(Resque_Redis::getPrefix(), $item['assertValue']); - } - } - - public function testJobWithNamespace() - { - Resque_Redis::prefix('php'); - $queue = 'jobs'; - $payload = array('another_value'); - Resque::enqueue($queue, 'Test_Job_With_TearDown', $payload); - - $this->assertEquals(Resque::queues(), array('jobs')); - $this->assertEquals(Resque::size($queue), 1); - - Resque_Redis::prefix('resque'); - $this->assertEquals(Resque::size($queue), 0); - } - - public function testDequeueAll() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue'); - Resque::enqueue($queue, 'Test_Job_Dequeue'); - $this->assertEquals(Resque::size($queue), 2); - $this->assertEquals(Resque::dequeue($queue), 2); - $this->assertEquals(Resque::size($queue), 0); - } - - public function testDequeueMakeSureNotDeleteOthers() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue'); - Resque::enqueue($queue, 'Test_Job_Dequeue'); - $other_queue = 'other_jobs'; - Resque::enqueue($other_queue, 'Test_Job_Dequeue'); - Resque::enqueue($other_queue, 'Test_Job_Dequeue'); - $this->assertEquals(Resque::size($queue), 2); - $this->assertEquals(Resque::size($other_queue), 2); - $this->assertEquals(Resque::dequeue($queue), 2); - $this->assertEquals(Resque::size($queue), 0); - $this->assertEquals(Resque::size($other_queue), 2); - } - - public function testDequeueSpecificItem() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue1'); - Resque::enqueue($queue, 'Test_Job_Dequeue2'); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue2'); - $this->assertEquals(Resque::dequeue($queue, $test), 1); - $this->assertEquals(Resque::size($queue), 1); - } - - public function testDequeueSpecificMultipleItems() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue1'); - Resque::enqueue($queue, 'Test_Job_Dequeue2'); - Resque::enqueue($queue, 'Test_Job_Dequeue3'); - $this->assertEquals(Resque::size($queue), 3); - $test = array('Test_Job_Dequeue2', 'Test_Job_Dequeue3'); - $this->assertEquals(Resque::dequeue($queue, $test), 2); - $this->assertEquals(Resque::size($queue), 1); - } - - public function testDequeueNonExistingItem() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue1'); - Resque::enqueue($queue, 'Test_Job_Dequeue2'); - Resque::enqueue($queue, 'Test_Job_Dequeue3'); - $this->assertEquals(Resque::size($queue), 3); - $test = array('Test_Job_Dequeue4'); - $this->assertEquals(Resque::dequeue($queue, $test), 0); - $this->assertEquals(Resque::size($queue), 3); - } - - public function testDequeueNonExistingItem2() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue1'); - Resque::enqueue($queue, 'Test_Job_Dequeue2'); - Resque::enqueue($queue, 'Test_Job_Dequeue3'); - $this->assertEquals(Resque::size($queue), 3); - $test = array('Test_Job_Dequeue4', 'Test_Job_Dequeue1'); - $this->assertEquals(Resque::dequeue($queue, $test), 1); - $this->assertEquals(Resque::size($queue), 2); - } - - public function testDequeueItemID() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue'); - $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue' => $qid); - $this->assertEquals(Resque::dequeue($queue, $test), 1); - $this->assertEquals(Resque::size($queue), 1); - } - - public function testDequeueWrongItemID() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue'); - $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); - $this->assertEquals(Resque::size($queue), 2); - #qid right but class name is wrong - $test = array('Test_Job_Dequeue1' => $qid); - $this->assertEquals(Resque::dequeue($queue, $test), 0); - $this->assertEquals(Resque::size($queue), 2); - } - - public function testDequeueWrongItemID2() - { - $queue = 'jobs'; - Resque::enqueue($queue, 'Test_Job_Dequeue'); - $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue' => 'r4nD0mH4sh3dId'); - $this->assertEquals(Resque::dequeue($queue, $test), 0); - $this->assertEquals(Resque::size($queue), 2); - } - - public function testDequeueItemWithArg() - { - $queue = 'jobs'; - $arg = array('foo' => 1, 'bar' => 2); - Resque::enqueue($queue, 'Test_Job_Dequeue9'); - Resque::enqueue($queue, 'Test_Job_Dequeue9', $arg); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue9' => $arg); - $this->assertEquals(Resque::dequeue($queue, $test), 1); - #$this->assertEquals(Resque::size($queue), 1); - } - - public function testDequeueSeveralItemsWithArgs() - { - // GIVEN - $queue = 'jobs'; - $args = array('foo' => 1, 'bar' => 10); - $removeArgs = array('foo' => 1, 'bar' => 2); - Resque::enqueue($queue, 'Test_Job_Dequeue9', $args); - Resque::enqueue($queue, 'Test_Job_Dequeue9', $removeArgs); - Resque::enqueue($queue, 'Test_Job_Dequeue9', $removeArgs); - $this->assertEquals(Resque::size($queue), 3); - - // WHEN - $test = array('Test_Job_Dequeue9' => $removeArgs); - $removedItems = Resque::dequeue($queue, $test); - - // THEN - $this->assertEquals($removedItems, 2); - $this->assertEquals(Resque::size($queue), 1); - $item = Resque::pop($queue); - $this->assertInternalType('array', $item['args']); - $this->assertEquals(10, $item['args'][0]['bar'], 'Wrong items were dequeued from queue!'); - } - - public function testDequeueItemWithUnorderedArg() - { - $queue = 'jobs'; - $arg = array('foo' => 1, 'bar' => 2); - $arg2 = array('bar' => 2, 'foo' => 1); - Resque::enqueue($queue, 'Test_Job_Dequeue'); - Resque::enqueue($queue, 'Test_Job_Dequeue', $arg); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue' => $arg2); - $this->assertEquals(Resque::dequeue($queue, $test), 1); - $this->assertEquals(Resque::size($queue), 1); - } - - public function testDequeueItemWithiWrongArg() - { - $queue = 'jobs'; - $arg = array('foo' => 1, 'bar' => 2); - $arg2 = array('foo' => 2, 'bar' => 3); - Resque::enqueue($queue, 'Test_Job_Dequeue'); - Resque::enqueue($queue, 'Test_Job_Dequeue', $arg); - $this->assertEquals(Resque::size($queue), 2); - $test = array('Test_Job_Dequeue' => $arg2); - $this->assertEquals(Resque::dequeue($queue, $test), 0); - $this->assertEquals(Resque::size($queue), 2); - } - - public function testUseDefaultFactoryToGetJobInstance() - { - $payload = array( - 'class' => 'Some_Job_Class', - 'args' => null - ); - $job = new Resque_Job('jobs', $payload); - $instance = $job->getInstance(); - $this->assertInstanceOf('Some_Job_Class', $instance); - } - - public function testUseFactoryToGetJobInstance() - { - $payload = array( - 'class' => 'Some_Job_Class', - 'args' => array(array()) - ); - $job = new Resque_Job('jobs', $payload); - $factory = new Some_Stub_Factory(); - $job->setJobFactory($factory); - $instance = $job->getInstance(); - $this->assertInstanceOf('Resque_JobInterface', $instance); - } - - public function testDoNotUseFactoryToGetInstance() - { - $payload = array( - 'class' => 'Some_Job_Class', - 'args' => array(array()) - ); - $job = new Resque_Job('jobs', $payload); - $factory = $this->getMock('Resque_Job_FactoryInterface'); - $testJob = $this->getMock('Resque_JobInterface'); - $factory->expects(self::never())->method('create')->will(self::returnValue($testJob)); - $instance = $job->getInstance(); - $this->assertInstanceOf('Resque_JobInterface', $instance); - } + protected $worker; + + public function setUp() + { + parent::setUp(); + + // Register a worker to test with + $this->worker = new Resque_Worker('jobs'); + $this->worker->setLogger(new Resque_Log()); + $this->worker->registerWorker(); + } + + public function testJobCanBeQueued() + { + $this->assertTrue((bool)Resque::enqueue('jobs', 'Test_Job')); + } + + /** + * @expectedException Resque_RedisException + */ + public function testRedisErrorThrowsExceptionOnJobCreation() + { + $mockCredis = $this->getMockBuilder('Credis_Client') + ->setMethods(['connect', '__call']) + ->getMock(); + $mockCredis->expects($this->any())->method('__call') + ->will($this->throwException(new CredisException('failure'))); + + Resque::setBackend(function ($database) use ($mockCredis) { + return new Resque_Redis('localhost:6379', $database, $mockCredis); + }); + Resque::enqueue('jobs', 'This is a test'); + } + + public function testQeueuedJobCanBeReserved() + { + Resque::enqueue('jobs', 'Test_Job'); + + $job = Resque_Job::reserve('jobs'); + if ($job == false) { + $this->fail('Job could not be reserved.'); + } + $this->assertEquals('jobs', $job->queue); + $this->assertEquals('Test_Job', $job->payload['class']); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testObjectArgumentsCannotBePassedToJob() + { + $args = new stdClass; + $args->test = 'somevalue'; + Resque::enqueue('jobs', 'Test_Job', $args); + } + + public function testQueuedJobReturnsExactSamePassedInArguments() + { + $args = [ + 'int' => 123, + 'numArray' => [ + 1, + 2 + ], + 'assocArray' => [ + 'key1' => 'value1', + 'key2' => 'value2' + ] + ]; + Resque::enqueue('jobs', 'Test_Job', $args); + $job = Resque_Job::reserve('jobs'); + + $this->assertEquals($args, $job->getArguments()); + } + + public function testAfterJobIsReservedItIsRemoved() + { + Resque::enqueue('jobs', 'Test_Job'); + Resque_Job::reserve('jobs'); + $this->assertFalse(Resque_Job::reserve('jobs')); + } + + public function testRecreatedJobMatchesExistingJob() + { + $args = [ + 'int' => 123, + 'numArray' => [ + 1, + 2 + ], + 'assocArray' => [ + 'key1' => 'value1', + 'key2' => 'value2' + ] + ]; + + Resque::enqueue('jobs', 'Test_Job', $args); + $job = Resque_Job::reserve('jobs'); + + // Now recreate it + $job->recreate(); + + $newJob = Resque_Job::reserve('jobs'); + $this->assertEquals($job->payload['class'], $newJob->payload['class']); + $this->assertEquals($job->getArguments(), $newJob->getArguments()); + } + + + public function testFailedJobExceptionsAreCaught() + { + $payload = [ + 'class' => 'Failing_Job', + 'args' => null + ]; + $job = new Resque_Job('jobs', $payload); + $job->worker = $this->worker; + + $this->worker->perform($job); + + $this->assertEquals(1, Resque_Stat::get('failed')); + $this->assertEquals(1, Resque_Stat::get('failed:' . $this->worker)); + } + + /** + * @expectedException Resque_Exception + */ + public function testJobWithoutPerformMethodThrowsException() + { + Resque::enqueue('jobs', 'Test_Job_Without_Perform_Method'); + $job = $this->worker->reserve(); + $job->worker = $this->worker; + $job->perform(); + } + + /** + * @expectedException Resque_Exception + */ + public function testInvalidJobThrowsException() + { + Resque::enqueue('jobs', 'Invalid_Job'); + $job = $this->worker->reserve(); + $job->worker = $this->worker; + $job->perform(); + } + + public function testJobWithSetUpCallbackFiresSetUp() + { + $payload = [ + 'class' => 'Test_Job_With_SetUp', + 'args' => [ + 'somevar', + 'somevar2' + ] + ]; + $job = new Resque_Job('jobs', $payload); + $job->perform(); + + $this->assertTrue(Test_Job_With_SetUp::$called); + } + + public function testJobWithTearDownCallbackFiresTearDown() + { + $payload = [ + 'class' => 'Test_Job_With_TearDown', + 'args' => [ + 'somevar', + 'somevar2' + ] + ]; + $job = new Resque_Job('jobs', $payload); + $job->perform(); + + $this->assertTrue(Test_Job_With_TearDown::$called); + } + + public function testNamespaceNaming() + { + $fixture = [ + ['test' => 'more:than:one:with:', 'assertValue' => 'more:than:one:with:'], + ['test' => 'more:than:one:without', 'assertValue' => 'more:than:one:without:'], + ['test' => 'resque', 'assertValue' => 'resque:'], + ['test' => 'resque:', 'assertValue' => 'resque:'] + ]; + + foreach ($fixture as $item) { + Resque_Redis::prefix($item['test']); + $this->assertEquals(Resque_Redis::getPrefix(), $item['assertValue']); + } + } + + public function testJobWithNamespace() + { + Resque_Redis::prefix('php'); + $queue = 'jobs'; + $payload = ['another_value']; + Resque::enqueue($queue, 'Test_Job_With_TearDown', $payload); + + $this->assertEquals(Resque::queues(), ['jobs']); + $this->assertEquals(Resque::size($queue), 1); + + Resque_Redis::prefix('resque'); + $this->assertEquals(Resque::size($queue), 0); + } + + public function testDequeueAll() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + Resque::enqueue($queue, 'Test_Job_Dequeue'); + $this->assertEquals(Resque::size($queue), 2); + $this->assertEquals(Resque::dequeue($queue), 2); + $this->assertEquals(Resque::size($queue), 0); + } + + public function testDequeueMakeSureNotDeleteOthers() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + Resque::enqueue($queue, 'Test_Job_Dequeue'); + $other_queue = 'other_jobs'; + Resque::enqueue($other_queue, 'Test_Job_Dequeue'); + Resque::enqueue($other_queue, 'Test_Job_Dequeue'); + $this->assertEquals(Resque::size($queue), 2); + $this->assertEquals(Resque::size($other_queue), 2); + $this->assertEquals(Resque::dequeue($queue), 2); + $this->assertEquals(Resque::size($queue), 0); + $this->assertEquals(Resque::size($other_queue), 2); + } + + public function testDequeueSpecificItem() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue1'); + Resque::enqueue($queue, 'Test_Job_Dequeue2'); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue2']; + $this->assertEquals(Resque::dequeue($queue, $test), 1); + $this->assertEquals(Resque::size($queue), 1); + } + + public function testDequeueSpecificMultipleItems() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue1'); + Resque::enqueue($queue, 'Test_Job_Dequeue2'); + Resque::enqueue($queue, 'Test_Job_Dequeue3'); + $this->assertEquals(Resque::size($queue), 3); + $test = ['Test_Job_Dequeue2', 'Test_Job_Dequeue3']; + $this->assertEquals(Resque::dequeue($queue, $test), 2); + $this->assertEquals(Resque::size($queue), 1); + } + + public function testDequeueNonExistingItem() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue1'); + Resque::enqueue($queue, 'Test_Job_Dequeue2'); + Resque::enqueue($queue, 'Test_Job_Dequeue3'); + $this->assertEquals(Resque::size($queue), 3); + $test = ['Test_Job_Dequeue4']; + $this->assertEquals(Resque::dequeue($queue, $test), 0); + $this->assertEquals(Resque::size($queue), 3); + } + + public function testDequeueNonExistingItem2() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue1'); + Resque::enqueue($queue, 'Test_Job_Dequeue2'); + Resque::enqueue($queue, 'Test_Job_Dequeue3'); + $this->assertEquals(Resque::size($queue), 3); + $test = ['Test_Job_Dequeue4', 'Test_Job_Dequeue1']; + $this->assertEquals(Resque::dequeue($queue, $test), 1); + $this->assertEquals(Resque::size($queue), 2); + } + + public function testDequeueItemID() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue' => $qid]; + $this->assertEquals(Resque::dequeue($queue, $test), 1); + $this->assertEquals(Resque::size($queue), 1); + } + + public function testDequeueWrongItemID() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); + $this->assertEquals(Resque::size($queue), 2); + #qid right but class name is wrong + $test = ['Test_Job_Dequeue1' => $qid]; + $this->assertEquals(Resque::dequeue($queue, $test), 0); + $this->assertEquals(Resque::size($queue), 2); + } + + public function testDequeueWrongItemID2() + { + $queue = 'jobs'; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + $qid = Resque::enqueue($queue, 'Test_Job_Dequeue'); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue' => 'r4nD0mH4sh3dId']; + $this->assertEquals(Resque::dequeue($queue, $test), 0); + $this->assertEquals(Resque::size($queue), 2); + } + + public function testDequeueItemWithArg() + { + $queue = 'jobs'; + $arg = ['foo' => 1, 'bar' => 2]; + Resque::enqueue($queue, 'Test_Job_Dequeue9'); + Resque::enqueue($queue, 'Test_Job_Dequeue9', $arg); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue9' => $arg]; + $this->assertEquals(Resque::dequeue($queue, $test), 1); + #$this->assertEquals(Resque::size($queue), 1); + } + + public function testDequeueSeveralItemsWithArgs() + { + // GIVEN + $queue = 'jobs'; + $args = ['foo' => 1, 'bar' => 10]; + $removeArgs = ['foo' => 1, 'bar' => 2]; + Resque::enqueue($queue, 'Test_Job_Dequeue9', $args); + Resque::enqueue($queue, 'Test_Job_Dequeue9', $removeArgs); + Resque::enqueue($queue, 'Test_Job_Dequeue9', $removeArgs); + $this->assertEquals(Resque::size($queue), 3); + + // WHEN + $test = ['Test_Job_Dequeue9' => $removeArgs]; + $removedItems = Resque::dequeue($queue, $test); + + // THEN + $this->assertEquals($removedItems, 2); + $this->assertEquals(Resque::size($queue), 1); + $item = Resque::pop($queue); + $this->assertInternalType('array', $item['args']); + $this->assertEquals(10, $item['args'][0]['bar'], 'Wrong items were dequeued from queue!'); + } + + public function testDequeueItemWithUnorderedArg() + { + $queue = 'jobs'; + $arg = ['foo' => 1, 'bar' => 2]; + $arg2 = ['bar' => 2, 'foo' => 1]; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + Resque::enqueue($queue, 'Test_Job_Dequeue', $arg); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue' => $arg2]; + $this->assertEquals(Resque::dequeue($queue, $test), 1); + $this->assertEquals(Resque::size($queue), 1); + } + + public function testDequeueItemWithiWrongArg() + { + $queue = 'jobs'; + $arg = ['foo' => 1, 'bar' => 2]; + $arg2 = ['foo' => 2, 'bar' => 3]; + Resque::enqueue($queue, 'Test_Job_Dequeue'); + Resque::enqueue($queue, 'Test_Job_Dequeue', $arg); + $this->assertEquals(Resque::size($queue), 2); + $test = ['Test_Job_Dequeue' => $arg2]; + $this->assertEquals(Resque::dequeue($queue, $test), 0); + $this->assertEquals(Resque::size($queue), 2); + } + + public function testUseDefaultFactoryToGetJobInstance() + { + $payload = [ + 'class' => 'Some_Job_Class', + 'args' => null + ]; + $job = new Resque_Job('jobs', $payload); + $instance = $job->getInstance(); + $this->assertInstanceOf('Some_Job_Class', $instance); + } + + public function testUseFactoryToGetJobInstance() + { + $payload = [ + 'class' => 'Some_Job_Class', + 'args' => [[]] + ]; + $job = new Resque_Job('jobs', $payload); + $factory = new Some_Stub_Factory(); + $job->setJobFactory($factory); + $instance = $job->getInstance(); + $this->assertInstanceOf('Resque_JobInterface', $instance); + } + + public function testDoNotUseFactoryToGetInstance() + { + $payload = [ + 'class' => 'Some_Job_Class', + 'args' => [[]] + ]; + $job = new Resque_Job('jobs', $payload); + $factory = $this->getMock('Resque_Job_FactoryInterface'); + $testJob = $this->getMock('Resque_JobInterface'); + $factory->expects(self::never())->method('create')->will(self::returnValue($testJob)); + $instance = $job->getInstance(); + $this->assertInstanceOf('Resque_JobInterface', $instance); + } } class Some_Job_Class implements Resque_JobInterface { - /** - * @return bool - */ - public function perform() - { - return true; - } + /** + * @return bool + */ + public function perform() + { + return true; + } } class Some_Stub_Factory implements Resque_Job_FactoryInterface { - /** - * @param $className - * @param array $args - * @param $queue - * @return Resque_JobInterface - */ - public function create($className, $args, $queue) - { - return new Some_Job_Class(); - } + /** + * @param $className + * @param array $args + * @param $queue + * @return Resque_JobInterface + */ + public function create($className, $args, $queue) + { + return new Some_Job_Class(); + } } diff --git a/test/Resque/Tests/LogTest.php b/test/Resque/Tests/LogTest.php index db97b160..f7f571da 100644 --- a/test/Resque/Tests/LogTest.php +++ b/test/Resque/Tests/LogTest.php @@ -1,31 +1,32 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_LogTest extends Resque_Tests_TestCase { - public function testLogInterpolate() - { - $logger = new Resque_Log(); - $actual = $logger->interpolate('string {replace}', array('replace' => 'value')); - $expected = 'string value'; + public function testLogInterpolate() + { + $logger = new Resque_Log(); + $actual = $logger->interpolate('string {replace}', ['replace' => 'value']); + $expected = 'string value'; - $this->assertEquals($expected, $actual); - } + $this->assertEquals($expected, $actual); + } - public function testLogInterpolateMutiple() - { - $logger = new Resque_Log(); - $actual = $logger->interpolate( - 'string {replace1} {replace2}', - array('replace1' => 'value1', 'replace2' => 'value2') - ); - $expected = 'string value1 value2'; + public function testLogInterpolateMutiple() + { + $logger = new Resque_Log(); + $actual = $logger->interpolate( + 'string {replace1} {replace2}', + ['replace1' => 'value1', 'replace2' => 'value2'] + ); + $expected = 'string value1 value2'; - $this->assertEquals($expected, $actual); - } + $this->assertEquals($expected, $actual); + } } diff --git a/test/Resque/Tests/RedisTest.php b/test/Resque/Tests/RedisTest.php index 55b5e17d..03cf08a5 100644 --- a/test/Resque/Tests/RedisTest.php +++ b/test/Resque/Tests/RedisTest.php @@ -1,197 +1,270 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_RedisTest extends Resque_Tests_TestCase { - /** - * @expectedException Resque_RedisException - */ - public function testRedisExceptionsAreSurfaced() - { - $mockCredis = $this->getMockBuilder('Credis_Client') - ->setMethods(['connect', '__call']) - ->getMock(); - $mockCredis->expects($this->any())->method('__call') - ->will($this->throwException(new CredisException('failure'))); + /** + * @expectedException Resque_RedisException + */ + public function testRedisExceptionsAreSurfaced() + { + $mockCredis = $this->getMockBuilder('Credis_Client') + ->setMethods(['connect', '__call']) + ->getMock(); + $mockCredis->expects($this->any())->method('__call') + ->will($this->throwException(new CredisException('failure'))); - Resque::setBackend(function($database) use ($mockCredis) { - return new Resque_Redis('localhost:6379', $database, $mockCredis); - }); - Resque::redis()->ping(); - } + Resque::setBackend(function ($database) use ($mockCredis) { + return new Resque_Redis('localhost:6379', $database, $mockCredis); + }); + Resque::redis()->ping(); + } - /** - * These DNS strings are considered valid. - * - * @return array - */ - public function validDsnStringProvider() - { - return array( - // Input , Expected output - array('', array( - 'localhost', - Resque_Redis::DEFAULT_PORT, - false, - false, false, - array(), - )), - array('localhost', array( - 'localhost', - Resque_Redis::DEFAULT_PORT, - false, - false, false, - array(), - )), - array('localhost:1234', array( - 'localhost', - 1234, - false, - false, false, - array(), - )), - array('localhost:1234/2', array( - 'localhost', - 1234, - 2, - false, false, - array(), - )), - array('redis://foobar', array( - 'foobar', - Resque_Redis::DEFAULT_PORT, - false, - false, false, - array(), - )), - array('redis://foobar/', array( - 'foobar', - Resque_Redis::DEFAULT_PORT, - false, - false, false, - array(), - )), - array('redis://foobar:1234', array( - 'foobar', - 1234, - false, - false, false, - array(), - )), - array('redis://foobar:1234/15', array( - 'foobar', - 1234, - 15, - false, false, - array(), - )), - array('redis://foobar:1234/0', array( - 'foobar', - 1234, - 0, - false, false, - array(), - )), - array('redis://user@foobar:1234', array( - 'foobar', - 1234, - false, - 'user', false, - array(), - )), - array('redis://user@foobar:1234/15', array( - 'foobar', - 1234, - 15, - 'user', false, - array(), - )), - array('redis://user:pass@foobar:1234', array( - 'foobar', - 1234, - false, - 'user', 'pass', - array(), - )), - array('redis://user:pass@foobar:1234?x=y&a=b', array( - 'foobar', - 1234, - false, - 'user', 'pass', - array('x' => 'y', 'a' => 'b'), - )), - array('redis://:pass@foobar:1234?x=y&a=b', array( - 'foobar', - 1234, - false, - false, 'pass', - array('x' => 'y', 'a' => 'b'), - )), - array('redis://user@foobar:1234?x=y&a=b', array( - 'foobar', - 1234, - false, - 'user', false, - array('x' => 'y', 'a' => 'b'), - )), - array('redis://foobar:1234?x=y&a=b', array( - 'foobar', - 1234, - false, - false, false, - array('x' => 'y', 'a' => 'b'), - )), - array('redis://user@foobar:1234/12?x=y&a=b', array( - 'foobar', - 1234, - 12, - 'user', false, - array('x' => 'y', 'a' => 'b'), - )), - array('tcp://user@foobar:1234/12?x=y&a=b', array( - 'foobar', - 1234, - 12, - 'user', false, - array('x' => 'y', 'a' => 'b'), - )), - ); - } + /** + * These DNS strings are considered valid. + * + * @return array + */ + public function validDsnStringProvider() + { + return [ + // Input , Expected output + [ + '', + [ + 'localhost', + Resque_Redis::DEFAULT_PORT, + false, + false, + false, + [] + ] + ], + [ + 'localhost', + [ + 'localhost', + Resque_Redis::DEFAULT_PORT, + false, + false, + false, + [] + ] + ], + [ + 'localhost:1234', + [ + 'localhost', + 1234, + false, + false, + false, + [] + ] + ], + [ + 'localhost:1234/2', + [ + 'localhost', + 1234, + 2, + false, + false, + [] + ] + ], + [ + 'redis://foobar', + [ + 'foobar', + Resque_Redis::DEFAULT_PORT, + false, + false, + false, + [] + ] + ], + [ + 'redis://foobar/', + [ + 'foobar', + Resque_Redis::DEFAULT_PORT, + false, + false, + false, + [] + ] + ], + [ + 'redis://foobar:1234', + [ + 'foobar', + 1234, + false, + false, + false, + [] + ] + ], + [ + 'redis://foobar:1234/15', + [ + 'foobar', + 1234, + 15, + false, + false, + [] + ] + ], + [ + 'redis://foobar:1234/0', + [ + 'foobar', + 1234, + 0, + false, + false, + [] + ] + ], + [ + 'redis://user@foobar:1234', + [ + 'foobar', + 1234, + false, + 'user', + false, + [] + ] + ], + [ + 'redis://user@foobar:1234/15', + [ + 'foobar', + 1234, + 15, + 'user', + false, + [] + ] + ], + [ + 'redis://user:pass@foobar:1234', + [ + 'foobar', + 1234, + false, + 'user', + 'pass', + [] + ] + ], + [ + 'redis://user:pass@foobar:1234?x=y&a=b', + [ + 'foobar', + 1234, + false, + 'user', + 'pass', + ['x' => 'y', 'a' => 'b'] + ] + ], + [ + 'redis://:pass@foobar:1234?x=y&a=b', + [ + 'foobar', + 1234, + false, + false, + 'pass', + ['x' => 'y', 'a' => 'b'] + ] + ], + [ + 'redis://user@foobar:1234?x=y&a=b', + [ + 'foobar', + 1234, + false, + 'user', + false, + ['x' => 'y', 'a' => 'b'] + ] + ], + [ + 'redis://foobar:1234?x=y&a=b', + [ + 'foobar', + 1234, + false, + false, + false, + ['x' => 'y', 'a' => 'b'] + ] + ], + [ + 'redis://user@foobar:1234/12?x=y&a=b', + [ + 'foobar', + 1234, + 12, + 'user', + false, + ['x' => 'y', 'a' => 'b'] + ] + ], + [ + 'tcp://user@foobar:1234/12?x=y&a=b', + [ + 'foobar', + 1234, + 12, + 'user', + false, + ['x' => 'y', 'a' => 'b'] + ] + ] + ]; + } - /** - * These DSN values should throw exceptions - * @return array - */ - public function bogusDsnStringProvider() - { - return array( - array('http://foo.bar/'), - array('user:@foobar:1234?x=y&a=b'), - array('foobar:1234?x=y&a=b'), - ); - } + /** + * These DSN values should throw exceptions + * @return array + */ + public function bogusDsnStringProvider() + { + return [ + ['http://foo.bar/'], + ['user:@foobar:1234?x=y&a=b'], + ['foobar:1234?x=y&a=b'] + ]; + } - /** - * @dataProvider validDsnStringProvider - */ - public function testParsingValidDsnString($dsn, $expected) - { - $result = Resque_Redis::parseDsn($dsn); - $this->assertEquals($expected, $result); - } + /** + * @dataProvider validDsnStringProvider + */ + public function testParsingValidDsnString($dsn, $expected) + { + $result = Resque_Redis::parseDsn($dsn); + $this->assertEquals($expected, $result); + } - /** - * @dataProvider bogusDsnStringProvider - * @expectedException InvalidArgumentException - */ - public function testParsingBogusDsnStringThrowsException($dsn) - { - // The next line should throw an InvalidArgumentException - $result = Resque_Redis::parseDsn($dsn); - } + /** + * @dataProvider bogusDsnStringProvider + * @expectedException InvalidArgumentException + */ + public function testParsingBogusDsnStringThrowsException($dsn) + { + // The next line should throw an InvalidArgumentException + $result = Resque_Redis::parseDsn($dsn); + } } \ No newline at end of file diff --git a/test/Resque/Tests/StatTest.php b/test/Resque/Tests/StatTest.php index aa418887..5d505360 100644 --- a/test/Resque/Tests/StatTest.php +++ b/test/Resque/Tests/StatTest.php @@ -1,49 +1,50 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_StatTest extends Resque_Tests_TestCase { - public function testStatCanBeIncremented() - { - Resque_Stat::incr('test_incr'); - Resque_Stat::incr('test_incr'); - $this->assertEquals(2, $this->redis->get('resque:stat:test_incr')); - } + public function testStatCanBeIncremented() + { + Resque_Stat::incr('test_incr'); + Resque_Stat::incr('test_incr'); + $this->assertEquals(2, $this->redis->get('resque:stat:test_incr')); + } - public function testStatCanBeIncrementedByX() - { - Resque_Stat::incr('test_incrX', 10); - Resque_Stat::incr('test_incrX', 11); - $this->assertEquals(21, $this->redis->get('resque:stat:test_incrX')); - } + public function testStatCanBeIncrementedByX() + { + Resque_Stat::incr('test_incrX', 10); + Resque_Stat::incr('test_incrX', 11); + $this->assertEquals(21, $this->redis->get('resque:stat:test_incrX')); + } - public function testStatCanBeDecremented() - { - Resque_Stat::incr('test_decr', 22); - Resque_Stat::decr('test_decr'); - $this->assertEquals(21, $this->redis->get('resque:stat:test_decr')); - } + public function testStatCanBeDecremented() + { + Resque_Stat::incr('test_decr', 22); + Resque_Stat::decr('test_decr'); + $this->assertEquals(21, $this->redis->get('resque:stat:test_decr')); + } - public function testStatCanBeDecrementedByX() - { - Resque_Stat::incr('test_decrX', 22); - Resque_Stat::decr('test_decrX', 11); - $this->assertEquals(11, $this->redis->get('resque:stat:test_decrX')); - } + public function testStatCanBeDecrementedByX() + { + Resque_Stat::incr('test_decrX', 22); + Resque_Stat::decr('test_decrX', 11); + $this->assertEquals(11, $this->redis->get('resque:stat:test_decrX')); + } - public function testGetStatByName() - { - Resque_Stat::incr('test_get', 100); - $this->assertEquals(100, Resque_Stat::get('test_get')); - } + public function testGetStatByName() + { + Resque_Stat::incr('test_get', 100); + $this->assertEquals(100, Resque_Stat::get('test_get')); + } - public function testGetUnknownStatReturns0() - { - $this->assertEquals(0, Resque_Stat::get('test_get_unknown')); - } + public function testGetUnknownStatReturns0() + { + $this->assertEquals(0, Resque_Stat::get('test_get_unknown')); + } } \ No newline at end of file diff --git a/test/Resque/Tests/TestCase.php b/test/Resque/Tests/TestCase.php index a97f64bf..681842bd 100644 --- a/test/Resque/Tests/TestCase.php +++ b/test/Resque/Tests/TestCase.php @@ -1,30 +1,31 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_TestCase extends PHPUnit_Framework_TestCase { - protected $resque; - protected $redis; + protected $resque; + protected $redis; - public static function setUpBeforeClass() - { - date_default_timezone_set('UTC'); - } + public static function setUpBeforeClass() + { + date_default_timezone_set('UTC'); + } - public function setUp() - { - $config = file_get_contents(REDIS_CONF); - preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches); - $this->redis = new Credis_Client('localhost', $matches[1]); + public function setUp() + { + $config = file_get_contents(REDIS_CONF); + preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches); + $this->redis = new Credis_Client('localhost', $matches[1]); - Resque::setBackend('redis://localhost:' . $matches[1]); + Resque::setBackend('redis://localhost:' . $matches[1]); - // Flush redis - $this->redis->flushAll(); - } + // Flush redis + $this->redis->flushAll(); + } } diff --git a/test/Resque/Tests/WorkerTest.php b/test/Resque/Tests/WorkerTest.php index 93c0621a..ac7fba20 100644 --- a/test/Resque/Tests/WorkerTest.php +++ b/test/Resque/Tests/WorkerTest.php @@ -1,287 +1,287 @@ - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ class Resque_Tests_WorkerTest extends Resque_Tests_TestCase { - public function testWorkerRegistersInList() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - // Make sure the worker is in the list - $this->assertTrue((bool)$this->redis->sismember('resque:workers', (string)$worker)); - } - - public function testGetAllWorkers() - { - $num = 3; - // Register a few workers - for($i = 0; $i < $num; ++$i) { - $worker = new Resque_Worker('queue_' . $i); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - } - - // Now try to get them - $this->assertEquals($num, count(Resque_Worker::all())); - } - - public function testGetWorkerById() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - $newWorker = Resque_Worker::find((string)$worker); - $this->assertEquals((string)$worker, (string)$newWorker); - } - - public function testInvalidWorkerDoesNotExist() - { - $this->assertFalse(Resque_Worker::exists('blah')); - } - - public function testWorkerCanUnregister() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - $worker->unregisterWorker(); - - $this->assertFalse(Resque_Worker::exists((string)$worker)); - $this->assertEquals(array(), Resque_Worker::all()); - $this->assertEquals(array(), $this->redis->smembers('resque:workers')); - } - - public function testPausedWorkerDoesNotPickUpJobs() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->pauseProcessing(); - Resque::enqueue('jobs', 'Test_Job'); - $worker->work(0); - $worker->work(0); - $this->assertEquals(0, Resque_Stat::get('processed')); - } - - public function testResumedWorkerPicksUpJobs() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->pauseProcessing(); - Resque::enqueue('jobs', 'Test_Job'); - $worker->work(0); - $this->assertEquals(0, Resque_Stat::get('processed')); - $worker->unPauseProcessing(); - $worker->work(0); - $this->assertEquals(1, Resque_Stat::get('processed')); - } - - public function testWorkerCanWorkOverMultipleQueues() - { - $worker = new Resque_Worker(array( - 'queue1', - 'queue2' - )); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - Resque::enqueue('queue1', 'Test_Job_1'); - Resque::enqueue('queue2', 'Test_Job_2'); - - $job = $worker->reserve(); - $this->assertEquals('queue1', $job->queue); - - $job = $worker->reserve(); - $this->assertEquals('queue2', $job->queue); - } - - public function testWorkerWorksQueuesInSpecifiedOrder() - { - $worker = new Resque_Worker(array( - 'high', - 'medium', - 'low' - )); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - // Queue the jobs in a different order - Resque::enqueue('low', 'Test_Job_1'); - Resque::enqueue('high', 'Test_Job_2'); - Resque::enqueue('medium', 'Test_Job_3'); - - // Now check we get the jobs back in the right order - $job = $worker->reserve(); - $this->assertEquals('high', $job->queue); - - $job = $worker->reserve(); - $this->assertEquals('medium', $job->queue); - - $job = $worker->reserve(); - $this->assertEquals('low', $job->queue); - } - - public function testWildcardQueueWorkerWorksAllQueues() - { - $worker = new Resque_Worker('*'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - Resque::enqueue('queue1', 'Test_Job_1'); - Resque::enqueue('queue2', 'Test_Job_2'); - - $job = $worker->reserve(); - $this->assertEquals('queue1', $job->queue); - - $job = $worker->reserve(); - $this->assertEquals('queue2', $job->queue); - } - - public function testWorkerDoesNotWorkOnUnknownQueues() - { - $worker = new Resque_Worker('queue1'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - Resque::enqueue('queue2', 'Test_Job'); - - $this->assertFalse($worker->reserve()); - } - - public function testWorkerClearsItsStatusWhenNotWorking() - { - Resque::enqueue('jobs', 'Test_Job'); - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $job = $worker->reserve(); - $worker->workingOn($job); - $worker->doneWorking(); - $this->assertEquals(array(), $worker->job()); - } - - public function testWorkerRecordsWhatItIsWorkingOn() - { - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - $payload = array( - 'class' => 'Test_Job' - ); - $job = new Resque_Job('jobs', $payload); - $worker->workingOn($job); - - $job = $worker->job(); - $this->assertEquals('jobs', $job['queue']); - if(!isset($job['run_at'])) { - $this->fail('Job does not have run_at time'); - } - $this->assertEquals($payload, $job['payload']); - } - - public function testWorkerErasesItsStatsWhenShutdown() - { - Resque::enqueue('jobs', 'Test_Job'); - Resque::enqueue('jobs', 'Invalid_Job'); - - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->work(0); - $worker->work(0); - - $this->assertEquals(0, $worker->getStat('processed')); - $this->assertEquals(0, $worker->getStat('failed')); - } - - public function testWorkerCleansUpDeadWorkersOnStartup() - { - // Register a good worker - $goodWorker = new Resque_Worker('jobs'); - $goodWorker->setLogger(new Resque_Log()); - $goodWorker->registerWorker(); - $workerId = explode(':', $goodWorker); - - // Register some bad workers - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->setId($workerId[0].':1:jobs'); - $worker->registerWorker(); - - $worker = new Resque_Worker(array('high', 'low')); - $worker->setLogger(new Resque_Log()); - $worker->setId($workerId[0].':2:high,low'); - $worker->registerWorker(); - - $this->assertEquals(3, count(Resque_Worker::all())); - - $goodWorker->pruneDeadWorkers(); - - // There should only be $goodWorker left now - $this->assertEquals(1, count(Resque_Worker::all())); - } - - public function testDeadWorkerCleanUpDoesNotCleanUnknownWorkers() - { - // Register a bad worker on this machine - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $workerId = explode(':', $worker); - $worker->setId($workerId[0].':1:jobs'); - $worker->registerWorker(); - - // Register some other false workers - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->setId('my.other.host:1:jobs'); - $worker->registerWorker(); - - $this->assertEquals(2, count(Resque_Worker::all())); - - $worker->pruneDeadWorkers(); - - // my.other.host should be left - $workers = Resque_Worker::all(); - $this->assertEquals(1, count($workers)); - $this->assertEquals((string)$worker, (string)$workers[0]); - } - - public function testWorkerFailsUncompletedJobsOnExit() - { - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->registerWorker(); - - $payload = array( - 'class' => 'Test_Job' - ); - $job = new Resque_Job('jobs', $payload); - - $worker->workingOn($job); - $worker->unregisterWorker(); - - $this->assertEquals(1, Resque_Stat::get('failed')); - } + public function testWorkerRegistersInList() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + // Make sure the worker is in the list + $this->assertTrue((bool)$this->redis->sismember('resque:workers', (string)$worker)); + } + + public function testGetAllWorkers() + { + $num = 3; + // Register a few workers + for ($i = 0; $i < $num; ++$i) { + $worker = new Resque_Worker('queue_' . $i); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + } + + // Now try to get them + $this->assertEquals($num, count(Resque_Worker::all())); + } + + public function testGetWorkerById() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + $newWorker = Resque_Worker::find((string)$worker); + $this->assertEquals((string)$worker, (string)$newWorker); + } + + public function testInvalidWorkerDoesNotExist() + { + $this->assertFalse(Resque_Worker::exists('blah')); + } + + public function testWorkerCanUnregister() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + $worker->unregisterWorker(); + + $this->assertFalse(Resque_Worker::exists((string)$worker)); + $this->assertEquals([], Resque_Worker::all()); + $this->assertEquals([], $this->redis->smembers('resque:workers')); + } + + public function testPausedWorkerDoesNotPickUpJobs() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->pauseProcessing(); + Resque::enqueue('jobs', 'Test_Job'); + $worker->work(0); + $worker->work(0); + $this->assertEquals(0, Resque_Stat::get('processed')); + } + + public function testResumedWorkerPicksUpJobs() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->pauseProcessing(); + Resque::enqueue('jobs', 'Test_Job'); + $worker->work(0); + $this->assertEquals(0, Resque_Stat::get('processed')); + $worker->unPauseProcessing(); + $worker->work(0); + $this->assertEquals(1, Resque_Stat::get('processed')); + } + + public function testWorkerCanWorkOverMultipleQueues() + { + $worker = new Resque_Worker([ + 'queue1', + 'queue2' + ]); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + Resque::enqueue('queue1', 'Test_Job_1'); + Resque::enqueue('queue2', 'Test_Job_2'); + + $job = $worker->reserve(); + $this->assertEquals('queue1', $job->queue); + + $job = $worker->reserve(); + $this->assertEquals('queue2', $job->queue); + } + + public function testWorkerWorksQueuesInSpecifiedOrder() + { + $worker = new Resque_Worker([ + 'high', + 'medium', + 'low' + ]); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + // Queue the jobs in a different order + Resque::enqueue('low', 'Test_Job_1'); + Resque::enqueue('high', 'Test_Job_2'); + Resque::enqueue('medium', 'Test_Job_3'); + + // Now check we get the jobs back in the right order + $job = $worker->reserve(); + $this->assertEquals('high', $job->queue); + + $job = $worker->reserve(); + $this->assertEquals('medium', $job->queue); + + $job = $worker->reserve(); + $this->assertEquals('low', $job->queue); + } + + public function testWildcardQueueWorkerWorksAllQueues() + { + $worker = new Resque_Worker('*'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + Resque::enqueue('queue1', 'Test_Job_1'); + Resque::enqueue('queue2', 'Test_Job_2'); + + $job = $worker->reserve(); + $this->assertEquals('queue1', $job->queue); + + $job = $worker->reserve(); + $this->assertEquals('queue2', $job->queue); + } + + public function testWorkerDoesNotWorkOnUnknownQueues() + { + $worker = new Resque_Worker('queue1'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + Resque::enqueue('queue2', 'Test_Job'); + + $this->assertFalse($worker->reserve()); + } + + public function testWorkerClearsItsStatusWhenNotWorking() + { + Resque::enqueue('jobs', 'Test_Job'); + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $job = $worker->reserve(); + $worker->workingOn($job); + $worker->doneWorking(); + $this->assertEquals([], $worker->job()); + } + + public function testWorkerRecordsWhatItIsWorkingOn() + { + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + $payload = [ + 'class' => 'Test_Job' + ]; + $job = new Resque_Job('jobs', $payload); + $worker->workingOn($job); + + $job = $worker->job(); + $this->assertEquals('jobs', $job['queue']); + if (!isset($job['run_at'])) { + $this->fail('Job does not have run_at time'); + } + $this->assertEquals($payload, $job['payload']); + } + + public function testWorkerErasesItsStatsWhenShutdown() + { + Resque::enqueue('jobs', 'Test_Job'); + Resque::enqueue('jobs', 'Invalid_Job'); + + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $worker->work(0); + $worker->work(0); + + $this->assertEquals(0, $worker->getStat('processed')); + $this->assertEquals(0, $worker->getStat('failed')); + } + + public function testWorkerCleansUpDeadWorkersOnStartup() + { + // Register a good worker + $goodWorker = new Resque_Worker('jobs'); + $goodWorker->setLogger(new Resque_Log()); + $goodWorker->registerWorker(); + $workerId = explode(':', $goodWorker); + + // Register some bad workers + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $worker->setId($workerId[0] . ':1:jobs'); + $worker->registerWorker(); + + $worker = new Resque_Worker(['high', 'low']); + $worker->setLogger(new Resque_Log()); + $worker->setId($workerId[0] . ':2:high,low'); + $worker->registerWorker(); + + $this->assertEquals(3, count(Resque_Worker::all())); + + $goodWorker->pruneDeadWorkers(); + + // There should only be $goodWorker left now + $this->assertEquals(1, count(Resque_Worker::all())); + } + + public function testDeadWorkerCleanUpDoesNotCleanUnknownWorkers() + { + // Register a bad worker on this machine + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $workerId = explode(':', $worker); + $worker->setId($workerId[0] . ':1:jobs'); + $worker->registerWorker(); + + // Register some other false workers + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $worker->setId('my.other.host:1:jobs'); + $worker->registerWorker(); + + $this->assertEquals(2, count(Resque_Worker::all())); + + $worker->pruneDeadWorkers(); + + // my.other.host should be left + $workers = Resque_Worker::all(); + $this->assertEquals(1, count($workers)); + $this->assertEquals((string)$worker, (string)$workers[0]); + } + + public function testWorkerFailsUncompletedJobsOnExit() + { + $worker = new Resque_Worker('jobs'); + $worker->setLogger(new Resque_Log()); + $worker->registerWorker(); + + $payload = [ + 'class' => 'Test_Job' + ]; + $job = new Resque_Job('jobs', $payload); + + $worker->workingOn($job); + $worker->unregisterWorker(); + + $this->assertEquals(1, Resque_Stat::get('failed')); + } public function testBlockingListPop() { $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); + $worker->setLogger(new Resque_Log()); $worker->registerWorker(); Resque::enqueue('jobs', 'Test_Job_1'); Resque::enqueue('jobs', 'Test_Job_2'); $i = 1; - while($job = $worker->reserve(true, 1)) - { + while ($job = $worker->reserve(true, 1)) { $this->assertEquals('Test_Job_' . $i, $job->payload['class']); - if($i == 2) { + if ($i == 2) { break; } diff --git a/test/bootstrap.php b/test/bootstrap.php index a4b68377..a92611c2 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -2,9 +2,9 @@ /** * Resque test bootstrap file - sets up a test environment. * - * @package Resque/Tests - * @author Chris Boulton - * @license http://www.opensource.org/licenses/mit-license.php + * @package Resque/Tests + * @author Chris Boulton + * @license http://www.opensource.org/licenses/mit-license.php */ $loader = require __DIR__ . '/../vendor/autoload.php'; @@ -15,24 +15,24 @@ // Attempt to start our own redis instance for tesitng. exec('which redis-server', $output, $returnVar); -if($returnVar != 0) { - echo "Cannot find redis-server in path. Please make sure redis is installed.\n"; - exit(1); +if ($returnVar != 0) { + echo "Cannot find redis-server in path. Please make sure redis is installed.\n"; + exit(1); } exec('cd ' . TEST_MISC . '; redis-server ' . REDIS_CONF, $output, $returnVar); usleep(500000); -if($returnVar != 0) { - echo "Cannot start redis-server.\n"; - exit(1); +if ($returnVar != 0) { + echo "Cannot start redis-server.\n"; + exit(1); } // Get redis port from conf $config = file_get_contents(REDIS_CONF); -if(!preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches)) { - echo "Could not determine redis port from redis.conf"; - exit(1); +if (!preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches)) { + echo "Could not determine redis port from redis.conf"; + exit(1); } Resque::setBackend('localhost:' . $matches[1]); @@ -43,57 +43,59 @@ function killRedis($pid) if (getmypid() !== $pid) { return; // don't kill from a forked worker } - $config = file_get_contents(REDIS_CONF); - if(!preg_match('#^\s*pidfile\s+([^\s]+)#m', $config, $matches)) { - return; - } - - $pidFile = TEST_MISC . '/' . $matches[1]; - if (file_exists($pidFile)) { - $pid = trim(file_get_contents($pidFile)); - posix_kill((int) $pid, 9); - - if(is_file($pidFile)) { - unlink($pidFile); - } - } - - // Remove the redis database - if(!preg_match('#^\s*dir\s+([^\s]+)#m', $config, $matches)) { - return; - } - $dir = $matches[1]; - - if(!preg_match('#^\s*dbfilename\s+([^\s]+)#m', $config, $matches)) { - return; - } - - $filename = TEST_MISC . '/' . $dir . '/' . $matches[1]; - if(is_file($filename)) { - unlink($filename); - } + $config = file_get_contents(REDIS_CONF); + if (!preg_match('#^\s*pidfile\s+([^\s]+)#m', $config, $matches)) { + return; + } + + $pidFile = TEST_MISC . '/' . $matches[1]; + if (file_exists($pidFile)) { + $pid = trim(file_get_contents($pidFile)); + posix_kill((int)$pid, 9); + + if (is_file($pidFile)) { + unlink($pidFile); + } + } + + // Remove the redis database + if (!preg_match('#^\s*dir\s+([^\s]+)#m', $config, $matches)) { + return; + } + $dir = $matches[1]; + + if (!preg_match('#^\s*dbfilename\s+([^\s]+)#m', $config, $matches)) { + return; + } + + $filename = TEST_MISC . '/' . $dir . '/' . $matches[1]; + if (is_file($filename)) { + unlink($filename); + } } + register_shutdown_function('killRedis', getmypid()); -if(function_exists('pcntl_signal')) { - // Override INT and TERM signals, so they do a clean shutdown and also - // clean up redis-server as well. - function sigint() - { - exit; - } - pcntl_signal(SIGINT, 'sigint'); - pcntl_signal(SIGTERM, 'sigint'); +if (function_exists('pcntl_signal')) { + // Override INT and TERM signals, so they do a clean shutdown and also + // clean up redis-server as well. + function sigint() + { + exit; + } + + pcntl_signal(SIGINT, 'sigint'); + pcntl_signal(SIGTERM, 'sigint'); } class Test_Job { - public static $called = false; + public static $called = false; - public function perform() - { - self::$called = true; - } + public function perform() + { + self::$called = true; + } } class Failing_Job_Exception extends Exception @@ -103,10 +105,10 @@ class Failing_Job_Exception extends Exception class Failing_Job { - public function perform() - { - throw new Failing_Job_Exception('Message!'); - } + public function perform() + { + throw new Failing_Job_Exception('Message!'); + } } class Test_Job_Without_Perform_Method @@ -116,33 +118,33 @@ class Test_Job_Without_Perform_Method class Test_Job_With_SetUp { - public static $called = false; - public $args = false; + public static $called = false; + public $args = false; - public function setUp() - { - self::$called = true; - } + public function setUp() + { + self::$called = true; + } - public function perform() - { + public function perform() + { - } + } } class Test_Job_With_TearDown { - public static $called = false; - public $args = false; + public static $called = false; + public $args = false; - public function perform() - { + public function perform() + { - } + } - public function tearDown() - { - self::$called = true; - } + public function tearDown() + { + self::$called = true; + } } \ No newline at end of file From 070f891d1dc3fef48a32b2a62c981c1987bfb97e Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 20 Feb 2017 21:28:09 +1100 Subject: [PATCH 5/6] updated syntax --- bin/resque | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bin/resque b/bin/resque index 8dd96ee1..92385114 100755 --- a/bin/resque +++ b/bin/resque @@ -101,16 +101,14 @@ if ($count > 1) { if ($pid === false || $pid === -1) { $logger->log(Psr\Log\LogLevel::EMERGENCY, 'Could not fork worker {count}', array('count' => $i)); die(); - } // Child, start the worker - else { - if (!$pid) { - $queues = explode(',', $QUEUE); - $worker = new Resque_Worker($queues); - $worker->setLogger($logger); - $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); - $worker->work($interval, $BLOCKING); - break; - } + } elseif (!$pid) { + // Child, start the worker + $queues = explode(',', $QUEUE); + $worker = new Resque_Worker($queues); + $worker->setLogger($logger); + $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); + $worker->work($interval, $BLOCKING); + break; } } } // Start a single worker From f28c078344acfc34ee86cdee6726a2683d786a31 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sat, 25 Feb 2017 17:43:11 +1100 Subject: [PATCH 6/6] changes --- bin/resque | 14 +++++++------- lib/Resque/Worker.php | 26 ++++++++++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/bin/resque b/bin/resque index 92385114..aa94cb47 100755 --- a/bin/resque +++ b/bin/resque @@ -2,12 +2,12 @@ 1) { $PREFIX = getenv('PREFIX'); if (!empty($PREFIX)) { - $logger->log(Psr\Log\LogLevel::INFO, 'Prefix set to {prefix}', array('prefix' => $PREFIX)); + $logger->log(Psr\Log\LogLevel::INFO, 'Prefix set to {prefix}', ['prefix' => $PREFIX]); Resque_Redis::prefix($PREFIX); } @@ -99,14 +99,14 @@ if ($count > 1) { for ($i = 0; $i < $count; ++$i) { $pid = Resque::fork(); if ($pid === false || $pid === -1) { - $logger->log(Psr\Log\LogLevel::EMERGENCY, 'Could not fork worker {count}', array('count' => $i)); + $logger->log(Psr\Log\LogLevel::EMERGENCY, 'Could not fork worker {count}', ['count' => $i]); die(); } elseif (!$pid) { // Child, start the worker $queues = explode(',', $QUEUE); $worker = new Resque_Worker($queues); $worker->setLogger($logger); - $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); + $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', ['worker' => $worker]); $worker->work($interval, $BLOCKING); break; } @@ -123,7 +123,7 @@ else { die('Could not write PID information to ' . $PIDFILE); } - $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', array('worker' => $worker)); + $logger->log(Psr\Log\LogLevel::NOTICE, 'Starting worker {worker}', ['worker' => $worker]); $worker->work($interval, $BLOCKING); } ?> diff --git a/lib/Resque/Worker.php b/lib/Resque/Worker.php index 6a0ae6a1..dfd72003 100644 --- a/lib/Resque/Worker.php +++ b/lib/Resque/Worker.php @@ -46,6 +46,11 @@ class Resque_Worker */ private $currentJob = null; + /** + * @var boolean for display if this worker is working or not + */ + private $working = false; + /** * @var int Process ID of child worker processes. */ @@ -196,7 +201,7 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) // Forked and we're the child. Run the job. if ($this->child === 0 || $this->child === false) { - $status = 'Processing ' . $job->queue . ' since ' . strftime('%F %T'); + $status = 'Processing ' . $job->queue . ' since ' . date('d/m/Y h:i:s a'); $this->updateProcLine($status); $this->logger->log(Psr\Log\LogLevel::INFO, $status); $this->perform($job); @@ -207,7 +212,7 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) if ($this->child > 0) { // Parent process, sit and wait - $status = 'Forked ' . $this->child . ' at ' . strftime('%F %T'); + $status = 'Forked ' . $this->child . ' at ' . date('d/m/Y h:i:s a'); $this->updateProcLine($status); $this->logger->log(Psr\Log\LogLevel::INFO, $status); @@ -257,7 +262,7 @@ public function reserve($blocking = false, $timeout = null) { $queues = $this->queues(); if (!is_array($queues)) { - return; + return false; } if ($blocking === true) { @@ -427,7 +432,7 @@ public function killChild() */ public function pruneDeadWorkers() { - $workerPids = $this->workerPids(); + $workerPids = self::workerPids(); $workers = self::all(); foreach ($workers as $worker) { if (is_object($worker)) { @@ -448,7 +453,7 @@ public function pruneDeadWorkers() * * @return array Array of Resque worker process IDs. */ - public function workerPids() + public static function workerPids() { $pids = []; if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { @@ -501,6 +506,7 @@ public function workingOn(Resque_Job $job) { $job->worker = $this; $this->currentJob = $job; + $this->working = true; $job->updateStatus(Resque_Job_Status::STATUS_RUNNING); $data = json_encode([ 'queue' => $job->queue, @@ -510,6 +516,13 @@ public function workingOn(Resque_Job $job) Resque::redis()->set('worker:' . $job->worker, $data); } + /** + * @return boolean get for the private attribute + */ + public function getWorking(){ + return $this->working; + } + /** * Notify Redis that we've finished working on a job, clearing the working * state and incrementing the job stats. @@ -517,6 +530,7 @@ public function workingOn(Resque_Job $job) public function doneWorking() { $this->currentJob = null; + $this->working = false; Resque_Stat::incr('processed'); Resque_Stat::incr('processed:' . (string)$this); Resque::redis()->del('worker:' . (string)$this); @@ -535,7 +549,7 @@ public function __toString() /** * Return an object describing the job this worker is currently working on. * - * @return object Object with details of current job. + * @return object|array Object with details of current job. */ public function job() {