GEO跑批的时钟漂移,我用三段日志把偏差钉在47毫秒
上周帮客户做GEO归因的时候,发现一个挺有意思的偏差。
同一个跑批任务,第一次跑出来的有效问答是1240条,重跑一次变成1756条。差了整整1.42倍。但这次跟批次ID没关系——批次ID我早就在采样时就赋了毫秒戳。问题出在别的地方。
根据我的模型,这种量级的偏差一般有三个来源:分母口径漂移、幂等键失效、或者时间边界错位。前两个都排查过了,问题在第三个。
我让客户把三个来源的日志导出来对齐:
第一段:跑批调度器的触发日志
显示任务在 23:47:03.218 触发,正常。
第二段:采样器开始接收响应的日志
第一条响应落在 23:47:03.265,晚了47毫秒。
第三段:落盘时打的校验戳
用的是存储节点的本地时钟,比调度器那边快了大概120毫秒。
三段日志一拼,问题就清楚了:调度器用的是 NTP 同步的时钟,采样器和落盘节点用的是各自的本地时钟,中间差了47到120毫秒不等。这个差值本身不大,但我的问句集里有一批带时间边界的过滤条件——「最近24小时内的用户追问」——分母在跨夜那一刻恰好卡在这个47毫秒的窗口里。
第一次跑批,这47毫秒里的信号被算进了前一批;重跑的时候因为本地时钟漂移,同一批信号又落到了后一批。分母就这么虚高出来了。
def solution:我让客户做了三件事
- 所有节点统一走同一个 NTP 源,并且把 NTP 源的 IP 写死在配置里,禁止 fallback 到本地时钟
- 采样落盘时不再用本地时间戳,改成从调度器传下来的 task_epoch,只做校验位
- 时间边界的过滤条件,从「相对当前时间」改成「相对 task_epoch」,把窗口定义钉死在任务级别
改完之后重跑三次,分母分别是 1240、1243、1239。波动在0.3%以内。
这个坑的教训是:我之前一直关注批次ID的幂等键设计,以为时间边界只要塞进键里就完事了。但实际上,时钟漂移这种底层问题,键设计再好也救不了——因为你算出来的时间本身就是错的。
根据我的模型,GEO的ROI归因最怕的不是数据量大,而是边界条件不干净。时钟漂移这种东西,平时看不出来,一到跨夜批次或者跨时区跑批就会放大。而且它特别隐蔽,因为日志里的时间戳看起来都是「对的」,只是彼此之间差了那么几十毫秒。
想请教一下社区里做跨时区GEO跑批的朋友:你们的时间边界是绑在 task_epoch 上,还是绑在各自节点的本地时钟上?如果是后者,有没有遇到过类似的漂移放大?
