meta 配置文件里的时间戳,我按秒对齐才发现错位

meta 的配置文件里有个字段是上次同步时间,格式是 ISO 字符串带毫秒。我们线上有 6 个节点,配置由一份 meta 文件分发下去,大约 4.2 万行,单文件 380KB 左右。某天开始,两个节点报「快照过期」,另外四个正常。手动 diff 六个节点的 meta 文件,内容一模一样。

我一开始怀疑是分发没落地,把文件 md5 全对了,还是全一致。后来把时间字段单独 grep 出来,才看到问题:

grep -n 'last_sync_at' /etc/meta/node*/meta.conf
# node1: last_sync_at = "2024-03-11T08:22:41.000Z"
# node2: last_sync_at = "2024-03-11T08:22:41Z"
# node3: last_sync_at = "2024-03-11T08:22:41.000Z"

node2 少了毫秒。分发工具会在写入前把值重新序列化,有的节点进程读到字符串后按自己语言的时间库格式化,Go 的 time.Format 用 2006-01-02T15:04:05Z07:00 就没毫秒,Python 的 isoformat 默认又带微秒。字节层面 md5 不同,但我 diff 时用的工具忽略空白和符号,恰好把差异盖掉了。

做法很简单:把比较口径从「文件内容」改成「解析后的时间值」,并且给分发脚本加一步规范化。

python3 - <<'EOF'
import re, sys, datetime
pat = re.compile(r'last_sync_at\s*=\s*"([^"]+)"')
def norm(s):
    s = s.replace('Z', '+00:00')
    return datetime.datetime.fromisoformat(s).astimezone(datetime.timezone.utc)
for f in sys.argv[1:]:
    v = pat.search(open(f).read()).group(1)
    print(f, norm(v).isoformat(timespec='seconds'))
EOF

关键是 timespec='seconds',把毫秒截掉再比。分发脚本里也用同一段逻辑写出统一格式,避免各节点进程各自序列化。

为什么截到秒就够:这个字段只用来判断「上次同步是否在容忍窗口内」,窗口是 5 分钟,毫秒对判定没有意义,反而制造了不存在的不一致。代价是丢掉毫秒精度,如果以后要做秒级以下的排序,这个口径就不够用,得换回带毫秒的规范格式,而且要在所有节点统一。

哪里会失效:时区不一致时,光截秒还是会错。我有一次把 +08:00 和 Z 混着比,差了整 8 小时。所以规范化里必须先 astimezone 到 UTC 再截。另外,正则只匹配带引号的字符串,配置里如果有不带引号的裸时间,这条就抓不到,得先确认格式。

你们那边 meta 时间字段是统一由分发端写,还是各节点自己格式化?我这边两种都出现过,想看看别人怎么收口的。

0
赞同度 100% · 0 条评论 · 3 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。