Периодическое расписание — это круто, это, вероятно, наиболее часто используемое и распространенное расписание cron. Например, рутинные задачи, такие как очистка, отправка информационных бюллетеней по электронной почте, должны выполняться ежедневно/еженедельно/ежемесячно.

Периодический график работает на неопределенный срок. Вопрос: Как нам это остановить? Эта статья о том, как это сделать на самом деле.

Обычно это абстрактный тип данных (ADT) со следующими свойствами.

  • start_date — дата выполнения.
  • duration — период до следующего выполнения.

Мотивация?

  • поощрительная программа с датой окончания...
  • рекламная программа с датой окончания…
  • или, в основном, любые задания, которые заканчиваются в расписании.

Одно из возможных решений — Expires_at

Один из способов решить эту проблему — ввести новое свойство: expires_at. Выполнять задания до сейчас( ) ≥ expires_at. На поверхности вроде работает. Но в то же время это приводит к тому, что расписание становится строгим. После того, как время выполнения пропущено expires_at, его невозможно выполнить повторно.

Это решение работает, если ваши задания допускают досрочное выполнение. Но, к сожалению, большинство запланированных заданий просто не позволяют этого.

Лучший подход — итерация

Интуиция? Вместо того, чтобы сохранять производное значение, более полезно и эффективно хранить значения, которые могут быть получены другими возможными значениями в любое время с помощью формулы.

Таким образом, действительно простой подход называется итерацией, это просто начальное значение, которое увеличивается/уменьшается с постоянным шагом, пока не будет достигнуто конечное значение.

Независимо от даты и времени истечения срока действия, есть преимущество в позднем выполнении задания, если оно выполняется после `next_run`.

Пример:

Проще говоря, указав, сколько раз должно выполняться периодическое расписание, то есть: 5. После 5-кратного выполнения задания оно завершается. Чтобы отслеживать ход выполнения задания, просто добавьте новое свойство число_выполнений.

entry = e(7 days, job);
iteration = 5;
For each 7 days,
[1]: job(); // execution_count = 1
[2]: job(); // execution_count = 2
[3]: job(); // execution_count = 3
[4]: job(); // execution_count = 4
[5]: job(); // execution_count = 5

Необычный подход — итерационный цикл

Основываясь на этой основе, теперь немного оживим. Давайте введем новый параметр: цикл. В рамках итерации может быть n циклов, каждый из которых отличается d периодом/длительностью. Такой дизайн допускает реализацию на основе вех, где он сбрасывается после каждой итерации.

entry = e(7 days, job);
// total executions = 2 * 2
iteration = 2;
cycle = 2;
// iteration 1
[1]: job();  // cycle 1
[2]: job();  // cycle 2
// iteration 2
[1]: job();  // cycle 1 
[2]: job();  // cycle 2

Учитывая свойство: число_выполнений, нетрудно вывести такие значения, как: current_iterationиcurrent_cycle. Используйте арифметическую прогрессию, где a = 0, d = 1.

Let it be the progression of execution_count:
a = 0  // initial value
d = 1  // single job execution per schedule
  +1  +1
  |   |
|---|---| ... 
0   1   2

Давайте подведем итог, учитывая итерацию = 2, цикл = 2,

1. execution_condition = execution_count < iteration * cycle
2. current_iteration = Math.ceil(execution_count/cycle)
3. current_cycle = ((execution_count-1)%cycle) + 1
|-----------------------------------------------------|
| execution_count | current_iteration | current_cycle |
|-----------------------------------------------------|
|         1       |        1          |        1      |
|         2       |        1          |        2      |
|         3       |        2          |        1      |
|         4       |        2          |        2      |
-------------------------------------------------------