Skip to content

Commit

Permalink
[8.x] Add a $failOnTimeouts job property (#37450)
Browse files Browse the repository at this point in the history
* failOnTimeouts

* formatting

* rename

Co-authored-by: Taylor Otwell <taylorotwell@gmail.com>
  • Loading branch information
themsaid and taylorotwell committed May 21, 2021
1 parent 8dcc5aa commit c2204de
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 11 deletions.
10 changes: 10 additions & 0 deletions src/Illuminate/Queue/Jobs/Job.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ public function maxExceptions()
return $this->payload()['maxExceptions'] ?? null;
}

/**
* Determine if the job should fail when it timeouts.
*
* @return bool
*/
public function shouldFailOnTimeout()
{
return $this->payload()['failOnTimeout'] ?? false;
}

/**
* The number of seconds to wait before retrying a job that encountered an uncaught exception.
*
Expand Down
2 changes: 2 additions & 0 deletions src/Illuminate/Queue/Queue.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ protected function createObjectPayload($job, $queue)
'job' => 'Illuminate\Queue\CallQueuedHandler@call',
'maxTries' => $job->tries ?? null,
'maxExceptions' => $job->maxExceptions ?? null,
'failOnTimeout' => $job->failOnTimeout ?? false,
'backoff' => $this->getJobBackoff($job),
'timeout' => $job->timeout ?? null,
'retryUntil' => $this->getJobExpiration($job),
Expand Down Expand Up @@ -244,6 +245,7 @@ protected function createStringPayload($job, $queue, $data)
'job' => $job,
'maxTries' => null,
'maxExceptions' => null,
'failOnTimeout' => false,
'backoff' => null,
'timeout' => null,
'data' => $data,
Expand Down
19 changes: 19 additions & 0 deletions src/Illuminate/Queue/Worker.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ protected function registerTimeoutHandler($job, WorkerOptions $options)
$this->markJobAsFailedIfWillExceedMaxExceptions(
$job->getConnectionName(), $job, $e
);

$this->markJobAsFailedIfItShouldFailOnTimeout(
$job->getConnectionName(), $job, $e
);
}

$this->kill(static::EXIT_ERROR);
Expand Down Expand Up @@ -536,6 +540,21 @@ protected function markJobAsFailedIfWillExceedMaxExceptions($connectionName, $jo
}
}

/**
* Mark the given job as failed if it should fail on timeouts.
*
* @param string $connectionName
* @param \Illuminate\Contracts\Queue\Job $job
* @param \Throwable $e
* @return void
*/
protected function markJobAsFailedIfItShouldFailOnTimeout($connectionName, $job, Throwable $e)
{
if (method_exists($job, 'shouldFailOnTimeout') ? $job->shouldFailOnTimeout() : false) {
$this->failJob($job, $e);
}
}

/**
* Mark the given job as failed and raise the relevant event.
*
Expand Down
4 changes: 2 additions & 2 deletions tests/Queue/QueueBeanstalkdQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function testPushProperlyPushesJobOntoBeanstalkd()
$pheanstalk = $this->queue->getPheanstalk();
$pheanstalk->shouldReceive('useTube')->once()->with('stack')->andReturn($pheanstalk);
$pheanstalk->shouldReceive('useTube')->once()->with('default')->andReturn($pheanstalk);
$pheanstalk->shouldReceive('put')->twice()->with(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), 1024, 0, 60);
$pheanstalk->shouldReceive('put')->twice()->with(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), 1024, 0, 60);

$this->queue->push('foo', ['data'], 'stack');
$this->queue->push('foo', ['data']);
Expand All @@ -62,7 +62,7 @@ public function testDelayedPushProperlyPushesJobOntoBeanstalkd()
$pheanstalk = $this->queue->getPheanstalk();
$pheanstalk->shouldReceive('useTube')->once()->with('stack')->andReturn($pheanstalk);
$pheanstalk->shouldReceive('useTube')->once()->with('default')->andReturn($pheanstalk);
$pheanstalk->shouldReceive('put')->twice()->with(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), Pheanstalk::DEFAULT_PRIORITY, 5, Pheanstalk::DEFAULT_TTR);
$pheanstalk->shouldReceive('put')->twice()->with(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), Pheanstalk::DEFAULT_PRIORITY, 5, Pheanstalk::DEFAULT_TTR);

$this->queue->later(5, 'foo', ['data'], 'stack');
$this->queue->later(5, 'foo', ['data']);
Expand Down
8 changes: 4 additions & 4 deletions tests/Queue/QueueDatabaseQueueUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function testPushProperlyPushesJobOntoDatabase()
$database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class));
$query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) use ($uuid) {
$this->assertSame('default', $array['queue']);
$this->assertSame(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
$this->assertSame(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
$this->assertEquals(0, $array['attempts']);
$this->assertNull($array['reserved_at']);
$this->assertIsInt($array['available_at']);
Expand Down Expand Up @@ -64,7 +64,7 @@ public function testDelayedPushProperlyPushesJobOntoDatabase()
$database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class));
$query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) use ($uuid) {
$this->assertSame('default', $array['queue']);
$this->assertSame(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
$this->assertSame(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
$this->assertEquals(0, $array['attempts']);
$this->assertNull($array['reserved_at']);
$this->assertIsInt($array['available_at']);
Expand Down Expand Up @@ -126,14 +126,14 @@ public function testBulkBatchPushesOntoDatabase()
$query->shouldReceive('insert')->once()->andReturnUsing(function ($records) use ($uuid) {
$this->assertEquals([[
'queue' => 'queue',
'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]),
'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]),
'attempts' => 0,
'reserved_at' => null,
'available_at' => 'available',
'created_at' => 'created',
], [
'queue' => 'queue',
'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'bar', 'job' => 'bar', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data']]),
'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'bar', 'job' => 'bar', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data']]),
'attempts' => 0,
'reserved_at' => null,
'available_at' => 'available',
Expand Down
10 changes: 5 additions & 5 deletions tests/Queue/QueueRedisQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testPushProperlyPushesJobOntoRedis()
$queue->expects($this->once())->method('getRandomId')->willReturn('foo');
$queue->setContainer($container = m::spy(Container::class));
$redis->shouldReceive('connection')->once()->andReturn($redis);
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0]));
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0]));

$id = $queue->push('foo', ['data']);
$this->assertSame('foo', $id);
Expand All @@ -52,7 +52,7 @@ public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook()
$queue->expects($this->once())->method('getRandomId')->willReturn('foo');
$queue->setContainer($container = m::spy(Container::class));
$redis->shouldReceive('connection')->once()->andReturn($redis);
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0]));
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0]));

Queue::createPayloadUsing(function ($connection, $queue, $payload) {
return ['custom' => 'taylor'];
Expand All @@ -79,7 +79,7 @@ public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook()
$queue->expects($this->once())->method('getRandomId')->willReturn('foo');
$queue->setContainer($container = m::spy(Container::class));
$redis->shouldReceive('connection')->once()->andReturn($redis);
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0]));
$redis->shouldReceive('eval')->once()->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0]));

Queue::createPayloadUsing(function ($connection, $queue, $payload) {
return ['custom' => 'taylor'];
Expand Down Expand Up @@ -115,7 +115,7 @@ public function testDelayedPushProperlyPushesJobOntoRedis()
$redis->shouldReceive('zadd')->once()->with(
'queues:default:delayed',
2,
json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])
json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])
);

$id = $queue->later(1, 'foo', ['data']);
Expand Down Expand Up @@ -143,7 +143,7 @@ public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis()
$redis->shouldReceive('zadd')->once()->with(
'queues:default:delayed',
2,
json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])
json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])
);

$queue->later($date, 'foo', ['data']);
Expand Down
6 changes: 6 additions & 0 deletions tests/Queue/QueueWorkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ class WorkerFakeJob implements QueueJobContract
public $released = false;
public $maxTries;
public $maxExceptions;
public $shouldFailOnTimeout = false;
public $uuid;
public $backoff;
public $retryUntil;
Expand Down Expand Up @@ -520,6 +521,11 @@ public function maxExceptions()
return $this->maxExceptions;
}

public function shouldFailOnTimeout()
{
return $this->shouldFailOnTimeout;
}

public function uuid()
{
return $this->uuid;
Expand Down

0 comments on commit c2204de

Please sign in to comment.