Skip to content

fix(poller): cap pollDocs / pollReleases fan-out to fit Worker subrequest budget (#149)#150

Merged
liplus-lin-lay merged 1 commit into
mainfrom
149-bugpoller-polldocs-pollreleases-exhaust-worker-subrequest-budget
May 7, 2026
Merged

fix(poller): cap pollDocs / pollReleases fan-out to fit Worker subrequest budget (#149)#150
liplus-lin-lay merged 1 commit into
mainfrom
149-bugpoller-polldocs-pollreleases-exhaust-worker-subrequest-budget

Conversation

@liplus-lin-lay
Copy link
Copy Markdown
Member

Closes #149

背景

PR #144pollComments の per-repo per-run fetch cap を 30→10 に下げて :15 cron は安定したが、:00 LIGHT_CRON で pollDocs / pollReleases / pollRepo を 5 repos 直列に走らせる側で同 root cause が独立に発生していた。2026-05-07 00:01 JST に Cloudflare Workers Observability で Too many subrequests by single Worker invocation が直近 24h で 90 件発火、Failed to poll docs for {repo} / Failed to poll releases for {repo} / Failed to poll {repo} の 3 surface 全てで観測。

変更内容

src/poller.ts:

  • MAX_DOC_FETCHES_PER_REPO_PER_RUN = 10 を新設し、pollDocs の changed-entry ループに fetchesIssued counter + warn log を追加 (PR fix(poller): lower pollComments cap from 30 to 10 to fit Worker subrequest budget #144 / MAX_COMMENT_FETCHES_PER_REPO_PER_RUN と同 shape)。
  • MAX_RELEASE_UPSERTS_PER_REPO_PER_RUN = 10 を新設し、pollReleases の release ループに upsertsIssued counter + warn log を追加。既存 MAX_EMBEDDINGS_PER_RUN (50) より手前に新 cap を置き、変更なし release の change-detection subrequest も含めて bound する。
  • どちらの surface も cap exhausted 時に 新 ETag watermark を保存しない。新 ETag を書くと次回 cron が 304 で短絡して未処理分を永続的に見落とすため、prior storedEtag を保持しつつ lastPolledAt だけ更新する。

subrequest 予算の見積もり

LIGHT_CRON は 1 Worker invocation で 5 repos × 3 surfaces を 1000-subrequest budget 共有で実行。1 item あたり ~5 subrequests (Workers AI embed + Vectorize upsert + D1 FTS upsert + Store DO + GH API)。

  • target headroom: 800 subrequests 以下 (200 を parent cron / error retry / watermark write 等に確保)
  • 800 / 5 repos / 3 surfaces ≈ 53 / surface / repo
  • 53 / ~5 subrequests-per-item ≈ 10 items
  • 結果: cap = 10

worst-case: 5 repos × 10 × ~5 = 250 / surface, 3 surfaces × 250 = 750 subrequests, 1000 budget に対し 250 の余裕。pollComments (PR #144) と同じ envelope。

観測

docs check

docs/0-requirements.md / docs/0-requirements.ja.mdMAX_WIKI_EMBEDDINGS_PER_RUN の数値言及はあるが、pollDocs / pollReleases 個別 cap の数値仕様は無く、subrequest 予算の架構レベル記述のみ。docs 変更は不要 (確認済み)。

related

…uest budget

PR #144 で `pollComments` の per-repo per-run fetch cap を 10 に引き下げて以降、`:15` cron は安定したが、`:00` LIGHT_CRON で `pollDocs` / `pollReleases` / `pollRepo` を 5 repos 直列に走らせる side が同 root cause で `Too many subrequests by single Worker invocation` を出している (issue #149, 2026-05-07 00:01 JST 観測, 24h で 90 errors)。

LIGHT_CRON は 1 Worker invocation で 5 repos x 3 surfaces を共通の 1000-subrequest budget で回す。1 doc fetch / 1 release upsert あたり ~5 subrequests (Workers AI embed + Vectorize upsert + D1 FTS upsert + Store DO + GH API) を消費するため、surface 内で fan-out cap が無いと一発で 1000 を超える。

本 PR で `MAX_DOC_FETCHES_PER_REPO_PER_RUN = 10` と `MAX_RELEASE_UPSERTS_PER_REPO_PER_RUN = 10` を `src/poller.ts` に追加し、PR #144 と同じ shape (warn log + per-repo counter) で運用する。worst-case 計算: 5 repos x 10 items x ~5 subrequests = 250 / surface, 3 surfaces x 250 = 750 subrequests, 1000 budget に対し 250 の余裕。

ETag watermark の取り扱いも合わせて修正: cap で run を打ち切った場合に新 ETag を保存すると次回 cron が 304 で短絡して未処理分を見落とすため、cap exhausted のときは prior storedEtag を保持 (lastPolledAt のみ更新)。`pollDocs` / `pollReleases` 双方で同様。

Refs #149
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
github-rag-mcp e598843 May 07 2026, 02:01 AM

Copy link
Copy Markdown
Member Author

@liplus-lin-lay liplus-lin-lay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI 自己レビュー (auto mode)

検証済み項目

実装パターン整合性

  • PR #144 (MAX_COMMENT_FETCHES_PER_REPO_PER_RUN, commit 2711932) の cap shape と一致: 定数定義 + counter 変数 + warn log + 越境ループ break。pollCommentsfetchBudgetExhausted flag pattern を pollDocs / pollReleases 双方に転用。
  • 数値設計 (10 = 800 subrequests / 5 repos / 3 surfaces / ~5 subreq-per-item) を src コメント + commit body + PR body 3 箇所で worst-case math 付きで明示。

ETag watermark 取り扱い

  • pollDocs / pollReleases 双方で cap exhausted 時に新 ETag を保存しないルートを追加。新 ETag を保存すると次回 cron が 304 短絡して未処理 doc / release を永続的に見落とす副作用を排除。
  • prior storedEtag 保持 + lastPolledAt 更新で run 観測性は維持。

budget 順序

  • pollReleases で新 cap を MAX_EMBEDDINGS_PER_RUN チェックの手前に配置。change-detection 段階の Store DO subrequest も bound する設計判断を src コメントに記載。

ビルド検証

  • npx tsc --noEmit 通過 (clean exit)。
  • npx wrangler deploy --dry-run 通過 (Total Upload 2226.23 KiB / gzip 378.17 KiB, 全 binding 解決)。
  • GitHub Actions CI: test pass, CI gate pass, Workers Builds pass。

docs check

  • docs/0-requirements.md / docs/0-requirements.ja.mdMAX_COMMENT_FETCHES / MAX_DOC / MAX_RELEASE / pollDocs / pollReleases / subrequest / fetch budget / PER_REPO_PER_RUN で grep。MAX_WIKI_EMBEDDINGS_PER_RUN の数値言及 + LIGHT_CRON / COMMENTS_CRON / DIFFS_CRON / WIKI_CRON の cron 構成記述はあるが、pollDocs / pollReleases 個別 cap の数値仕様は不在。docs 更新不要と判断。

検証できなかった項目

  • 本番 traffic 下での 1000-subrequest budget 実測: 計算上は 750 subrequests 想定だが、busy repo (e.g. dipper_ai) の実 LIGHT_CRON で per-cron 実測値は merge 後 24h Observability で要確認。pollComments#134 → PR #144 で 30 → 10 を 1 段階で済ませた前例があるため、10 で初期投入 → 観測 → 必要なら段階的に下げる方針。
  • ETag 保留パスの実 304 short-circuit 回避: 単体ロジックは確認したが、Store DO の watermark 永続化が etag: storedEtag (undefined になる初回 fetch path 含む) で正しく書き戻るかは run-time で要確認。storedEtag が undefined ならば次 cron は無条件 fetch なので機能上問題なし。

残留リスク

  • cap=10 は pollComments の 1 段階目 (cap=30) と同等の保守性: 仮に 10 でも溢れる構造的問題があれば PR #144 と同じく追加引き下げが必要。fan-out 計算 (~5 subreq/item) は釘刺し前提値で、実 traffic で 7-8 subreq/item に振れる場合は 5 repos × 10 × 8 = 400/surface × 3 = 1200 で再 overflow 余地あり。
  • 持ち越し挙動の負荷分散: 大量 changed .md がある repo (e.g. docs 一括 reorg) で毎時 10 docs しか処理できない → 100 docs 変更で 10 cron run = 10 時間。許容範囲だが緊急 indexing 用途の手動 trigger は別途必要になる可能性。
  • MAX_EMBEDDINGS_PER_RUN = 50 の実効性: 新 cap (10) が常に手前で発火するため、MAX_EMBEDDINGS_PER_RUN の per-surface 50 は事実上 dead code 化。後続 PR で整理候補 (本 PR 範囲外, follow-up issue 候補)。

@liplus-lin-lay liplus-lin-lay merged commit 9153a56 into main May 7, 2026
3 checks passed
@liplus-lin-lay liplus-lin-lay deleted the 149-bugpoller-polldocs-pollreleases-exhaust-worker-subrequest-budget branch May 7, 2026 02:04
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.

bug(poller): pollDocs / pollReleases exhaust Worker subrequest budget

1 participant