TT Lab
博客

한국어 English 中文

开始
博客

博客

数字叫停部署的那天:268,729 中的 729 与正则单词边界

状态:已解决(2026-10-15 09:43,修改注释)。 数据取自批量门禁的运行历史、门禁日志的失败信息以及提交历史。 正则表达式对比表是使用 Python 直接运行得出的值。修复正则表达式和元凶排查工作 尚未进行.

目录

一句话总结

影响 10-15 01:05 ~ 09:43,批量门禁连续 26 次失败。打包在一起的任务从 3 个累积到 19 个,导致没有任何内容被部署
原因 注释中的“268,729 字节”触发了商标检查正则表达式 `\b(…
为什么耗时变长 失败测试所更改的文件与名称不匹配,导致元凶排查返回“未知”,循环机制随即重新运行该批次
已修复 将注释改为“约 262KiB”。在下一轮中 19 个任务一同通过
遗留问题 正则边界、元凶排查、重复失败时停止并报警的机制

1. 发生了什么

相机记分牌是 TT Lab 的代表性页面。由于首屏在加载时连同暂不需要渲染的录制、编辑和自动计分代码一起请求了,因此我将其拆分为独立区块,并改为延迟加载。我在该修改的头条注释中写下了这样一句话。

//   정적 import 로 268,729 바이트를 받았다(Fast 3G 첫 그리기 4.7초). …

准确记录数字是个好习惯。问题在于该仓库中存在一项测试,用于 防止乒乓球乐园的静态文件中混入特定的用品商标和内部名称 商标列表中包含一个以乒乓球胶皮闻名的数字名称。没错,就是 729 。

BRANDS = re.compile(r"\b(butterfly|tenergy|…|sanwei|729)\b", re.I)

门禁日志中的失败提示非常诚实:

AssertionError: <re.Match object; span=(1518, 1521), match='729'> is not None : …/backend/static/tt/play/cam.js

2. 为什么“268,729”会匹配“729”:单词边界

\b 匹配的不是字符,而是 位置。即一侧是单词字符(\w —— 字母、数字、下划线),另一侧是非单词字符的位置。

\b 는 「단어 문자와 아닌 문자 사이」 2 6 8 , 7 2 9 공백 바 이 트 경계 경계 쉼표·공백은 \w 가 아니다 → \b729\b 가 맞는다 묶음 게이트 27번(10-15 01:05 → 09:43) 01:05 09:43 막대 = 묶음에 든 작업 수(3 → 19) 빨강 26번: 같은 시험, 같은 파일, 범인을 못 가림 초록 1번: 주석을 「약 262KiB」 로 고친 뒤 19개가 함께 통과
图:268,729 的单词边界与批量门禁 27 次运行的时间线

不仅是千位分隔符,小数点(v1.729)或连字符(729-2)也是如此。如果用 \b 包围数字商标,任何包含数字的文章随时都可能中招。

3. 为什么花了 8 个半小时:未能识别元凶的批量门禁

门禁(整体测试)单次运行大约需要 20 分钟。因此,系统不会为每个任务单独运行,而是将多个任务打包在一起进行测试,通过后一同合并。如果失败,则通过“元凶排查”剔除导致失败的任务后重新运行。

元凶排查使用了以下线索:

  1. 修改了与失败测试名称(test_tt_play)同名文件(tests/test_tt_play.py, backend/app/tt_play.py)的任务
  2. 修改了在失败提示中用引号括起来的 .py 文件的任务

这一次,这两条线索都失效了。没有人修改过失败的测试文件,且失败提示中出现的文件不是 .py ,而是 cam.js 。 尽管失败提示中 cam.js 的完整路径 就明摆在那里 ,但元凶排查机制并没有去查看它。结果就是:“门禁失败,未能识别元凶”。

随后,外部循环在几分钟后 重新运行了同一批次 。新提交的任务也一同被卷入了该批次。

时间 (10-15) 批次中的任务 结果
01:05 ~ 01:46 3 失败 3 次
02:06 ~ 02:25 8 失败 2 次
02:45 ~ 08:21 9 失败 18 次
08:42 18 失败
09:03 ~ 09:23 19 失败 2 次
09:43 19 通过 — 19 个任务一同通过

全部 26 次运行测试、文件以及 match='729' 都完全相同(核对过所有残存的 26 条门禁日志)。将注释修改为“通过静态 import 获取了约 262KiB”的任务文件在 09:42 被修正,下一轮测试于 09:43 启动并顺利通过(268,729 ÷ 1,024 ≈ 262.4)。

4. 需要修复什么

正则表达式:数字商标应排除紧邻数字的位置

我用 Python 对比了两个正则表达式。

old = re.compile(r"\b(729)\b")
new = re.compile(r"(?<![\d.,])729(?![\d.,])")   # 앞뒤가 숫자·점·쉼표면 상표가 아니다
文本 \b729\b 排除相邻数字
268,729 바이트 命中 -
약 262KiB - -
Friendship 729 러버 命中 命中
729-2 러버 命中 命中
v1.729 命中 -
0x729 - 命中

新的表达式也并非完美(会捕获0x729 )。既然无法仅凭一个正则表达式完美区分“作为商标的 729”与“数字内部的 729”,那就宁可误报,但更为重要的是 在误报发生时能够立即被察觉 。此项修改尚未实施。

元凶排查:查看失败信息中的路径,未知时则进行拆分

循环:当相同的失败重复出现时,应停止并发出通知

如果同一个测试因为相同的原因失败了两次,那么第三次也会失败。届时应停止重试,并将失败信息展示给人工。26 次重试耗费了 8 个半小时,在此期间,18 个无关的任务被迫打包在一起苦苦等待。

这三项改进均尚未实施。这篇文章就是我的待办事项清单。

5. 这次事故带给我们的教训

蒂蒂寄语。 729 是一款好胶皮,但如果藏在数字里就会带来麻烦!正则表达式中的 \b 不把逗号当作边界。而且,如果因为相同的原因摔倒了两次,请在第三次奔跑前先检查鞋带。

参考

所有资料均于 2026-10-16 查阅。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论