目的
/admin/backfill-issue-index の per-call 上限を実測に合わせる。あわせて、予算切れで見送った候補を cursor が追い越す設計を直し、上限値の正確さに結果が依存しない形にする。
確認済みの事実(2026-08-03 本番実行、#210 の backfill 掃引)
limit を変えて全 repo を掃引した結果:
| limit |
結果 |
| 50 |
1 回目の呼び出しで Too many subrequests by single Worker invocation(呼び出し全体が 500 で失敗)。4 repo すべてで同じ(dipper_ai のみ候補が 50 未満で完走) |
25(DEFAULT_INDEX_BACKFILL_LIMIT) |
全 48 呼び出しが例外なく indexed=24 absent=0 failed=1。ランダムではなく毎回きっちり 1 件、repo をまたいで同一 |
| 15 |
全 8 呼び出しで failed=0 |
25 で毎回 1 件だけ落ち、15 でゼロになることから、失敗は item の内容ではなく 1 呼び出しあたりの予算で決まっている。
実消費の見積もり(観測境界からの算術)
limit=25 で 24 件成功・25 件目で予算切れ、という境界が repo をまたいで一定だった。1 invocation の予算を 1000、呼び出しごとの固定費(fetchHighestItemNumber 1 + scan chunk の D1 query 数)を O とすると:
24c + O <= 1000 < 25c + O
O を 0〜20 の範囲に置いても c ≈ 40(39.2〜41.7)に収まる。1 候補あたり約 40 subrequest。
コード上の見積もりはこれと合っていない:
DEFAULT_INDEX_BACKFILL_LIMIT = 25 のコメント: 1 候補あたり「GitHub fetch + embed + Vectorize + D1 + store fan-out、1000 subrequest のうち ~5」
MAX_INDEX_BACKFILL_LIMIT = 100 のコメント: 「100 候補 × ~5 ≈ 500、予算の半分」
実装上の論理的な呼び出し数は確かに 6 前後(GitHub fetch 1 / store DO 2 / Workers AI 1 / Vectorize 1 / D1 1)。論理呼び出し 1 回が subrequest 1 個に対応していないことになるが、どの binding が見積もりを超えて消費しているかは未特定。この issue で機構を断定しないこと。既存の MAX_COMMENT_FETCHES_PER_REPO_PER_RUN(src/poller.ts)のコメントも同種の過小見積もり(~3-5)から実測で下方修正された履歴を持つ。
修正方針
1. 予算切れで見送った候補を cursor が追い越さない(構造側、こちらが本体)
現状は processAndUpsertIssue が内部で embed 失敗を catch して failed を返し、cursor はその候補を追い越す。結果として 1 回の掃引では拾い切れず、実運用では 25 で掃引 → 15 で再掃引の 2 パスが必要だった。
#215 で issue / PR poller の watermark に入れたのと同じ不変条件を持ち込む——取り込みそこねた最初の候補の手前で cursor を止める。 そうすれば次の呼び出しがちょうどその候補から再開し、上限値が多少ずれていても取りこぼしはゼロになる。定数の正確さに結果が依存しない形が要点。
トレードオフは #215 と同じで、恒久的に失敗する候補があると掃引が止まる。ただしこれは人間 / AI が駆動する admin 掃引なので、nextCursor が進まないこととして応答に現れる(cron の暗黙の停止とは違う)。停止時に手動で cursor を進められる余地は残すこと。
2. 定数を実測に合わせる
DEFAULT_INDEX_BACKFILL_LIMIT: 15(本番で failed=0 を実測済みの値)
MAX_INDEX_BACKFILL_LIMIT: c ≈ 40 から余裕を取った値。100 は到達不能なので必ず下げる。 通らない上限を残すのは、次に使う人(AI 含む)を同じ穴に落とす
- 根拠コメントは「~5」を別の推定値に差し替えるのではなく、上の観測境界と算術を書く。推定値を推定値で置き換えると同じ誤りになる
制約
対象ファイル
src/backfill-issue-index.ts(cursor の不変条件、DEFAULT_INDEX_BACKFILL_LIMIT / MAX_INDEX_BACKFILL_LIMIT と根拠コメント)
docs/installation.md / docs/installation.ja.md(§15 の limit 記述と掃引手順)
docs/0-requirements.md / docs/0-requirements.ja.md(cursor の不変条件を書いている箇所があれば)
- 回帰テスト
背景
#210 の backfill を本番実行して判明した。索引は 6 repo すべて 100% に到達済みで、修復自体は完了している。本 issue は器具側の精度の問題。
目的
/admin/backfill-issue-indexの per-call 上限を実測に合わせる。あわせて、予算切れで見送った候補を cursor が追い越す設計を直し、上限値の正確さに結果が依存しない形にする。確認済みの事実(2026-08-03 本番実行、#210 の backfill 掃引)
limitを変えて全 repo を掃引した結果:Too many subrequests by single Worker invocation(呼び出し全体が 500 で失敗)。4 repo すべてで同じ(dipper_ai のみ候補が 50 未満で完走)DEFAULT_INDEX_BACKFILL_LIMIT)indexed=24 absent=0 failed=1。ランダムではなく毎回きっちり 1 件、repo をまたいで同一failed=025 で毎回 1 件だけ落ち、15 でゼロになることから、失敗は item の内容ではなく 1 呼び出しあたりの予算で決まっている。
実消費の見積もり(観測境界からの算術)
limit=25で 24 件成功・25 件目で予算切れ、という境界が repo をまたいで一定だった。1 invocation の予算を 1000、呼び出しごとの固定費(fetchHighestItemNumber1 + scan chunk の D1 query 数)を O とすると:O を 0〜20 の範囲に置いても c ≈ 40(39.2〜41.7)に収まる。1 候補あたり約 40 subrequest。
コード上の見積もりはこれと合っていない:
DEFAULT_INDEX_BACKFILL_LIMIT = 25のコメント: 1 候補あたり「GitHub fetch + embed + Vectorize + D1 + store fan-out、1000 subrequest のうち ~5」MAX_INDEX_BACKFILL_LIMIT = 100のコメント: 「100 候補 × ~5 ≈ 500、予算の半分」実装上の論理的な呼び出し数は確かに 6 前後(GitHub fetch 1 / store DO 2 / Workers AI 1 / Vectorize 1 / D1 1)。論理呼び出し 1 回が subrequest 1 個に対応していないことになるが、どの binding が見積もりを超えて消費しているかは未特定。この issue で機構を断定しないこと。既存の
MAX_COMMENT_FETCHES_PER_REPO_PER_RUN(src/poller.ts)のコメントも同種の過小見積もり(~3-5)から実測で下方修正された履歴を持つ。修正方針
1. 予算切れで見送った候補を cursor が追い越さない(構造側、こちらが本体)
現状は
processAndUpsertIssueが内部で embed 失敗を catch してfailedを返し、cursor はその候補を追い越す。結果として 1 回の掃引では拾い切れず、実運用では 25 で掃引 → 15 で再掃引の 2 パスが必要だった。#215 で issue / PR poller の watermark に入れたのと同じ不変条件を持ち込む——取り込みそこねた最初の候補の手前で cursor を止める。 そうすれば次の呼び出しがちょうどその候補から再開し、上限値が多少ずれていても取りこぼしはゼロになる。定数の正確さに結果が依存しない形が要点。
トレードオフは #215 と同じで、恒久的に失敗する候補があると掃引が止まる。ただしこれは人間 / AI が駆動する admin 掃引なので、
nextCursorが進まないこととして応答に現れる(cron の暗黙の停止とは違う)。停止時に手動で cursor を進められる余地は残すこと。2. 定数を実測に合わせる
DEFAULT_INDEX_BACKFILL_LIMIT: 15(本番で failed=0 を実測済みの値)MAX_INDEX_BACKFILL_LIMIT: c ≈ 40 から余裕を取った値。100 は到達不能なので必ず下げる。 通らない上限を残すのは、次に使う人(AI 含む)を同じ穴に落とす制約
対象ファイル
src/backfill-issue-index.ts(cursor の不変条件、DEFAULT_INDEX_BACKFILL_LIMIT/MAX_INDEX_BACKFILL_LIMITと根拠コメント)docs/installation.md/docs/installation.ja.md(§15 のlimit記述と掃引手順)docs/0-requirements.md/docs/0-requirements.ja.md(cursor の不変条件を書いている箇所があれば)背景
#210 の backfill を本番実行して判明した。索引は 6 repo すべて 100% に到達済みで、修復自体は完了している。本 issue は器具側の精度の問題。