La situación
Tengo una tarea de apio que estoy ejecutando en una zona horaria diferente para cada cliente.
Básicamente, para cada cliente en mi base de datos, obtengo la zona horaria y luego configuro la tarea de apio de esta manera.
'schedule': crontab(minute=30, hour=14, nowfun=self.now_function)Básicamente, lo que quiero es que la tarea se ejecute a las 14:30, en la zona horaria del cliente. De ahí la función now_.
Mi now_function solo obtiene la hora actual con la zona horaria del cliente.
def now_function(self): """ return the now function for the task this is used to compute the time a task should be scheduled for a given customer """ return datetime.now(timezone(self.customer.c_timezone))Qué está pasando
Obtengo inconsistencias en el tiempo de ejecución de la tarea, a veces se ejecutan a la hora prevista, así que digamos a las 14:30 en la zona horaria del cliente, si la zona horaria es America/Chicago , se ejecuta a las 20:30 y ese es mi comportamiento esperado. .
Algunos otros días, funciona a las 14:30, que es la hora en UTC.
Estoy rastreando para ver si hay un patrón en el día en que la tarea se ejecuta en el momento correcto y el día en que las tarjetas se ejecutan en el momento incorrecto.
información adicional
He intentado esto en apio 4.4.2 y 5.xx pero todavía tiene el mismo comportamiento.
Aquí está mi configuración de apio.
CELERY_REDIS_SCHEDULER_URL = redis_instance_url logger.debug("****** CELERY_REDIS_SCHEDULER_URL: ", CELERY_REDIS_SCHEDULER_URL) logger.debug("****** environment: ", environment) redbeat_redis_url = CELERY_REDIS_SCHEDULER_URL broker_url = CELERY_REDIS_SCHEDULER_URL result_backend = CELERY_REDIS_SCHEDULER_URL task_serializer = 'pickle' result_serializer = 'pickle' accept_content = ['pickle'] enable_utc = False task_track_started = True task_send_sent_event = True Puede notar que enable_utc está establecido en False .
RedBeatScheduler de este paquete para programar mis tareas.Si alguien ha tenido este problema o me puede ayudar a reproducirlo, le estaré muy agradecido.
Otras ediciones:
weekly_schedule : crontab(minute=30, hour=14, nowfun=self.now_function, day_of_week=1) monthly_schedule : crontab(minute=30, hour=14, nowfun=self.now_function, day_of_month=1)Proyecto de muestra
Aquí hay un proyecto de muestra en GitHub si desea ejecutar y reproducir el problema.
El codificador y decodificador de RedBeat no son compatibles nowfun .
Código fuente: https://github.com/sibson/redbeat/blob/e6d72e2/redbeat/decoder.py#L94-L102
El comportamiento que ve se describió anteriormente: sibson/redbeat#192 (comentario 756397651)
Puede subclasificar y reemplazar RedBeatJSONDecoder y RedBeatJSONEncoder .
Dado que nowfun tiene que ser JSON serializable, solo podemos admitir algunos casos especiales,
por ejemplo nowfun=partial(datetime.now, tz=pytz.timezone(self.customer.c_timezone))
from datetime import datetime from functools import partial from celery.schedules import crontab import pytz from pytz.tzinfo import DstTzInfo from redbeat.decoder import RedBeatJSONDecoder, RedBeatJSONEncoder class CustomJSONDecoder(RedBeatJSONDecoder): def dict_to_object(self, d): if '__type__' not in d: return d objtype = d.pop('__type__') if objtype == 'crontab': if d.get('nowfun', {}).get('keywords', {}).get('zone'): d['nowfun'] = partial(datetime.now, tz=pytz.timezone(d.pop('nowfun')['keywords']['zone'])) return crontab(**d) d['__type__'] = objtype return super().dict_to_object(d) class CustomJSONEncoder(RedBeatJSONEncoder): def default(self, obj): if isinstance(obj, crontab): d = super().default(obj) if 'nowfun' not in d and isinstance(obj.nowfun, partial) and obj.nowfun.func == datetime.now: zone = None if obj.nowfun.args and isinstance(obj.nowfun.args[0], DstTzInfo): zone = obj.nowfun.args[0].zone elif isinstance(obj.nowfun.keywords.get('tz'), DstTzInfo): zone = obj.nowfun.keywords['tz'].zone if zone: d['nowfun'] = {'keywords': {'zone': zone}} return d return super().default(obj) Reemplace las clases en redbeat.schedulers :
from redbeat import schedulers schedulers.RedBeatJSONDecoder = CustomJSONDecoder schedulers.RedBeatJSONEncoder = CustomJSONEncoder