ホーム技術ブログ › 07
2026-09-07 #07

書き込み口を2つにしたら、人が入れた修正が黙って消えた

Upstash のオーバーレイと、バッチによる直接更新が衝突。drain-first と base 照合で lost update を止めるまで。

設計排他制御Redisレビュー

症状: 直したはずの箇所が、元に戻っている

管理画面から自分で直した箇所が、しばらくすると元の文に戻っていました。 エラーは出ていません。ログにも何も残っていません。 「直し忘れたのかな」と思うくらいの、静かな消え方でした。

原因: 書き込み口が2つあった

この構成には、同じ segment に書き込む経路が2本あります。

校正で確定できなかった箇所
   │  screen_verdict=unsure で CSV に書く
   ▼
data/medium_low_findings*.csv
   │  build_review_queue.py(デプロイのたびに作り直す)
   ▼
review_queue テーブル(現在 8,493件)
   │
   ▼
/admin/review で該当箇所に色が付く
   │  人が直す
   ▼
Upstash Redis の corrections(オーバーレイ)── 即座に本番の表示へ反映   ← 経路①
   │  merge_overlay.py
   ▼
data/yuru.db(恒久化)                                              ← 経路②(バッチが直接触る)

オーバーレイを噛ませているのは、DBの再配布を待たずに直せるようにするためです。 400MBのDBを毎回配るわけにいかないので、差分だけ Redis に置いて上書き表示しています。

ここで、まだマージしていないオーバーレイがある状態で、 こちらがバッチでDBを直接触ると、マージのときに後から来たほうが黙って勝ちます。 負けるのはたいてい手で直したほうでした。いちばん手間をかけたものから消えます。

対処1: 先に排出してから触る(drain-first)

# ❌ これをやると、未マージの修正の上に書いてしまう
$ python3 scripts/bulk_fix.py

# ✅ 必ず先に排出する
$ python3 scripts/merge_overlay.py     # 人の修正を DB に取り込む
$ python3 scripts/bulk_fix.py          # そのうえで機械の編集

対処2: base を持たせて、競合を残す

修正提案に base(提案者が見ていた文)を持たせて、 API・承認・マージの各段で照合します。不一致なら上書きせず、競合として保全します。

$ python3 scripts/feedback_queue.py conflicts
[0] 5HUPJcw-YXA:781
    base : デカルト味は世界に        ← 提案者が見ていた文
    提案 : デカルトみは世界に
    現在 : 「デカルトみ」は世界に    ← その後こちらが変えた

$ python3 scripts/feedback_queue.py resolve 0 keep

ここは、機械に判定させて片方を消さないようにしています。両方の文を出して、自分で選ぶ形です。

もう1件: push と DB入れ替えを連結して20分落とした

# ❌ やってしまったこと
$ git push && bash scripts/upload_db.sh

# push が Vercel の再ビルドを起こす
#   → その再ビルドが「古いDBを消して新しいDBを上げている最中」に当たる
#   → 全ページ 500(約20分)

いまは DB の入れ替えを原子的にしています。

Step 0  : FTS 索引を作り直す
Step 0.5: 要確認台帳から review_queue を再構築
Step 1  : 出荷前検査5項目(1つでも中止条件に触れたら止める)
Step 2  : 一時名でアップロード → サイズ検証
Step 2.5: 数秒でリネームして世代交代(前世代は yuru-deploy-prev.db として保持)
Step 3  : 空コミットで Vercel を再ビルド
Step 4  : 出荷時点のスナップショットを控える(次回の検査の比較元)

そのうえで、コード変更の push とは連結しないようにしました。

再発防止

  • UIより先に、書き込み口がいくつあるかを数える
  • 2つ以上あるなら調停の規則を先に決める
  • 競合は自分で判定せず、両方を見せて選ばせる
  • デプロイは原子的に。一時名 → 検証 → リネーム、前世代を残す

← 「要確認」を付けて確認する仕組みにした。ただし漏れが… 追記専用の編集台帳とデプロイ前検査で、事故を出荷前に… → 目次へ

ゆる言語学ラジオ・ゆるコンピュータ科学ラジオ 公認/株式会社pedantic の了承と、運営者・安東暁史との合意に基づく協力体制で運営しています。ドメイン yururadio.jp は株式会社pedanticが保有。開発・保守: 安東暁史·このサイトについて·お問い合わせ