Skip to content

Commit 3ad4e94

Browse files
committed
feature #58001 [Scheduler] Add capability to skip missed periodic tasks, only the last schedule will be called (eltharin)
This PR was squashed before being merged into the 7.2 branch. Discussion ---------- [Scheduler] Add capability to skip missed periodic tasks, only the last schedule will be called | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #57858 | License | MIT Allow a Schedule to run only once reccuring messages when a worker break (with stateful enalbed) Commits ------- 9ae569b [Scheduler] Add capability to skip missed periodic tasks, only the last schedule will be called
2 parents 5d47bea + 9ae569b commit 3ad4e94

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

src/Symfony/Component/Scheduler/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.2
5+
---
6+
7+
* Add capability to skip missed periodic tasks, only the last schedule will be called
8+
49
6.4
510
---
611

src/Symfony/Component/Scheduler/Generator/MessageGenerator.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,15 @@ public function getMessages(): \Generator
7474
$yield = false;
7575
}
7676

77-
if ($nextTime = $trigger->getNextRunDate($time)) {
77+
$nextTime = $trigger->getNextRunDate($time);
78+
79+
if ($this->schedule->shouldProcessOnlyLastMissedRun()) {
80+
while ($nextTime < $this->clock->now()) {
81+
$nextTime = $trigger->getNextRunDate($nextTime);
82+
}
83+
}
84+
85+
if ($nextTime) {
7886
$heap->insert([$nextTime, $index, $recurringMessage]);
7987
}
8088

src/Symfony/Component/Scheduler/Schedule.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function __construct(
3131
private ?LockInterface $lock = null;
3232
private ?CacheInterface $state = null;
3333
private bool $shouldRestart = false;
34+
private bool $onlyLastMissed = false;
3435

3536
public function with(RecurringMessage $message, RecurringMessage ...$messages): static
3637
{
@@ -123,6 +124,21 @@ public function getState(): ?CacheInterface
123124
return $this->state;
124125
}
125126

127+
/**
128+
* @return $this
129+
*/
130+
public function processOnlyLastMissedRun(bool $onlyLastMissed): static
131+
{
132+
$this->onlyLastMissed = $onlyLastMissed;
133+
134+
return $this;
135+
}
136+
137+
public function shouldProcessOnlyLastMissedRun(): bool
138+
{
139+
return $this->onlyLastMissed;
140+
}
141+
126142
/**
127143
* @return array<RecurringMessage>
128144
*/

src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,67 @@ public function testCheckpointSavedInBrokenLoop()
204204
$this->assertEquals(self::makeDateTime('22:13:00'), $checkpoint->time());
205205
}
206206

207+
public function testCheckpointSavedInBigBrokenLoop()
208+
{
209+
$clock = new MockClock(self::makeDateTime('22:15:00'));
210+
211+
$message = RecurringMessage::every('1 minute', (object) ['id' => 'message']);
212+
$schedule = (new Schedule())->add($message);
213+
214+
$cache = new ArrayAdapter();
215+
$schedule->stateful($cache);
216+
$checkpoint = new Checkpoint('dummy', cache: $cache);
217+
218+
$scheduler = new MessageGenerator($schedule, 'dummy', clock: $clock, checkpoint: $checkpoint);
219+
220+
// Warmup. The first run is always returns nothing.
221+
$this->assertSame([], iterator_to_array($scheduler->getMessages(), false));
222+
$this->assertEquals(self::makeDateTime('22:15:00'), $checkpoint->time());
223+
224+
$clock->sleep(60 + 10); // 22:16:10
225+
226+
$this->assertCount(1, iterator_to_array($scheduler->getMessages(), false));
227+
228+
$clock->sleep(2 * 60); // 22:18:10
229+
230+
$this->assertCount(2, iterator_to_array($scheduler->getMessages(), false));
231+
232+
$clock->sleep(5 * 60); // 22:23:10
233+
234+
$this->assertCount(5, iterator_to_array($scheduler->getMessages(), false));
235+
236+
$this->assertEquals(self::makeDateTime('22:23:00'), $checkpoint->time());
237+
}
238+
239+
public function testCheckpointSavedInBigBrokenLoopWithOnlyLastMissed()
240+
{
241+
$clock = new MockClock(self::makeDateTime('22:15:00'));
242+
243+
$message = RecurringMessage::every('1 minute', (object) ['id' => 'message']);
244+
$schedule = (new Schedule())->add($message);
245+
246+
$cache = new ArrayAdapter();
247+
$schedule->stateful($cache)->processOnlyLastMissedRun(true);
248+
$checkpoint = new Checkpoint('dummy', cache: $cache);
249+
250+
$scheduler = new MessageGenerator($schedule, 'dummy', clock: $clock, checkpoint: $checkpoint);
251+
252+
// Warmup. The first run is always returns nothing.
253+
$this->assertSame([], iterator_to_array($scheduler->getMessages(), false));
254+
$this->assertEquals(self::makeDateTime('22:15:00'), $clock->now());
255+
256+
$clock->sleep(60 + 10); // 22:16:10
257+
$this->assertCount(1, iterator_to_array($scheduler->getMessages(), false));
258+
259+
$clock->sleep(2 * 60); // 22:18:10
260+
$this->assertCount(1, iterator_to_array($scheduler->getMessages(), false));
261+
262+
$clock->sleep(5 * 60); // 22:23:10
263+
$this->assertCount(1, iterator_to_array($scheduler->getMessages(), false));
264+
265+
$this->assertEquals(self::makeDateTime('22:23:10'), $clock->now());
266+
}
267+
207268
public static function messagesProvider(): \Generator
208269
{
209270
$first = (object) ['id' => 'first'];

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy