Сельдерей бьется: Изменение индивидуальных заданий часовых поясов вызывает ошибку валидации "Неверный часовой пояс"
celery --version 5.1.2 (солнечная гармоника)
django --version 3.2.8
У меня есть расписание celery, в котором есть четыре задачи, выполняемые в разных часовых поясах. Я использую nowfun для установки временных зон и установил CELERY_ENABLE_UTC = False в settings.py. Я следовал верхнему ответу в этом посте SO: Celery beat - разный часовой пояс для каждой задачи
Обратите внимание, что я сделал это изменение сегодня утром - я запускал предыдущую версию кода без этих настроек.
В настоящее время я сохраняю результаты celery в CELERY_RESULT_BACKEND = 'django-db'.
После внедрения изменения, позволяющего запускать разные задачи в соответствии с разными часовыми поясами, я получаю ошибку при запуске celery -A backend beat -l info.
Это очень длинно, но вот голова и хвост: Голова:
[2021-10-29 07:29:36,059: INFO/MainProcess] beat: Starting... [2021-10-29 07:29:36,067: ERROR/MainProcess] Cannot add entry 'celery.backend_cleanup' в расписание базы данных: ValidationError(["Invalid timezone '<LocalTimezone: UTC+00>'"]). Contents: {'task': 'celery.backend_cleanup', 'schedule': <crontab: 0 4
- (m/h/d/dM/MY)>, 'options': {'expire_seconds': 43200}}
Хвост:
django.core.exceptions.ValidationError: ["Неверный часовой пояс '<LocalTimezone: UTC+00>'"]
.
Celery beat зависает на этом последнем сообщении об ошибке, и мне приходится убивать его с помощью ctrl + c.
Я зашел на сайт celery и прочитал их инструкции о ручном сбросе базы данных при изменении настроек, связанных с часовым поясом - на сайте написано:
$ python manage.py shell
from django_celery_beat.models import
PeriodicTask PeriodicTask.objects.update(last_run_at=None)
Затем я нашел некоторую документацию, которая гласила:
Предупреждение: Если вы измените параметр Django TIME_ZONE, ваше периодическое расписание задач будет по-прежнему основываться на старом часовом поясе. Чтобы исправить это, вам необходимо сбросить "время последнего запуска" для каждой периодической задачи:
from django_celery_beat.models import PeriodicTask, PeriodicTasks
.PeriodicTask.objects.all().update(last_run_at=None)
PeriodicTasks.changed()
Обратите внимание, что это сбросит состояние, как если бы периодические задачи никогда не запускались раньше.
Так что я думаю, что причиной проблемы является именно то, что написано выше - я сменил часовой пояс, и расписание все еще работает в старом часовом поясе UTC, поэтому мне нужно обновить его, хотя мои расписания работали раньше, и поэтому, когда я набираю:
>>> PeriodicTask.objects.all().update(last_run_at=None)
Я получаю ответ:
13
и затем, когда я ввожу:
>>> PeriodicTasks.changed()
Я получаю ошибку типа:
TypeError: changed() missing 1 required positional argument: 'instance'
Итак, мой вопрос таков:
Что нужно сделать, чтобы обновить PeriodTask и PeriodicTasks? Какие аргументы я должен передать в PeriodicTasks.changed() и является ли 13 ожидаемым ответом для первой команды?
Вот мой celery.py:
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings
from celery.schedules import crontab
import pytz
from datetime import datetime
os.environ.setdefault(
'DJANGO_SETTINGS_MODULE',
'backend.settings'
)
app = Celery(
'backend'
)
app.config_from_object(
settings,
namespace='CELERY'
)
def uk_time():
return datetime.now(pytz.timezone('Europe/London'))
def us_time():
return datetime.now(pytz.timezone('EST'))
def jp_time():
return datetime.now(pytz.timezone('Japan'))
# Celery Beat Settings
app.conf.beat_schedule={
'generate_signals_london': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=8,
nowfun=uk_time,
day_of_week='1,2,3,4,5'
),
'args': ('UK',),
},
'generate_signals_ny': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=7,
nowfun=us_time,
day_of_week='1,2,3,4,5'
),
'args': ('NY',),
},
'generate_signals_nyse': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=9,
nowfun=us_time,
day_of_week='1,2,3,4,5'
),
'args': ('NYSE',),
},
'generate_signals_asia': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=8,
nowfun=jp_time,
day_of_week='1,2,3,4,5'
),
'args': ('JP',),
},
}
app.autodiscover_tasks()
Иногда, пытаясь решить проблему, мы на самом деле решаем проблему, созданную неправильным способом решения исходной проблемы.
Если ответ был: "Вот как вы обновляете PeriodicTask, PeriodicTasks ранее запущенных задач при смене часового пояса", то каков был первоначальный вопрос?
Изначально вопрос заключался в изменении часового пояса для различных задач таким образом, чтобы эти задачи учитывали DST для различных часовых поясов. Следование решению на Celery beat - different time zone per task было не самым эффективным подходом к решению этой проблемы.
Чтобы избежать необходимости обновления PeriodicTask и PeriodicTasks, не меняйте CELERY_ENABLE_UTC = False и выполняйте все в UTC, как указано в ответе на это сообщение: Проблемы запланированных задач Celery с часовым поясом
Это решает исходную проблему.
Вот мой обновленный celery.py и рабочее решение:
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings
from celery.schedules import crontab
import pytz
from datetime import datetime
os.environ.setdefault(
'DJANGO_SETTINGS_MODULE',
'backend.settings'
)
app = Celery(
'backend'
)
app.config_from_object(
settings,
namespace='CELERY'
)
dt = datetime.now()
tz = pytz.timezone('Europe/London')
dst = tz.localize(dt)
if bool(dst.dst()):
uk_market_open = 9
else:
uk_market_open = 8
tz = pytz.timezone('EST')
dst = tz.localize(dt)
if bool(dst.dst()):
ny_market_open = 12
nyse_market_open = 14
else:
ny_market_open = 13
nyse_market_open = 15
jp_market_open = 23
# Celery Beat Settings
app.conf.beat_schedule={
'generate_signals_london': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=uk_market_open,
day_of_week='1,2,3,4,5'
),
'args': ('UK',),
},
'generate_signals_ny': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=ny_market_open,
day_of_week='1,2,3,4,5'
),
'args': ('NY',),
},
'generate_signals_nyse': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=nyse_market_open,
day_of_week='1,2,3,4,5'
),
'args': ('NYSE',),
},
'generate_signals_asia': {
'task': 'signals.tasks.generate_signals',
'schedule': crontab(
minute=0,
hour=jp_market_open,
day_of_week='1,2,3,4,5'
),
'args': ('JP',),
},
}
app.autodiscover_tasks()