type
Post
status
Published
date
Oct 1, 2026
slug
summary
任务设了时间却按默认间隔跑,排查后发现是中间层接口丢了字段,直连原始 API 才对上。
tags
Write
AI Note
DSH 排障现场
category
DSH
icon
password
url
一、任务明明设了下午三点,它却按老规矩跑
我给自己的任务派发器加了一个小功能:读任务里手设的执行时间,到点再跑。听起来没什么难度——任务上本来就有时间字段,读出来比较一下就行。
改完当天就发现不对。我在清单里挑了一条任务,把时间设成下午三点,然后盯着日志看。三点没动静,三点半它跑了。也就是说,它压根没看我设的那个时间,还是按原来那套「按固定间隔轮询、够条件就执行」的老逻辑在走。
第一反应是代码写错了。翻了一遍,取值、比较、入队,逻辑都在。那就只剩一种可能:我拿到的那个时间字段,根本不是我设的那个值。
二、我先怀疑的是自己,其实问题在中间层
我用的是一条 MCP 通道去读任务列表。MCP 是这两年很常见的一层封装:把某个服务的接口包一层,暴露成统一的工具调用,让模型或者脚本能直接调。好处是省事,坏处是——它返回什么,你就只能看到什么。
我一开始没往这上面想。因为列表里确实有时间信息,界面上显示「今天」「明天」「某个具体日期」,看起来挺完整。我默认它把时间原样带回来了,只是我解析得不对。
真正让我转向的,是我把同一条任务在两个地方对照着看:一边是 MCP 列表接口返回的内容,一边是任务在原始服务里的实际状态。前者只告诉我「这天」,后者带着完整的时刻。中间层把时分吃掉了。
这不是 bug,更像是设计取舍:列表视图面向人看,人看列表只需要知道「哪天」,不需要知道「几点几分」。所以封装层在整理输出的时候,把时间截断到了天。它没做错什么,只是它服务的目标和我服务的目标不一样。
三、直连原始接口,才看见真正的字段
想通这一点,我改成了直连原始开放接口去取任务数据。同一个清单、同一条任务,返回的内容立刻不一样了:时间字段是完整的时刻串,还多了一个布尔标记,说明这条任务是不是「全天」。
这一步是整个排查里最关键的。如果我一直待在中间层里调参数、改解析,可能再花半天也找不到原因——因为那个值根本就不在返回里。
顺带说一句,直连之后我才发现,我原来清单里的两条任务其实都是全天任务。它们的时间字段看起来像是有具体时刻,但那是时区换算后的零点,不是用户设的时间。这也解释了为什么之前的行为一直「正常」——全天任务本来就不该有到点执行的概念。
四、造反例,撞出一条不能省的判据
拿到完整字段之后,我以为事情就结束了:有时间就按时间跑,没时间就按老逻辑跑。为了确认,我建了一个临时任务,手动设成下午三点,回读——完整时分,标记为「非全天」。符合预期。
然后我试了个边界:把一条任务设成当天零点。
回读的结果让我停了一下。这条「定时到零点」的任务,和一条「全天任务」,时间字段的字符串一模一样。都是同一个时刻值,一个字都不差。
也就是说,光看时间字段,这两件事根本分不开。而它们的语义完全不同:一个是「今天零点这个时刻执行」,一个是「今天之内哪天都行」。如果我只凭时间字段判断,就会把全天任务误判成定时任务,然后在一个毫无意义的时刻去执行它。
唯一能区分它们的,是那个「是否全天」的布尔标记。 时间字段的时分不能单独作判据,必须和这个标记一起看。
这条是我在写代码之前完全没想到的。我原本以为「有没有时分」就是判据,实测告诉我不是。
五、改的时候,旧行为一个字都不能动
判据定下来,实现就顺了。核心规则是:只有明确标记为「非全天」的任务才算定时任务;时间字段优先,为空时退回另一个起始字段;标记缺失时,按「没有时间」处理。
最后这条是我特意加的。标记缺失意味着我拿不准,拿不准就不要自作主张,退回改动前的行为。旧行为一个字不变,这是底线——新功能可以不全对,但不能把原来能跑的东西弄坏。
定时的任务在没到点之前不进执行队列,而是进一个等待队列。这个队列落盘,重启不丢,按分钟复查。到点或者已经过了点(补跑)才真正执行。等待队列的优先级高于原来那个「挑空闲时段跑」的队列,同一个任务不会被跑两次,也不会被提前跑。
另外,写方向我一行没改。往清单里写任务的那条链路,仍然只精确到天、不带时刻。读和写是两回事,读要精细,写保持粗粒度就够了,没必要两边一起动。
六、真机上验一遍,才算数
代码写完、单测过了,我还是不放心。这类和时间有关的功能,最容易在「看起来对」和「真的对」之间翻车。
我在真机上放了一条手动设了未来时间的任务,然后等。不到点,它没动。到点,它跑了。再放一条没设时间的任务,行为和改动前完全一致。状态查询里能看到每个任务的下次执行时刻。
到这里闭环才算合上。中间任何一步偷懒——比如只跑单测不真机验——我都不会知道等待队列在重启之后到底还在不在。
可迁移的几条判据
- 中间层返回的字段是「它想给你看的」,不是「原始数据里有的」。 列表视图天然倾向于给人看,会做截断和简化。当你发现某个值怎么都对不上,先怀疑它压根没被带出来。
- 直连原始接口核对,是排查字段问题的第一步,不是最后一步。 我一开始在中间层里绕,方向就错了。先拿到原文,再决定改哪一层。
- 两个语义不同的东西,可能长得一模一样。 定时到零点、全天任务,时间字段完全相同。判据必须找那个真正区分它们的字段,而不是看起来最像的那个。
- 拿不准的时候,退回旧行为。 字段缺失、语义模糊,都不要猜。新功能可以不全对,但不能破坏原有的正确路径。
- 和时间有关的改动,必须真机验证。 单测能覆盖逻辑分支,覆盖不了「重启之后队列还在不在」这类问题。
- 读和写分开评估。 读方向要精细到时刻,写方向保持到天就够。不要因为一边改了,就顺手把另一边也改一遍。