Skip to content

Commit 931632b

Browse files
authored
Merge pull request #28 from netlogix/feat/in-memory-retry-for-scheduling
fix: Improve database task scheduling retry mechanism
2 parents 523bca5 + 653e22b commit 931632b

2 files changed

Lines changed: 68 additions & 24 deletions

File tree

Classes/Domain/Scheduler.php

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -269,30 +269,37 @@ protected function scheduleJob(ScheduledJob $job): void
269269
job = :job,
270270
claimed = :claimed
271271
MySQL;
272-
$this->dbal
273-
->executeQuery(
274-
$statement,
275-
[
276-
'groupname' => $job->getGroupName(),
277-
'identifier' => $job->getIdentifier(),
278-
'duedate' => $job->getDuedate(),
279-
'queue' => $job->getQueueName(),
280-
'job' => serialize($job->getJob()),
281-
'incarnation' => $job->getIncarnation(),
282-
'claimed' => $job->getClaimed(),
283-
'running' => $job->isRunning() ? 1 : 0,
284-
],
285-
[
286-
'groupname' => Types::STRING,
287-
'identifier' => Types::STRING,
288-
'duedate' => Types::DATETIME_IMMUTABLE,
289-
'queue' => Types::STRING,
290-
'job' => Types::BLOB,
291-
'incarnation' => Types::INTEGER,
292-
'claimed' => Types::STRING,
293-
'running' => Types::BOOLEAN,
294-
]
295-
);
272+
273+
(new Retry())
274+
/**
275+
* @see http://backoffcalculator.com/?attempts=5&rate=1&interval=0.1
276+
*/
277+
->withExponentialBackoff(retryInterval: 0.05, maxRetries: 5)
278+
->onExceptionsOfType(RetryableException::class)
279+
->task(fn() => $this->dbal
280+
->executeQuery(
281+
$statement,
282+
[
283+
'groupname' => $job->getGroupName(),
284+
'identifier' => $job->getIdentifier(),
285+
'duedate' => $job->getDuedate(),
286+
'queue' => $job->getQueueName(),
287+
'job' => serialize($job->getJob()),
288+
'incarnation' => $job->getIncarnation(),
289+
'claimed' => $job->getClaimed(),
290+
'running' => $job->isRunning() ? 1 : 0,
291+
],
292+
[
293+
'groupname' => Types::STRING,
294+
'identifier' => Types::STRING,
295+
'duedate' => Types::DATETIME_IMMUTABLE,
296+
'queue' => Types::STRING,
297+
'job' => Types::BLOB,
298+
'incarnation' => Types::INTEGER,
299+
'claimed' => Types::STRING,
300+
'running' => Types::BOOLEAN,
301+
]
302+
));
296303
// TODO: Find a way to "trigger queueing" without cronjobs. Maybe "dynamic cronjobs" like "at".
297304
// TODO: On Shutdown: Add queueing job to job queue.
298305
}

Tests/Functional/SchedulingTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
namespace Netlogix\JobQueue\Scheduled\Tests\Functional;
55

6+
use Doctrine\DBAL\Exception\DeadlockException;
67
use Netlogix\JobQueue\Scheduled\Domain\Model\ScheduledJob;
78
use Netlogix\JobQueue\Scheduled\Domain\Scheduler;
9+
use Netlogix\JobQueue\Scheduled\Service\Connection;
810

911
class SchedulingTest extends TestCase
1012
{
@@ -103,4 +105,39 @@ public function Scheduled_jobs_contain_due_dates(): void
103105
$scheduledJob->getDuedate()
104106
);
105107
}
108+
109+
/**
110+
* @test
111+
* @depends A_scheduled_job_gets_persisted
112+
*/
113+
public function Scheduling_jobs_retries_RetryableExceptions(): void
114+
{
115+
$connection = self::createMock(Connection::class);
116+
$connection->expects(self::any())
117+
->method('executeQuery')
118+
->willThrowException(self::createStub(DeadlockException::class));
119+
120+
$this->scheduler->injectConnection($connection);
121+
122+
$start = microtime(true);
123+
try {
124+
$this->scheduler->schedule(
125+
new ScheduledJob(
126+
self::getJobQueueJob(),
127+
self::getQueueName(),
128+
self::getDueDate(),
129+
Scheduler::DEFAULT_GROUP_NAME
130+
)
131+
);
132+
} catch (DeadlockException $e) {
133+
}
134+
$end = microtime(true);
135+
$delta = $end - $start;
136+
137+
self::assertInstanceOf(DeadlockException::class, $e);
138+
139+
// guesstimated value is about 1.5
140+
self::assertGreaterThan(1, $delta);
141+
self::assertLessThan(2, $delta);
142+
}
106143
}

0 commit comments

Comments
 (0)