Я переношу свой сайт на python/django, и одно из основных упражнений включает в себя набор данных, где пользователи могут запланировать событие в свое местное время, и оно будет происходить каждый день.
В настоящее время у меня есть задание cron (на другом сервере), которое запускает метод каждые, например, 5 минут и смотрит, нужно ли что-то запланировать на следующие (скажем) 10 минут.
Я сохраняю значение времени и локальный часовой пояс пользователя для каждого задания.
Как лучше всего это сделать?
Сейчас я работаю над функцией, которая:
- Преобразует время сервера в локальное время пользователя.
- Создает локальный объект даты и времени, локализованный «сегодня» и время, указанное пользователем
- Проверяет, не прошло ли 10 минут после срабатывания будильника пользователя.
- Если это время между 23:50 и 23:59:59, а пользователь установил время с 00:00 до 00:10, локализованное «сегодня» создается с датой «завтра». (например, если до полуночи осталось 2 минуты, а пользователь хочет провести событие в 12:01, я вычисляю событие с завтрашней датой)
- Я устанавливаю поле last_scheduled, когда оно запланировано, и поле last_fired, чтобы гарантировать, что я не отправлю несколько.
Если это произойдет в течение 10 минут, я планирую задачу (поток, что угодно), которая вскоре запустится.
Не совсем уверен в лучшей практике здесь. Должен ли я:
Продолжать проверять, есть ли у меня какие-либо задачи в будущем, и планировать краткосрочные задачи?
Предварительно генерировать все мои времена заранее (может быть, за месяц?)
Сделать что-то совершенно другое?
Я также думал, что всегда могу просто запланировать "следующее" событие, но Меня беспокоит то, что, скажем, мой сервер отключится, и я пропущу "следующее" событие на следующий день. никогда не будет запланировано.
Уточнить:
- Я сохраняю время и часовой пояс для каждого задания (например, полдень в США/Восток).
- Я делаю поправку на летнее время, поэтому при расчете времени UTC я беру сегодняшнюю дату в формате UTC, конвертирую в местное время, а затем использую его для расчета дельты. Я использую pytz и normalize(), чтобы убедиться, что у меня нет проблем с переходом на летнее время.
- У меня есть время последнего запланированного и последнего запуска, чтобы убедиться, что я не выполняю дважды.
Глядя на решение ниже, я думаю, что мое единственное другое наблюдение заключается в том, что если по какой-либо причине я пропущу запланированное время, мое «следующее» никогда не произойдет, потому что оно было тогда в прошлом. Я полагаю, я мог бы сделать вторую функцию, чтобы исправить любые пропущенные сигналы тревоги.
Редактировать: После изучения ответов ниже я пришел к следующему менее худшему сценарию:
У меня есть следующие поля
- Время выполнения последнего события
- В последний раз событие было запланировано
- Время выполнения следующего события
- Время суток и часовой пояс
Я рассчитываю и устанавливаю next_run_time всякий раз, когда я: обновляю событие или запускаю событие. Это делает следующее:
- Если у него есть последнее время выполнения, вычисляется next_run_time, по крайней мере, через 2 часа в будущем (избегайте проблем с летним временем, добавляя некоторые отступы).
- Если мероприятие никогда не проводилось, запланируйте как минимум 15 минут в будущем (избегайте нескольких одновременных расписаний).
Моя запланированная работа делает следующее:
- Проверяет все события, которые имеют next_run_time в ближайшие 15 минут и не запланированы в данный момент. Любые совпадения запланированы.
Планирование работы:
- Планирует задание и устанавливает задание как запланированное «сейчас»
Когда задача выполняется (успех):
- last_run_time обновляется до «сейчас»
- next_run_time пересчитывается
Если задача не удалась: - Задание переносится на 30 секунд вперед. Если сбой превышает пороговое значение (в моем случае просрочено на 3 минуты), задача прерывается, а next_run_time пересчитывается на следующий день. Это регистрируется и, надеюсь, не слишком часто
Кажется, это в основном работает, потому что мои события всегда (ежедневно), поэтому я могу позволить себе внести некоторые дополнения во время и избежать некоторых волосатых проблем.