Skip to content

fix: D1のbind上限で自動要約が無限に課金されるのを止める - #77

Merged
gitdmnt merged 10 commits into
mainfrom
fix/d1-bind-limit
Sep 8, 2026
Merged

gitdmnt merged 10 commits into
mainfrom
fix/d1-bind-limit

Conversation

@gitdmnt

@gitdmnt gitdmnt commented Sep 8, 2026 •

Copy link
Copy Markdown
Owner

起きていたこと

Cloudflare D1 は1文あたりの bind パラメータを100個までしか受け付けない。
しかし、PUT /channel/summary で実行される UPDATE は channel_id 1個 + message_id 最大200個をバインドしていて、要約の記録に毎回失敗していた。
一方で、UPDATE 前のサマリー生成は10分おきに稼動していたので、LLM APIに毎回同じ200メッセージが投げられていた。

修正したこと

  • shared: D1_MAX_BOUND_PARAMS と confirm_pending_chunks を追加。UPDATE を99件ずつに分割することにした。
  • workers: 上記に対応。Vectorize の deleteByIds にも100件上限があるので同様に分割した。
  • app: tracing-subscriber を初期化して、連続5回失敗したチャンネルを自動要約から外すようにした。
  • ci: workers を PR 段階で clippy にかける。
  • docs: D1 と Vectorize の上限、暴走の指紋を見るクエリ、memory.timestamp が時系列順に並ばない件を追記。

🤖 Generated with Claude Code

migidmnt and others added 3 commits September 9, 2026 07:51
D1は1文あたりのbindパラメータを100個までしか受け付けず、この上限は
d1.batch()の中でも文ごとに個別適用される。PUT /channel/summary の確定
UPDATEは channel_id 1個 + message_id 最大200個をバインドしていたため、
未要約が100件たまったチャンネルでは必ず101個以上になり、prepareの時点で
拒否されていた。

batchはトランザクションなので、確定の失敗がカーソル前進ごとロールバック
する。一方でLLM呼び出しと記憶の作成はその前に完了しているため、進捗が
一切進まないまま10分ごとに同じ200件を要約し直していた。2026-09-02から
6日半で833回、重複記憶833件。

候補条件が pending_count >= 100、取得上限が MAX_MESSAGES_PER_RUN = 200
なので、件数経路で候補になった瞬間にバインドは必ず101..=201になる。
偶発ではなく確定で踏む。

- shared: D1_MAX_BOUND_PARAMS と confirm_pending_chunks を追加し、確定
  UPDATEを99件ずつに分割する。分割してもbatchはトランザクションなので
  原子性は保たれ、行トリガの発火回数も行数で決まるため結果は変わらない。
  workersはワークスペースからexcludeされていて workers/src に書いた
  テストはCIで走らないので、検査可能な形にするためsharedに置く。
- workers: 確定UPDATEを分割版に差し替える。Vectorizeの deleteByIds も
  100件上限があるので同様に分割する(記載は無いがcode 40007で落ちる)。
  search_memory の clamp を定数に結び直す。
- app: tracing-subscriberを初期化する。依存はtracingだけでsubscriberを
  張っていなかったため、この事故のerror!は833回とも捨てられていた。
- app: 連続5回失敗したチャンネルを自動要約から外す。確定できない状態で
  回り続けると要約費用だけが積み上がるため、支払いを有界にする。
- app: 見送ったチャンネルがtick枠を消費していたのを直す。詰まった1つが
  他を飢えさせていた。
- ci: workersをPR段階でclippyにかける。exclude配下なので、これまで
  deployジョブまで一度もコンパイルされていなかった。
- workers/tests/d1_bind_probe.sh: 「定数がプラットフォームの実際の上限を
  超えていないか」を実D1で確かめる。ユニットテストが守るのは
  「コードが定数を超えない」ことだけで、両方揃わないと不変条件が閉じない。
- summary_state.sh: 素のsqlite3ではこの上限を検査できないことを明記する。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
案内として要らない説明・経緯・実装の性質を落とし、生きた制約だけを残す。

- workers/tests/d1_bind_probe.sh を削除。wrangler の認証が要るので CI から
  呼べず、誰も走らせない。実 D1 での確認手順は docs/003_observability.md に
  文章で残す。
- shared: max_variable_bindings を削除し、呼び出し側で D1_MAX_BOUND_PARAMS を
  直接使う。ConfirmChunk の未使用 derive も落とす。
- shared から workers へ: VECTORIZE_MAX_DELETE_IDS を使う側に移す。shared は
  app と worker が共有する DTO の置き場で、app はこの定数を使わない。
- コメントを書き直す。関数 doc は「<入力> から <出力> を返す。」の1行を基本に
  し、事故の再話・正しさの証明・他モジュールとの分担は落とす。
  プラットフォームの上限そのもののような、今も効いている制約は残す。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
出力量の制約は経緯ではなく、subscriberを入れた直後にまさに効く。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gitdmnt

gitdmnt commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

連続5回失敗したチャンネルを自動要約から外すようにするやつって失敗を不可視化するだけなんとちゃうの

migidmnt and others added 6 commits September 9, 2026 08:17
連続失敗で自動要約から外す機構は支払いを有界にするが、諦めたことは
error! 1行しか残らない。docker logs はコンテナごと消え、以後は tick
ごとに黙って飛ばすだけなので、失敗が不可視化される。

外した瞬間に1回だけデバッグチャンネルへ通知し、チャンネル・連続失敗
回数・最後のエラー・再起動するまで止まったままであることを伝える。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Remove commented-out notes regarding cargo test behavior and worker checks.
Removed commented lines about tracing subscriber configuration.
Removed comments in Cargo.toml for clarity.
@gitdmnt

gitdmnt commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

あー眠くてあんまりコード監査してないけどマージしちゃう もう眠いから……

@gitdmnt
gitdmnt merged commit 1906977 into main Sep 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant