上个月排查一个线上问题,最后卡在 meta 配置的版本号上。环境是 Ubuntu 22.04,Python 3.11,meta 配置文件一共 6 个,单个文件 800 到 2000 行不等,注释行占比大概三成。现象是同一份 meta 在测试环境行为正常,到线上就报字段类型不匹配。
我先用最直接的办法比对:
diff -u meta.test.yaml meta.prod.yaml | head -200
结果只输出了几行环境变量差异,看起来完全一致。但问题是真实存在的,说明差异藏在别处。
我改成先剥掉注释再比对,同时把键路径展开成一行一条:
python3 - <<'PY'
import yaml, sys
def walk(node, prefix=""):
if isinstance(node, dict):
for k, v in node.items():
yield from walk(v, f"{prefix}.{k}" if prefix else str(k))
elif isinstance(node, list):
for i, v in enumerate(node):
yield from walk(v, f"{prefix}[{i}]")
else:
yield prefix, node
for path in sys.argv[1:]:
with open(path) as f:
data = yaml.safe_load(f)
out = path + ".flat"
with open(out, "w") as f:
for k, v in walk(data):
f.write(f"{k}\t{v!r}\n")
PY
diff -u meta.test.yaml.flat meta.prod.yaml.flat
这次差异出来了:线上那份的 schema.version 是 2.3,测试是 2.3.1。而这个字段在源文件里写在注释后面,diff 的行级比对把它和上下文一起吞了,肉眼看过去只注意到注释变了。
为什么会出现这种情况:YAML 的注释不参与解析,但版本号这种字段恰好写在注释块下方,diff 按行比较时,只要整段行内容有变化就归为同一处差异,而人眼会跳过注释。展开成键路径后,每个叶子节点单独一行,版本号的差异就没法藏。代价是得到一份中间文件,字段顺序和源文件不同,定位到差异后还得回源文件里找对应位置;文件大了之后中间文件也会膨胀,2000 行的配置展开后大概 3000 多行。
什么情况下这套办法会失效:如果配置里存在锚点和引用,yaml.safe_load 展开后引用被复制成多份,同一个值会在不同路径重复出现,diff 结果会有冗余,需要自己按路径前缀过滤。另外浮点数在 repr 里可能写成 2.3 和 2.30 两种形态,实际值相同却报差异,我后来把值统一转成字符串再比。还有一个坑:空值和 null 在展开后都变成 None,如果业务上区分这两者,这个办法看不出来。
你们比对多环境 meta 时,是直接 diff 源文件,还是先展开成键路径再比?展开的话,锚点引用那块怎么处理?
0 comments
No comments yet — be the first.