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초). …

数字を正確に書くのは良い習慣です。問題はこのリポジトリに 卓球遊び場の静的ファイルに特定の用品商標と内部名が混ざらないように ブロックするテストが存在していたことでした。商標リストには卓球ラバーで有名な数字の名前が1つ含まれています。そう、 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時間半もかかったのか: 犯人を特定できなかったバッチゲート

ゲート(全体テスト)は1回あたり約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で2つの正規表現を比較してみました。

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 を捉えます)。正規表現1つで「商標として使った 729」と「数字の中の 729」を完全に切り分けることはできないため、ヒットする側に倒しつつ ヒットした時にすぐ分かるように することがより重要です。この変更はまだ行っていません。

犯人探し: 失敗メッセージの中のパスを見る、分からなければ分割する

ループ: 同じ失敗が繰り返されたら停止して通知する

同じテストが同じ理由で2回失敗した場合、3回目も失敗します。その時は再実行せずに停止し、人に失敗メッセージを見せなければなりません。26回の再試行が費やした時間は8時間半であり、その間に関係のない作業18個が一緒にまとめられて待機していました。

3つともまだ行っていません。この記事がそのToDoリストです。

5. このインシデントが教えてくれたこと

Titiの一言。 729 は良いラバーですが、数字の中に隠れていると困ります!正規表現の \b はカンマを境界としてみなしません。そして同じ理由で2回転倒したなら、3回目に走る前に靴紐から確認してください。

参考

すべての資料は 2026-10-16 に閲覧しました。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます