Skip to content

Commit edb3758

Browse files
committed
New option WorkerOptions::$backoffMultiplier (default: 1) to define a multiplier for backoff time
1 parent 6aa14f8 commit edb3758

File tree

5 files changed

+92
-9
lines changed

5 files changed

+92
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [1.2.0] - 2025-06-25
9+
10+
### Added
11+
12+
- New option `WorkerOptions::$backoffMultiplier` (default: 1) to define a multiplier for backoff time
13+
814
## [1.1.0] - 2025-06-05
915

1016
### Added

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,15 @@ $worker->setLogger(new NullLogger());
117117

118118
// Configure worker options
119119
$options = new WorkerOptions(
120-
name: 'worker', // Worker name
121-
limit: 10, // Max jobs to execute
122-
memoryLimit: 128, // Memory limit in MB
123-
timeLimit: 60, // Time limit in seconds
124-
killFilePath: 10, // File to kill process
125-
sleep: 2, // Sleep time between jobs in seconds
126-
stopNoJob: true, // Stop if no job
127-
backoffTime: 0, // Time to wait before retry failed job
120+
name: 'worker', // Worker name
121+
limit: 10, // Max jobs to execute
122+
memoryLimit: 128, // Memory limit in MB
123+
timeLimit: 60, // Time limit in seconds
124+
killFilePath: 10, // File to kill process
125+
sleep: 2, // Sleep time between jobs in seconds
126+
stopNoJob: true, // Stop if no job
127+
backoffTime: 0, // Time to wait before retry failed job
128+
backoffMultiplier: 1, // Multiplier for backoff time
128129
);
129130

130131
// Create a queue instance

src/Worker.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public function run(QueueInterface $queue, WorkerOptions $options = new WorkerOp
205205
);
206206
} catch (Throwable $exception) {
207207
// Release job
208-
$job->release($options->backoffTime);
208+
$job->release($this->nextDelayAfterFailure($job, $options));
209209

210210
$this->logger?->error(
211211
sprintf(
@@ -237,4 +237,21 @@ public function executeJob(JobInterface $job): void
237237
{
238238
$this->jobHandlerManager->handle($job);
239239
}
240+
241+
/**
242+
* Next delay after job failure.
243+
*
244+
* @param JobInterface $job
245+
* @param WorkerOptions $options
246+
*
247+
* @return int
248+
*/
249+
public function nextDelayAfterFailure(JobInterface $job, WorkerOptions $options): int
250+
{
251+
if ($job->getAttempts() <= 1) {
252+
return $options->backoffTime;
253+
}
254+
255+
return $options->backoffTime * pow(max($options->backoffMultiplier, 1), $job->getAttempts() - 1);
256+
}
240257
}

src/WorkerOptions.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function __construct(
2626
public int|float $sleep = 0,
2727
public int|float $sleepNoJob = 1,
2828
public int $backoffTime = 0,
29+
public int $backoffMultiplier = 1,
2930
) {
3031
}
3132

@@ -47,6 +48,7 @@ public function logOptions(?LoggerInterface $logger): void
4748
$logger?->debug(sprintf('Sleep between job consumption: %s', $this->unit($this->sleep, 'second(s)')));
4849
$logger?->debug(sprintf('Sleep if no job: %s', $this->unit($this->sleepNoJob, 'second(s)')));
4950
$logger?->debug(sprintf('Backoff time: %s', $this->unit($this->backoffTime, 'second(s)')));
51+
$logger?->debug(sprintf('Backoff multiplier: %s', $this->unit($this->backoffMultiplier)));
5052
}
5153

5254
private function unit(int|float $value, ?string $unit = null): string

tests/WorkerTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Berlioz\QueueManager\Worker;
1919
use Berlioz\QueueManager\WorkerExit;
2020
use Berlioz\QueueManager\WorkerOptions;
21+
use PHPUnit\Framework\Attributes\DataProvider;
2122
use PHPUnit\Framework\TestCase;
2223
use Psr\Log\LoggerInterface;
2324
use ReflectionClass;
@@ -164,4 +165,60 @@ public function testRunExitsWhenKillFileExists()
164165

165166
unlink($killFile); // Cleanup
166167
}
168+
169+
public static function provideJobAndOptionsForDelay()
170+
{
171+
return [
172+
[
173+
'jobAttempts' => 1,
174+
'backoffTime' => 30,
175+
'backoffMultiplier' => 2,
176+
'exceptedDelay' => 30,
177+
],
178+
[
179+
'jobAttempts' => 2,
180+
'backoffTime' => 30,
181+
'backoffMultiplier' => 2,
182+
'exceptedDelay' => 60,
183+
],
184+
[
185+
'jobAttempts' => 3,
186+
'backoffTime' => 30,
187+
'backoffMultiplier' => 2,
188+
'exceptedDelay' => 120,
189+
],
190+
[
191+
'jobAttempts' => 4,
192+
'backoffTime' => 30,
193+
'backoffMultiplier' => 2,
194+
'exceptedDelay' => 240,
195+
],
196+
[
197+
'jobAttempts' => 3,
198+
'backoffTime' => 30,
199+
'backoffMultiplier' => 1,
200+
'exceptedDelay' => 30,
201+
],
202+
[
203+
'jobAttempts' => 3,
204+
'backoffTime' => 1,
205+
'backoffMultiplier' => 2,
206+
'exceptedDelay' => 4,
207+
],
208+
];
209+
}
210+
211+
#[DataProvider('provideJobAndOptionsForDelay')]
212+
public function testNextDelayAfterFailure(
213+
int $jobAttempts,
214+
int $backoffTime,
215+
int $backoffMultiplier,
216+
int $exceptedDelay
217+
) {
218+
$options = new WorkerOptions(backoffTime: $backoffTime, backoffMultiplier: $backoffMultiplier);
219+
$jobMock = $this->createMock(JobInterface::class);
220+
$jobMock->method('getAttempts')->willReturn($jobAttempts);
221+
222+
$this->assertEquals($exceptedDelay, $this->worker->nextDelayAfterFailure($jobMock, $options));
223+
}
167224
}

0 commit comments

Comments
 (0)