DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关

Celery beat 太阳调度报 ephem.Date 不能与 timedelta 相加

solar 条目遇到当天不存在的太阳事件,可能在日期回退计算中拖垮 beat。隔离故障条目,保留调度历史,避免把类型补丁误当成正确的无事件处理。

beat 原本在正常发布定时任务,到了某个日期却退出,日志末尾出现:

TypeError: unsupported operand type(s) for +: 'ephem.Date' and 'datetime.timedelta'

如果 traceback 指向 celery.schedules.solar.remaining_estimate() 的 next_antitransit(),先检查配置的太阳事件当天在目标经纬度是否存在。这条异常路径在 Celery 5.6.3 中仍保留;ephem 4.2.1 与后续开发版 Celery 的组合也有相同故障记录。不能把它泛化为全部定时任务失效,或认为升级到某个尚未发布的版本就已解决。

事件不存在,触发了另一条日期计算路径

太阳升落、民用曙光、航海曙光和天文曙光对太阳高度的要求不同。在较高纬度,某些季节可能没有对应事件。即使当地每天仍有日出,也不能保证当天有天文曙光;先核对具体的 solar() 事件名,而不只看城市是否处于极昼。

计算库在这类条件下可能抛出 CircumpolarError。Celery 捕获它后,尝试在下次太阳下中天之后再检查,却把 ephem.Date 直接加上 datetime.timedelta(minutes=1),于是又抛出上面的 TypeError。

因此,表面的类型错误背后还有一项调度含义:当天可能根本没有那个太阳事件。只把加法换成能通过的写法,并不能自动保证“没有事件时不执行任务”。

先保存 beat 的完整 traceback、Celery 与 ephem 版本、事件名称、经纬度、故障时刻以及使用的调度器。经纬度正负号错误、事件选错,与确实不存在该事件,应分别核对。

先恢复健康调度,单独处理这条太阳规则

一个条目的时间计算异常可能打断整个 beat 循环,连普通 crontab 任务也不再发布。可先按现有调度器的管理方式停用或移出已确认的故障条目,保留其配置和运行记录,再恢复单一 beat 实例。

如果配置直接来自 beat_schedule,调整的是对应配置项;若使用数据库调度器,则在它的正常管理入口停用该条目。不要只改 Python 配置却留下数据库里的同一条规则,也不要为恢复服务同时启动第二个 beat,造成重复发布。

恢复后确认健康条目的下一次发布时间和 worker 实际执行情况。beat 启动不再报错,只是第一步;此前停顿期间漏发了哪些任务,还需按业务记录核对。补发有副作用的任务时沿用原来的去重或幂等规则。

不要把“次日再检查”当成“次日一定执行”

业务需要的若是真正的天文事件,可以在有明确无事件处理规则的实现中预先计算日期:不存在事件时不生成执行时刻,存在时再安排任务,并保存计算所用的位置、时区和规则。大量远期任务不宜一次全塞进 broker;仍应沿用可持久化、可核对的调度方案。

业务实际需要每天固定时间执行时,才考虑改成普通日历调度,并让业务方确认这一变化。固定钟点不等价于日出或曙光,不能只为让任务继续跑就悄悄替换。

后续采用补丁或正式版本时,检查它是否同时处理类型运算与无事件语义,而不只看该 TypeError 消失。隔离验收至少覆盖有事件的一天、无事件的一天、跨日期后的下一次计算,以及其他正常周期任务。目标是该执行时执行、不存在事件时不伪造一次触发,beat 也能继续调度其他条目。

保留原配置与调度存储备份,恢复时只恢复需要的条目和状态。不要删除整个 beat 状态文件来消除异常;那会丢失其他任务的调度历史,却没有改变这段日期计算。