Skip to content

Commit 0d9aea0

Browse files
feat: Use Retry when claiming against deadlock exceptions
When claims hit doctrine "RetryableException", which basically are DeadlockExceptions and LockWaitTimeoutException, an exponential retry mechaism tries a couple of times.
1 parent a9bfec8 commit 0d9aea0

3 files changed

Lines changed: 56 additions & 14 deletions

File tree

Classes/Domain/Scheduler.php

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
namespace Netlogix\JobQueue\Scheduled\Domain;
66

77
use DateTimeImmutable;
8+
use Doctrine\DBAL\Exception\RetryableException;
89
use Doctrine\DBAL\Types\Types;
910
use InvalidArgumentException;
1011
use Neos\Flow\Utility\Algorithms;
1112
use Netlogix\JobQueue\Scheduled\Domain\Model\ScheduledJob;
1213
use Netlogix\JobQueue\Scheduled\DueDateCalculation\TimeBaseForDueDateCalculation;
1314
use Netlogix\JobQueue\Scheduled\Service\Connection;
15+
use Netlogix\Retry\Retry;
1416

1517
use function array_filter;
1618
use function in_array;
@@ -108,20 +110,26 @@ public function next(string $groupName): ?ScheduledJob
108110
SET claimed = :claimed
109111
WHERE claimed = ""
110112
';
111-
$this->dbal
112-
->executeQuery(
113-
$update,
114-
[
115-
'now' => $this->timeBaseForDueDateCalculation->getNow(),
116-
'groupname' => $groupName,
117-
'claimed' => $claim,
118-
],
119-
[
120-
'now' => Types::DATETIME_IMMUTABLE,
121-
'groupname' => Types::STRING,
122-
'claimed' => Types::STRING,
123-
]
124-
);
113+
(new Retry())
114+
/**
115+
* @see http://backoffcalculator.com/?attempts=5&rate=1&interval=0.5
116+
*/
117+
->withExponentialBackoff(retryInterval: 0.5, maxRetries: 5)
118+
->onExceptionsOfType(RetryableException::class)
119+
->task(fn() => $this->dbal
120+
->executeQuery(
121+
$update,
122+
[
123+
'now' => $this->timeBaseForDueDateCalculation->getNow(),
124+
'groupname' => $groupName,
125+
'claimed' => $claim,
126+
],
127+
[
128+
'now' => Types::DATETIME_IMMUTABLE,
129+
'groupname' => Types::STRING,
130+
'claimed' => Types::STRING,
131+
]
132+
));
125133

126134
$select = /** @lang MySQL */ '
127135
SELECT identifier, duedate, queue, job, incarnation, claimed

Tests/Functional/RetrievingTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
namespace Netlogix\JobQueue\Scheduled\Tests\Functional;
55

66
use DateTimeImmutable;
7+
use Doctrine\DBAL\Exception\DeadlockException;
8+
use Doctrine\ORM\EntityManagerInterface;
9+
use Neos\Flow\Persistence\Doctrine\PersistenceManager;
10+
use Neos\Flow\Persistence\PersistenceManagerInterface;
711
use Netlogix\JobQueue\Scheduled\Domain\Model\ScheduledJob;
812
use Netlogix\JobQueue\Scheduled\Domain\Scheduler;
13+
use Netlogix\JobQueue\Scheduled\Service\Connection;
914

1015
class RetrievingTest extends TestCase
1116
{
@@ -117,6 +122,34 @@ public function Scheduled_jobs_can_be_retrieved_only_once(): void
117122
self::assertNull($retrievedJob2);
118123
}
119124

125+
126+
/**
127+
* @test
128+
*/
129+
public function Retry_claiming_when_deadlock_exceptions_happen(): void
130+
{
131+
$connection = self::createMock(Connection::class);
132+
$connection->expects(self::any())
133+
->method('executeQuery')
134+
->willThrowException(self::createStub(DeadlockException::class));
135+
136+
$this->scheduler->injectConnection($connection);
137+
138+
$start = microtime(true);
139+
try {
140+
$this->scheduler->next(Scheduler::DEFAULT_GROUP_NAME);
141+
} catch (DeadlockException $e) {
142+
}
143+
$end = microtime(true);
144+
$delta = $end - $start;
145+
146+
self::assertInstanceOf(DeadlockException::class, $e);
147+
148+
// guesstimated value is 15 and something.
149+
self::assertGreaterThan(14, $delta);
150+
self::assertLessThan(18, $delta);
151+
}
152+
120153
/**
121154
* @test
122155
*/

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"doctrine/orm": "^2.6",
1414
"flowpack/jobqueue-common": "^3.0",
1515
"neos/flow": "~8.3",
16+
"netlogix/retry": "~1.0",
1617
"php": "~8.2 || ~8.3"
1718
},
1819
"require-dev": {

0 commit comments

Comments
 (0)