Skip to content

Commit e459c08

Browse files
author
Marick van Tuil
committed
wip
1 parent 62c3f40 commit e459c08

File tree

1 file changed

+37
-29
lines changed

1 file changed

+37
-29
lines changed

src/Worker.php

+37-29
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
namespace Stackkit\LaravelGoogleCloudTasksQueue;
66

7+
use Illuminate\Contracts\Debug\ExceptionHandler;
8+
use Illuminate\Queue\Events\JobTimedOut;
79
use Illuminate\Queue\Worker as LaravelWorker;
810
use Illuminate\Queue\WorkerOptions;
11+
use Symfony\Component\ErrorHandler\Error\FatalError;
912

1013
/**
1114
* Custom worker class to handle specific requirements for Google Cloud Tasks.
@@ -14,52 +17,57 @@
1417
* integrate with Google Cloud Tasks, particularly focusing on job timeout
1518
* handling and graceful shutdowns to avoid interrupting the HTTP lifecycle.
1619
*
17-
* Firstly, the 'supportsAsyncSignals', 'listenForSignals', and 'registerTimeoutHandler' methods
18-
* are protected and called within the queue while(true) loop. We want (and need!) to have that
19-
* too in order to support job timeouts. So, to make it work, we create a public method that
20-
* can call the private signal methods.
21-
*
22-
* Secondly, we need to override the 'kill' method because it tends to kill the server process (artisan serve, octane),
23-
* as well as abort the HTTP request from Cloud Tasks. This is not the desired behavior.
24-
* Instead, it should just fire the WorkerStopped event and return a normal status code.
20+
* Firstly, normally job timeouts are handled using the pcntl extension. Since we
21+
* are running in an HTTP environment, we can't use those functions. An alternative
22+
* method is using set_time_limit and when PHP throws the fatal 'Maximum execution time exceeded' error,
23+
* we will handle the job error like how Laravel would if the pcntl alarm had gone off.
2524
*/
2625
class Worker extends LaravelWorker
2726
{
2827
public function process($connectionName, $job, WorkerOptions $options): void
2928
{
30-
if ($this->supportsAsyncSignals()) {
31-
$this->listenForSignals();
29+
assert($job instanceof CloudTasksJob);
3230

33-
pcntl_signal(SIGSEGV, fn () => $this->shouldQuit = true);
31+
set_time_limit(max($this->timeoutForJob($job, $options), 0));
3432

35-
$this->registerTimeoutHandler($job, $options);
36-
}
33+
app(ExceptionHandler::class)->reportable(
34+
fn (FatalError $error) => $this->onFatalError($error, $job, $options)
35+
);
3736

3837
parent::process($connectionName, $job, $options);
38+
}
39+
40+
private function onFatalError(FatalError $error, CloudTasksJob $job, WorkerOptions $options): bool
41+
{
42+
if (fnmatch('Maximum execution time * exceeded', $error->getMessage())) {
43+
$this->onJobTimedOut($job, $options);
3944

40-
if ($this->supportsAsyncSignals()) {
41-
$this->resetTimeoutHandler();
45+
return false;
4246
}
47+
48+
return true;
4349
}
4450

45-
public function kill($status = 0, $options = null): void
51+
private function onJobTimedOut(CloudTasksJob $job, WorkerOptions $options): void
4652
{
47-
if ($this->supportsAsyncSignals()) {
48-
$this->resetTimeoutHandler();
49-
}
53+
$this->markJobAsFailedIfWillExceedMaxAttempts(
54+
$job->getConnectionName(), $job, (int) $options->maxTries, $e = $this->timeoutExceededException($job)
55+
);
5056

51-
parent::stop($status, $options);
57+
$this->markJobAsFailedIfWillExceedMaxExceptions(
58+
$job->getConnectionName(), $job, $e
59+
);
5260

53-
// When running tests, we cannot run exit because it will kill the PHPunit process.
54-
// So, to still test that the application has exited, we will simply rely on the
55-
// WorkerStopped event that is fired when the worker is stopped.
56-
if (! app()->runningUnitTests()) {
57-
if (extension_loaded('posix') && extension_loaded('pcntl')) {
58-
posix_kill(getmypid(), SIGKILL);
59-
}
61+
$this->markJobAsFailedIfItShouldFailOnTimeout(
62+
$job->getConnectionName(), $job, $e
63+
);
6064

61-
exit($status);
62-
}
65+
$this->events->dispatch(new JobTimedOut(
66+
$job->getConnectionName(), $job
67+
));
6368

69+
if (! $job->isDeleted() && ! $job->isReleased() && ! $job->hasFailed()) {
70+
$job->release($this->calculateBackoff($job, $options));
71+
}
6472
}
6573
}

0 commit comments

Comments
 (0)