Skip to content

Commit 0d8ca79

Browse files
authored
Merge pull request #41 from netlogix/feat/reduce-worker-cpu
feat: Run due-job polling via PollScheduler
2 parents 6e6626a + 4b9c132 commit 0d8ca79

2 files changed

Lines changed: 26 additions & 14 deletions

File tree

Classes/Command/SchedulerCommandController.php

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Flowpack\JobQueue\Common\Job\JobManager;
1313
use Neos\Flow\Cli\CommandController;
1414
use Neos\Flow\Log\ThrowableStorageInterface;
15+
use Netlogix\JobQueue\Polling\PollScheduler;
1516
use Netlogix\JobQueue\Pool\Pool;
1617
use Netlogix\JobQueue\Scheduled\Domain\Model\ScheduledJob;
1718
use Netlogix\JobQueue\Scheduled\Domain\SchedulingCoordinator;
@@ -90,14 +91,21 @@ public function pollForIncomingJobsCommand(
9091
outputResults: $outputResults,
9192
preforkSize: $preforkSize
9293
)
93-
->runLoop(function (Pool $pool) use ($groupName, $parallel, $stopPollingAfter, $pollingIntervalInSeconds, &$queueDueJobs): void {
94-
// Check for new jobs in the database and schedule as much as the pool has capacity for
95-
$queueDueJobs = $pool->eventLoop->addPeriodicTimer(
96-
interval: $pollingIntervalInSeconds,
97-
callback: function () use ($pool, $groupName, $parallel) {
98-
$this->queueDueJobs(pool: $pool, groupName: $groupName, parallel: $parallel);
99-
}
94+
->runLoop(function (Pool $pool) use ($groupName, $parallel, $stopPollingAfter, $pollingIntervalInSeconds): void {
95+
$scheduler = null;
96+
97+
// Check for new jobs in the database and schedule as much as the pool has capacity for.
98+
// Capacity check and slot occupation happen synchronously inside queueDueJobs, so the
99+
// periodic poll and the immediate poll on job completion can never exceed $parallel.
100+
$scheduler = PollScheduler::create(
101+
loop: $pool->eventLoop,
102+
tryToPickUpWork: function () use ($pool, $groupName, $parallel, &$scheduler): void {
103+
$this->queueDueJobs(pool: $pool, groupName: $groupName, parallel: $parallel, pollScheduler: $scheduler);
104+
},
105+
hasCapacity: fn () => count($pool) < $parallel,
106+
interval: $pollingIntervalInSeconds
100107
);
108+
$scheduler->start();
101109

102110
// Keep the database connection alive
103111
$ping = $pool->eventLoop->addPeriodicTimer(
@@ -111,8 +119,8 @@ public function pollForIncomingJobsCommand(
111119
if ($stopPollingAfter) {
112120
$pool->eventLoop->addTimer(
113121
interval: $stopPollingAfter,
114-
callback: function () use ($pool, $queueDueJobs, $ping) {
115-
$pool->eventLoop->cancelTimer($queueDueJobs);
122+
callback: function () use ($pool, $scheduler, $ping) {
123+
$scheduler->stop();
116124
$checkForPoolToClear = $pool->eventLoop->addPeriodicTimer(
117125
interval: 1,
118126
callback: function () use ($pool, $ping, &$checkForPoolToClear) {
@@ -132,9 +140,10 @@ public function pollForIncomingJobsCommand(
132140
* @param Pool $pool A pool of subprocesses waiting for new jobs to execute
133141
* @param string $groupName Handle only jobs of this group
134142
* @param int $parallel Number of jobs to handle in parallel
143+
* @param ?PollScheduler $pollScheduler Re-poll immediately once a slot frees up
135144
* @return int Number of handled jobs
136145
*/
137-
protected function queueDueJobs(Pool $pool, string $groupName, int $parallel): int
146+
protected function queueDueJobs(Pool $pool, string $groupName, int $parallel, ?PollScheduler $pollScheduler = null): int
138147
{
139148
$numberOfHandledJobs = 0;
140149
$retry = new SchedulingCoordinator($this->scheduler);
@@ -159,12 +168,14 @@ protected function queueDueJobs(Pool $pool, string $groupName, int $parallel): i
159168
}
160169
);
161170

162-
$process->on(Pool::EVENT_EXIT, function () use ($pool, $retry, $ping, &$numberOfHandledJobs) {
171+
$process->on(Pool::EVENT_EXIT, function () use ($pool, $retry, $ping, $pollScheduler, &$numberOfHandledJobs) {
163172
$pool->eventLoop->cancelTimer($ping);
164173
$numberOfHandledJobs--;
165-
if ($numberOfHandledJobs === 0) {
166-
($numberOfHandledJobs === 0) && $retry->scheduleAll();
167-
}
174+
if ($numberOfHandledJobs === 0) {
175+
$retry->scheduleAll();
176+
}
177+
// A slot just freed up - pick up the next due job without waiting for the next periodic tick.
178+
$pollScheduler?->requestImmediatePoll();
168179
});
169180
$process->on(Pool::EVENT_SUCCESS, fn () => $this->scheduler->release($next));
170181
$process->on(Pool::EVENT_ERROR, fn () => $retry->markJobForRescheduling($next));

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/jobqueue-polling": "^1.0",
1617
"netlogix/jobqueue-pool": "^1.0",
1718
"netlogix/retry": "~1.0",
1819
"php": "~8.2 || ~8.3"

0 commit comments

Comments
 (0)