Skip to content

Commit 3ffdee8

Browse files
committed
Let Cloud Tasks handle task deletion
1 parent 17a9d59 commit 3ffdee8

File tree

6 files changed

+27
-42
lines changed

6 files changed

+27
-42
lines changed

src/CloudTasksJob.php

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,9 @@ public function getTaskName(): string
6969

7070
public function delete(): void
7171
{
72-
// Laravel automatically calls delete() after a job is processed successfully. However, this is
73-
// not what we want to happen in Cloud Tasks because Cloud Tasks will also delete the task upon
74-
// a 200 OK status, which means a task is deleted twice, possibly resulting in errors. So if the
75-
// task was processed successfully (no errors or failures) then we will not delete the task
76-
// manually and will let Cloud Tasks do it.
77-
$successful =
78-
// If the task has failed, we should be able to delete it permanently
79-
$this->hasFailed() === false
80-
// If the task has errored, it should be released, which in process deletes the errored task
81-
&& $this->hasError() === false;
82-
83-
if ($successful) {
84-
return;
85-
}
86-
87-
parent::delete();
88-
89-
$this->driver->delete($this);
72+
// Laravel automatically calls delete() after a job is processed successfully.
73+
// However, this is not what we want to happen in Cloud Tasks because Cloud Tasks
74+
// will also delete the task upon a 200 OK status, which means a task is deleted twice.
9075
}
9176

9277
public function hasError(): bool

src/CloudTasksQueue.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,11 @@ public function delete(CloudTasksJob $job): void
222222

223223
public function release(CloudTasksJob $job, int $delay = 0): void
224224
{
225-
$job->delete();
226-
227-
$payload = $job->getRawBody();
228-
229-
$options = ['delay' => $delay];
230-
231-
$this->pushRaw($payload, $job->getQueue(), $options);
225+
$this->pushRaw(
226+
payload: $job->getRawBody(),
227+
queue: $job->getQueue(),
228+
options: ['delay' => $delay]
229+
);
232230
}
233231

234232
public function getHandler(): string

src/IncomingTask.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static function fromJson(string $payload): self
2626
}
2727
}
2828

29-
public function isEmpty(): bool
29+
public function isInvalid(): bool
3030
{
3131
return $this->task === [];
3232
}

src/TaskHandler.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function handle(?string $task = null): void
2121
{
2222
$task = IncomingTask::fromJson($task ?: request()->getContent());
2323

24-
if ($task->isEmpty()) {
24+
if ($task->isInvalid()) {
2525
abort(422, 'Invalid task payload');
2626
}
2727

@@ -33,7 +33,12 @@ public function handle(?string $task = null): void
3333

3434
$this->config = is_array($config) ? $config : [];
3535

36-
$this->run($task);
36+
// We want to catch any errors so we have more fine-grained control over
37+
// how tasks are retried. Cloud Tasks will retry the task if a 5xx status
38+
// is returned. Because we manually manage retries by releaseing jobs,
39+
// we never want to return a 5xx status as that will result in duplicate
40+
// job attempts.
41+
rescue(fn () => $this->run($task), report: false);
3742
}
3843

3944
private function run(IncomingTask $task): void

tests/Support/FailingJob.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class FailingJob extends BaseJob
1212

1313
public function handle()
1414
{
15+
event(new JobOutput('FailingJob:running'));
1516
throw new Error('simulating a failing job');
1617
}
1718

tests/TaskHandlerTest.php

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,33 +78,31 @@ public function after_max_attempts_it_will_log_to_failed_table()
7878
}
7979

8080
#[Test]
81-
public function after_max_attempts_it_will_delete_the_task()
81+
public function after_max_attempts_it_will_no_longer_execute_the_task()
8282
{
8383
// Arrange
84+
Event::fake([JobOutput::class]);
8485
$job = $this->dispatch(new FailingJob());
8586

8687
// Act & Assert
8788
$releasedJob = $job->runAndGetReleasedJob();
88-
CloudTasksApi::assertDeletedTaskCount(1);
89-
CloudTasksApi::assertTaskDeleted($job->task->getName());
89+
Event::assertDispatched(JobOutput::class, 1);
9090
$this->assertDatabaseCount('failed_jobs', 0);
9191

9292
$releasedJob = $releasedJob->runAndGetReleasedJob();
93-
CloudTasksApi::assertDeletedTaskCount(2);
94-
CloudTasksApi::assertTaskDeleted($job->task->getName());
93+
Event::assertDispatched(JobOutput::class, 2);
9594
$this->assertDatabaseCount('failed_jobs', 0);
9695

9796
$releasedJob->run();
98-
CloudTasksApi::assertDeletedTaskCount(3);
99-
CloudTasksApi::assertTaskDeleted($job->task->getName());
97+
Event::assertDispatched(JobOutput::class, 4);
10098
$this->assertDatabaseCount('failed_jobs', 1);
10199
}
102100

103101
#[Test]
104102
#[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:00:00', 'should_fail' => false]])]
105103
#[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:04:59', 'should_fail' => false]])]
106104
#[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:05:00', 'should_fail' => true]])]
107-
public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the_task(array $args)
105+
public function after_max_retry_until_it_will_log_to_failed_table(array $args)
108106
{
109107
// Arrange
110108
$this->travelTo($args['now']);
@@ -115,8 +113,6 @@ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the
115113
$releasedJob = $job->runAndGetReleasedJob();
116114

117115
// Assert
118-
CloudTasksApi::assertDeletedTaskCount(1);
119-
CloudTasksApi::assertTaskDeleted($job->task->getName());
120116
$this->assertDatabaseCount('failed_jobs', 0);
121117

122118
// Act
@@ -130,6 +126,9 @@ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the
130126
#[Test]
131127
public function test_unlimited_max_attempts()
132128
{
129+
// Assert
130+
Event::fake(JobOutput::class);
131+
133132
// Act
134133
$job = $this->dispatch(new FailingJobWithUnlimitedTries());
135134

@@ -138,8 +137,7 @@ public function test_unlimited_max_attempts()
138137
$job = $job->runAndGetReleasedJob();
139138
}
140139

141-
// -1 because the last job is not run.
142-
CloudTasksApi::assertDeletedTaskCount(51);
140+
Event::assertDispatched(JobOutput::class, 51);
143141
}
144142

145143
#[Test]
@@ -204,9 +202,7 @@ public function failing_jobs_are_released()
204202

205203
$job->run();
206204

207-
CloudTasksApi::assertDeletedTaskCount(1);
208205
CloudTasksApi::assertCreatedTaskCount(2);
209-
CloudTasksApi::assertTaskDeleted($job->task->getName());
210206
Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
211207
return $event->job->attempts() === 1;
212208
});

0 commit comments

Comments
 (0)