From a95c24b32e31218ee8efe7de09e6cad60c23eeb2 Mon Sep 17 00:00:00 2001 From: Chris Boulton Date: Thu, 18 Sep 2014 23:20:04 +1000 Subject: [PATCH 1/5] add beforeEnqueue hook that allows the enqueue to be cancelled --- README.md | 12 ++++++ lib/Resque.php | 35 ++++++++++++----- lib/Resque/Job.php | 7 +++- lib/Resque/Job/DontCreate.php | 12 ++++++ test/Resque/Tests/EventTest.php | 69 ++++++++++++++++++++++++--------- 5 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 lib/Resque/Job/DontCreate.php diff --git a/README.md b/README.md index a254bc0..4a96920 100644 --- a/README.md +++ b/README.md @@ -413,6 +413,18 @@ Called whenever a job fails. Arguments passed (in this order) include: * Exception - The exception that was thrown when the job failed * Resque_Job - The job that failed +#### beforeEnqueue #### + +Called immediately before a job is enqueued using the `Resque::enqueue` method. +Arguments passed (in this order) include: + +* Class - string containing the name of the job to be enqueued +* Arguments - array of arguments for the job +* Queue - string containing the name of the queue the job is to be enqueued in +* ID - string containing the token of the job to be enqueued + +You can prevent enqueing of the job by throwing an exception of `Resque_Job_DontCreate`. + #### afterEnqueue #### Called after a job has been queued using the `Resque::enqueue` method. Arguments passed diff --git a/lib/Resque.php b/lib/Resque.php index 6b067ae..908b63e 100644 --- a/lib/Resque.php +++ b/lib/Resque.php @@ -201,17 +201,24 @@ class Resque */ public static function enqueue($queue, $class, $args = null, $trackStatus = false) { - $result = Resque_Job::create($queue, $class, $args, $trackStatus); - if ($result) { - Resque_Event::trigger('afterEnqueue', array( - 'class' => $class, - 'args' => $args, - 'queue' => $queue, - 'id' => $result, - )); + $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 $id; } - return $result; + Resque_Job::create($queue, $class, $args, $trackStatus, $id); + Resque_Event::trigger('afterEnqueue', $hookParams); + + return $id; } /** @@ -341,5 +348,15 @@ class Resque $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/Job.php b/lib/Resque/Job.php index ca21445..1eb8fdb 100755 --- a/lib/Resque/Job.php +++ b/lib/Resque/Job.php @@ -50,14 +50,17 @@ class Resque_Job * * @return string */ - public static function create($queue, $class, $args = null, $monitor = false) + 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.' ); } - $id = md5(uniqid('', true)); Resque::push($queue, array( 'class' => $class, 'args' => array($args), diff --git a/lib/Resque/Job/DontCreate.php b/lib/Resque/Job/DontCreate.php new file mode 100644 index 0000000..31c33cd --- /dev/null +++ b/lib/Resque/Job/DontCreate.php @@ -0,0 +1,12 @@ + + * @license http://www.opensource.org/licenses/mit-license.php + */ +class Resque_Job_DontCreate extends Exception +{ + +} \ No newline at end of file diff --git a/test/Resque/Tests/EventTest.php b/test/Resque/Tests/EventTest.php index 894fd7a..44f31b1 100644 --- a/test/Resque/Tests/EventTest.php +++ b/test/Resque/Tests/EventTest.php @@ -9,11 +9,11 @@ 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()); @@ -38,7 +38,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $job->worker = $this->worker; return $job; } - + public function eventCallbackProvider() { return array( @@ -47,7 +47,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase array('afterFork', 'afterForkEventCallback'), ); } - + /** * @dataProvider eventCallbackProvider */ @@ -58,10 +58,10 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $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'; @@ -76,6 +76,18 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $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'; @@ -87,23 +99,34 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $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')); + + 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'); + } + public function testAfterEnqueueEventCallbackFires() { $callback = 'afterEnqueueEventCallback'; - $event = 'afterEnqueue'; - + $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'; + $event = 'beforePerform'; Resque_Event::listen($event, array($this, $callback)); Resque_Event::stopListening($event, array($this, $callback)); @@ -112,18 +135,23 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $this->worker->perform($job); $this->worker->work(0); - $this->assertNotContains($callback, $this->callbacksHit, + $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; @@ -133,7 +161,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase $args = $job->getArguments(); $this->assertEquals($args[0], 'somevar'); } - + public function afterEnqueueEventCallback($class, $args) { $this->callbacksHit[] = __FUNCTION__; @@ -142,12 +170,17 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase '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); @@ -157,7 +190,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase { $this->assertValidEventCallback(__FUNCTION__, $job); } - + public function afterForkEventCallback($job) { $this->assertValidEventCallback(__FUNCTION__, $job); From e815c687f169addcb023ae108fee939eef9fb300 Mon Sep 17 00:00:00 2001 From: Chris Boulton Date: Thu, 18 Sep 2014 23:20:45 +1000 Subject: [PATCH 2/5] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a77d5be..d3ceb19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Changes by iskandar introduce improved support for using DSNs to connect to Redi * Fix an issue where a lost connection to Redis could cause an infinite loop (atorres757) * Add a helper method to `Resque_Redis` to remove the namespace applied to Redis keys (tonypiper) * Call beforePerform hook before retrieivng an instance of the job class (allows beforePerform to cancel a job with DontPerform before initialising your application) +* Add `beforeEnqueue` hook, called before a job is placed on a queue ## 1.2 (2012-10-13) ## From 2308c3ced47ebecdfcb698332d837812f6942c59 Mon Sep 17 00:00:00 2001 From: Chris Boulton Date: Thu, 18 Sep 2014 23:26:59 +1000 Subject: [PATCH 3/5] update phpdoc --- lib/Resque/Job.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Resque/Job.php b/lib/Resque/Job.php index 1eb8fdb..344c6c1 100755 --- a/lib/Resque/Job.php +++ b/lib/Resque/Job.php @@ -47,6 +47,7 @@ class Resque_Job * @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 */ From 800770522110af8b220c92ca7b2c3fcb425ec745 Mon Sep 17 00:00:00 2001 From: Chris Boulton Date: Mon, 2 Feb 2015 12:27:28 -0800 Subject: [PATCH 4/5] return false when job creation is cancelled by beforeEnqueue --- lib/Resque.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Resque.php b/lib/Resque.php index 908b63e..db7d4f8 100644 --- a/lib/Resque.php +++ b/lib/Resque.php @@ -197,7 +197,7 @@ class Resque * @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 + * @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) { @@ -212,7 +212,7 @@ class Resque Resque_Event::trigger('beforeEnqueue', $hookParams); } catch(Resque_Job_DontCreate $e) { - return $id; + return false; } Resque_Job::create($queue, $class, $args, $trackStatus, $id); From 0c39e2ca7ae5cbc2f6b40c2d90e7ffcf3ab23abf Mon Sep 17 00:00:00 2001 From: Chris Boulton Date: Mon, 2 Feb 2015 12:30:40 -0800 Subject: [PATCH 5/5] ensure false is returned when DontCreate is thrown --- test/Resque/Tests/EventTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Resque/Tests/EventTest.php b/test/Resque/Tests/EventTest.php index 44f31b1..1147c99 100644 --- a/test/Resque/Tests/EventTest.php +++ b/test/Resque/Tests/EventTest.php @@ -106,9 +106,10 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase Resque_Event::listen('beforeEnqueue', array($this, $callback)); Resque_Event::listen('afterEnqueue', array($this, 'afterEnqueueEventCallback')); - Resque::enqueue('test_job', 'TestClass'); + $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()