Одной из распространенных проблем с cron является то, что задание cron выполняется дольше, чем интервал между двумя последовательными запусками. В этом случае второй экземпляр задания запустится до завершения первого, что приведет к перекрытию и потенциально неожиданному поведению, например дублированию.
Сценарий состоит из задания, которое запускается каждые 5 секунд с использованием библиотеки CronJob, и функции delay, которая имитирует длительную задачу.
Сценарий использует простой флаг isJobRunning для предотвращения перекрывающихся запусков задания и переменную jobSkippedCount для отслеживания того, сколько раз задание было пропущено из-за перекрытия.
Мы собираемся использовать nanoid, чтобы узнать идентификаторы заданий, и cron, чтобы запланировать задания.
npm install nanoid npm install cron
Давайте углубимся в код:
import { CronJob } from 'cron'
import { nanoid } from 'nanoid'let isJobRunning = false
let jobSkippedCount = 0
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
const job = new CronJob('*/5 * * * * *', async () => {
if (isJobRunning) {
jobSkippedCount++
console.log("__ JOB SKIPPED COUNT __ ", jobSkippedCount)
if (jobSkippedCount > 2) {
/* If a job takes more than 15 seconds
to complete we can stop this job,
throw a error and start a new job */
console.log("__ TIMEOUT __")
jobSkippedCount = 0
isJobRunning = false
}
return
}
isJobRunning = true
try {
const id = nanoid(10)
console.log("\n\n__ JOB RUNNING __ ", id, new Date())
/* Your functionality goes here
I have used a delay function to
simulate a long running job */
await delay(16000)
isJobRunning = false
jobSkippedCount = 0
console.log("__ JOB COMPLETED __ ", id, new Date())
}
catch (err) {
console.error(err);
}
})
job.start();
Есть 3 сценария, которые происходят с этим: наш цикл составляет 5 секунд один раз, а максимальное количество пропусков равно 2.
1. Все гладко
delay(2000) — Задание cron выполняется и завершает свою задачу перед следующим циклом, создавая следующий вывод:
__ JOB RUNNING __ EC6MqpB4FG 2023-04-06T17:53:35.004Z __ JOB COMPLETED __ EC6MqpB4FG 2023-04-06T17:53:37.017Z __ JOB RUNNING __ 6nutPdea6_ 2023-04-06T17:53:40.004Z __ JOB COMPLETED __ 6nutPdea6_ 2023-04-06T17:53:42.005Z
2. Пропустил задание, но нет ТАЙМ-АУТА
delay(7000) — Задание cron выполняется и завершает свою задачу, пропуская один цикл, выдавая следующий результат:
__ JOB RUNNING __ soR8V3YrQp 2023-04-06T17:54:15.006Z __ JOB SKIPPED COUNT __ 1 __ JOB COMPLETED __ soR8V3YrQp 2023-04-06T17:54:22.019Z __ JOB RUNNING __ 8-LBtb85Jq 2023-04-06T17:54:25.002Z __ JOB SKIPPED COUNT __ 1 __ JOB COMPLETED __ 8-LBtb85Jq 2023-04-06T17:54:32.004Z
3. Достигается тайм-аут
delay(17000) — Задание cron не завершается в течение тайм-аута, поэтому запускается новое задание: независимо от того, завершено предыдущее задание или нет. Мы начнем работу после тайм-аута.
Возможно, вам придется убить свою логику, используя эмиттеры событий, чтобы остановить выполнение функции.
__ JOB RUNNING __ lrMOLDgtzm 2023-04-06T18:00:05.004Z __ JOB SKIPPED COUNT __ 1 __ JOB SKIPPED COUNT __ 2 __ JOB SKIPPED COUNT __ 3 __ TIMEOUT __ __ JOB COMPLETED __ lrMOLDgtzm 2023-04-06T18:00:22.015Z __ JOB RUNNING __ qPK1lQ8QuV 2023-04-06T18:00:25.003Z __ JOB SKIPPED COUNT __ 1 __ JOB SKIPPED COUNT __ 2 __ JOB SKIPPED COUNT __ 3 __ TIMEOUT __ __ JOB COMPLETED __ qPK1lQ8QuV 2023-04-06T18:00:42.003Z
isJobRunning — это флаг, указывающий, выполняется ли задание в данный момент.
jobSkippedCount – это счетчик, который отслеживает, сколько раз задание было пропущено из-за наложения. Это также помогает **TIMEOUT**. Мы можем установить максимально допустимое количество пропусков, например 5. Затем, если задание будет пропущено в 5-й раз, оно будет сброшено и начнется новое задание.
delay() — функция, которая возвращает обещание, которое разрешается через заданное количество миллисекунд. Обычно это ваша логика.
Это больше похоже на временный хак, чтобы избежать наложений, такие сценарии тайм-аута настолько опасны.
Более масштабируемый подход заключается в использовании системы очередей для управления заданиями. Это позволяет ставить в очередь несколько экземпляров задания и выполнять их последовательно, без перекрытий или дубликатов. Популярные системы очередей для Node.js включают Bull, Bee-Queue и Agenda.
Спасибо за прочтение
🕊 Peace