diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e4b81..4deb5aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # 0.4.0 +- Add job status codes so listeners can act on the result of a job's outcome - Add controller plugin to ease push of jobs into queues - BC: job's jsonSerialize() removed in favour of the queue's serializeJob() method - BC: job's metadata field "name" is now reserved for SlmQueue and should not be used by end users diff --git a/docs/3.Jobs.md b/docs/3.Jobs.md index ba63588..7e11e78 100644 --- a/docs/3.Jobs.md +++ b/docs/3.Jobs.md @@ -213,6 +213,65 @@ class MyController extends AbstractActionController } ``` +Job status codes +---------------- + +When using [events](6.Events.md) you might want to hook in the status process of a job. Has +a job successfully been executed or were there errors? The result of a job is expressed in +its status code. SlmQueue defines the following default status codes: + +0. `JOB_STATUS_UNKNOWN` +1. `JOB_STATUS_SUCCESS` +2. `JOB_STATUS_FAILURE` +3. `JOB_STATUS_FAILURE_RECOVERABLE` + +The status codes are stored in the WorkerEvent object (more on that at the +[event section](6.Events.md)). Normally when jobs are completely executed, the status is +success. If any exception is thrown, the status is set to failure. + +```php +use SlmQueue\Job\AbstractJob; + +class SuccessfulJob extends AbstractJob +{ + public function execute() + { + // all is OK + } +} +``` + +```php +use RuntimeException +use SlmQueue\Job\AbstractJob; + +class FailingJob extends AbstractJob +{ + public function execute() + { + throw new RuntimeException('Not going well'); + } +} +``` + +However, if you want to indicate `JOB_STATUS_FAILURE_RECOVERABLE` or you need to introduce +some custom job status codes, you can return a non-NULL value from the Job's `execute()` +method: + +```php +use SlmQueue\Job\AbstractJob; +use SlmQueue\Worker\WorkerEvent; + +class RecoverableJob extends AbstractJob +{ + public function execute() + { + // Ooops, something went wrong? + return WorkerEvent::JOB_STATUS_FAILURE_RECOVERABLE; + } +} +``` + Navigation ---------- diff --git a/docs/6.Events.md b/docs/6.Events.md index f24189b..170e5df 100644 --- a/docs/6.Events.md +++ b/docs/6.Events.md @@ -21,6 +21,25 @@ $em->attach(WorkerEvent::EVENT_PROCESS_JOB_PRE, function(WorkerEvent $e) { In above example, `$em` refers to the event manager inside the worker object: `$em = $worker->getEventManager();`. +Job status codes +---------------- + +When a job is processed, the [job or worker returns a status code](3.Jobs.md#job-status-codes). You +can use a listener to act upon this status, for example to log any failed jobs: + +```php +$logger = $sm->get('logger'); +$em->attach(WorkerEvent::EVENT_PROCESS_JOB_POST, function(WorkerEvent $e) use ($logger) { + $result = $e->getResult(); + if ($result & WorkerEvent::JOB_STATUS_FAILURE) { + $job = $e->getJob(); + $logger->warn(sprintf( + 'Job #%s (%s) failed executing', $job->getId, get_class($job) + )); + } +}); +``` + Using the shared event manager ------------------------------ diff --git a/src/SlmQueue/Worker/AbstractWorker.php b/src/SlmQueue/Worker/AbstractWorker.php index 2c584b0..075e7f8 100644 --- a/src/SlmQueue/Worker/AbstractWorker.php +++ b/src/SlmQueue/Worker/AbstractWorker.php @@ -87,12 +87,14 @@ public function processQueue($queueName, array $options = array()) } $workerEvent->setJob($job); + $workerEvent->setResult(WorkerEvent::JOB_STATUS_UNKNOWN); $eventManager->trigger(WorkerEvent::EVENT_PROCESS_JOB_PRE, $workerEvent); - $this->processJob($job, $queue); + $result = $this->processJob($job, $queue); $count++; + $workerEvent->setResult($result); $eventManager->trigger(WorkerEvent::EVENT_PROCESS_JOB_POST, $workerEvent); // Check for internal stop condition diff --git a/src/SlmQueue/Worker/WorkerEvent.php b/src/SlmQueue/Worker/WorkerEvent.php index 5a165a6..70a1198 100644 --- a/src/SlmQueue/Worker/WorkerEvent.php +++ b/src/SlmQueue/Worker/WorkerEvent.php @@ -19,6 +19,26 @@ class WorkerEvent extends Event const EVENT_PROCESS_JOB_PRE = 'processJob.pre'; const EVENT_PROCESS_JOB_POST = 'processJob.post'; + /** + * Status for unstarted jobs + */ + const JOB_STATUS_UNKNOWN = 0; + + /** + * Status for successfully finished job + */ + const JOB_STATUS_SUCCESS = 1; + + /** + * Status for job that has failed and cannot be processed again + */ + const JOB_STATUS_FAILURE = 2; + + /** + * Status for job that has failed but can be processed again + */ + const JOB_STATUS_FAILURE_RECOVERABLE = 4; + /** * @var QueueInterface */ @@ -29,6 +49,12 @@ class WorkerEvent extends Event */ protected $job; + /** + * Result of the processed job. + * @var int + */ + protected $result; + /** * @param QueueInterface $queue */ @@ -61,4 +87,20 @@ public function getQueue() { return $this->queue; } + + /** + * @param int $result + */ + public function setResult($result) + { + $this->result = $result; + } + + /** + * @return int|null + */ + public function getResult() + { + return $this->result; + } } diff --git a/src/SlmQueue/Worker/WorkerInterface.php b/src/SlmQueue/Worker/WorkerInterface.php index 0145584..015aa38 100644 --- a/src/SlmQueue/Worker/WorkerInterface.php +++ b/src/SlmQueue/Worker/WorkerInterface.php @@ -26,7 +26,7 @@ public function processQueue($queueName, array $options = array()); * * @param JobInterface $job * @param QueueInterface $queue - * @return void + * @return int Status of the job */ public function processJob(JobInterface $job, QueueInterface $queue); } diff --git a/tests/SlmQueueTest/Asset/SimpleWorker.php b/tests/SlmQueueTest/Asset/SimpleWorker.php index fc7a90f..e6a1828 100644 --- a/tests/SlmQueueTest/Asset/SimpleWorker.php +++ b/tests/SlmQueueTest/Asset/SimpleWorker.php @@ -10,6 +10,6 @@ class SimpleWorker extends AbstractWorker { public function processJob(JobInterface $job, QueueInterface $queue) { - $job->execute(); + return $job->execute(); } } diff --git a/tests/SlmQueueTest/Worker/AbstractWorkerTest.php b/tests/SlmQueueTest/Worker/AbstractWorkerTest.php index 5ba1814..d760c19 100644 --- a/tests/SlmQueueTest/Worker/AbstractWorkerTest.php +++ b/tests/SlmQueueTest/Worker/AbstractWorkerTest.php @@ -6,6 +6,7 @@ use SlmQueue\Options\WorkerOptions; use SlmQueue\Worker\WorkerEvent; use SlmQueueTest\Asset\SimpleWorker; +use Zend\EventManager\EventManager; class AbstractWorkerTest extends TestCase { @@ -141,6 +142,27 @@ public function testEventManagerTriggersEvents() $this->worker->processQueue('foo'); } + public function testWorkerSetsJobStatusInEventClass() + { + $eventManager = new EventManager; + $this->worker->setEventManager($eventManager); + + $this->job->expects($this->once()) + ->method('execute') + ->will($this->returnValue(WorkerEvent::JOB_STATUS_SUCCESS)); + + $this->queue->expects($this->once()) + ->method('pop') + ->will($this->returnValue($this->job)); + + $self = $this; + $eventManager->attach(WorkerEvent::EVENT_PROCESS_JOB_POST, function($e) use ($self) { + $self->assertEquals(WorkerEvent::JOB_STATUS_SUCCESS, $e->getResult()); + }); + + $this->worker->processQueue('foo'); + } + public function testMethod_hasMemoryExceeded() { $this->options->setMaxMemory(10000000000); $this->assertFalse($this->worker->isMaxMemoryExceeded());