Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Scheduler] Potential missing messages if --limit is set  #58758

Open
@FrancoisPog

Description

@FrancoisPog

Symfony version(s) affected

7.1

Description

I'm experiencing an issue with the Scheduler component when setting the --limit option to 1.
I have two tasks defined with the #[AsCronTask('* * * * *')] attribute, so they should run every minute.

Here's what happens :

  1. The first task is executed.
  2. The scheduler saves its checkpoint and stops, as expected due to the limit.
  3. Upon restarting the scheduler, it recalculates its task heap starting from the previously saved checkpoint.
  4. Both the first and second tasks are rescheduled for the next minute.
  5. However, the second task is never executed in subsequent runs.

It appears that the scheduler saves the checkpoint even if not all scheduled tasks have been executed. As a result, the second task is perpetually rescheduled but never runs.

Thank you for your assistance.

How to reproduce

  1. Define two tasks with the #[AsCronTask('* * * * *')] attribute.
  2. Start the scheduler with the --limit=1 option: bin/console scheduler:start --limit=1.
  3. Observe that only the first task is executed.
  4. Restart the scheduler.
  5. Notice that the second task is never executed, even though it's rescheduled.

There is a script simulating this behavior

use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Scheduler\Generator\MessageGenerator;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Cache\CacheInterface;

class FirstMessage
{
}

class SecondMessage
{
}

class MyProvider implements ScheduleProviderInterface
{
    private CacheInterface $cache;

    public function getSchedule(): Schedule
    {
        return (new Schedule())
            ->stateful($this->cache ??= new ArrayAdapter())
            ->add(RecurringMessage::every('5 second', new FirstMessage()))
            ->add(RecurringMessage::every('5 second', new SecondMessage()));
    }
}

function run(): void
{
    static $nth = 0;
    echo 'Run #'.++$nth."\n";

    $generator = new MessageGenerator(new MyProvider(), 'default');

    while (true) {
        // waiting for messages
        foreach ($generator->getMessages() as $context => $message) {
            echo 'Message to execute : '.$message::class."\n";

            return; // Simulate --limit=1
        }
    }
}

run(); // The FirstMessage is executed: ok

run(); // The FirstMessage is again executed 5 seconds after, but the SecondMessage is not executed

run(); // again, the FirstMessage is executed, but the SecondMessage is not executed

Results:

Run #1
Message to execute : FirstMessage
Run #2
Message to execute : FirstMessage
Run #3
Message to execute : FirstMessage

Possible Solution

The checkpoint is saved globally to save the last execution of the scheduler.
Maybe we should consider different checkpoints for each message?

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions