Сельдерей бьется: Изменение индивидуальных заданий часовых поясов вызывает ошибку валидации "Неверный часовой пояс"

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()
Вернуться на верх