Permission is granted to copy, distribute and/or modify this document
    under the terms of the GNU Free Documentation License, Version 1.3
    or any later version published by the Free Software Foundation;
    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
    A copy of the license is included in the section entitled "GNU
    Free Documentation License".

###### Cron jobs

Some tasks must be executed regularly: refresh some materialized views, remove old data, ...

For this purpose, one can programmatically implements a "cron job", which will be scheduled by a specific command.

## The command `chill:cron-job:execute`

The command `chill:cron-job:execute` will schedule a task, one by one. In a classical implementation, it should
be executed every 15 minutes (more or less), to ensure that every task can be executed.

   This command should not be executed in parallel. The installer should ensure that two job are executed concurrently.

## How to implements a cron job ?

Implements a `Chill\MainBundle\Cron\CronJobInterface`. Here is an example:

    namespace Chill\MainBundle\Service\Something;

    use Chill\MainBundle\Cron\CronJobInterface;
    use Chill\MainBundle\Entity\CronJobExecution;
    use DateInterval;
    use DateTimeImmutable;
    use Symfony\Component\Clock\ClockInterface;

    class MyCronJob implements CronJobInterface
    {
        function __construct(private ClockInterface $clock) {}

        public function canRun(?CronJobExecution $cronJobExecution): bool
        {
            // the parameter $cronJobExecution contains data about the last execution of the cronjob
            // if it is null, it should be executed immediatly
            if (null === $cronJobExecution) {
                return true;
            }

            if ($cronJobExecution->getKey() !== $this->getKey()) {
                throw new UnexpectedValueException();
            }

            // this cron job should be executed if the last execution is greater than one day, but only during the night

            $now = $clock->now();

            return $cronJobExecution->getLastStart() < $now->sub(new DateInterval('P1D'))
                && in_array($now->format('H'), self::ACCEPTED_HOURS, true)
                // introduce a random component to ensure a roll of task execution when multiple instances are hosted on same machines
                && mt_rand(0, 5) === 0;
        }

        public function getKey(): string
        {
            return 'arbitrary-and-unique-key';
        }

        public function run(array $lastExecutionData): void
        {
            // here, we execute the command

            // we return execution data, which will be served for next execution
            // this data should be easily serializable in a json column: it should contains
            // only int, string, etc. Avoid storing object
            return ['last-execution-id' => 0];
       }
    }

## How are cron job scheduled ?

If the command `chill:cron-job:execute` is run with one or more `job` argument, those jobs are run, **without checking that the job can run** (the method `canRun` is not executed).

If any `job` argument is given, the `CronManager` schedule job with those steps:

* the tasks are ordered, with:
   * a priority is given for tasks that weren't never executed;
   * then, the tasks are ordered, the last executed are the first in the list
* then, for each tasks, and in the given order, the first task where `canRun` return `TRUE` will be executed.

The command `chill:cron-job:execute` execute **only one** task.
