Skip to content

Release and docs pollers refresh their ETag even when an embed failure left work behind #211

Description

@liplus-lin-lay

目的

release 面と docs 面で、embed に失敗した item が二度と再試行されない経路を閉じる。どちらも ETag の保存判定が「見送りの理由」を一部しか見ていない。

確認済みの事実(2026-08-03 実装読みで確定。実測は未実施)

#215 で issue / PR 面に入れた不変条件は holdEtag = capped || stats.retryBoundary !== undefined ——やり残しがあるどの理由でも ETag を保存しないという形。残り 2 面はこの形になっていない。

release 面(pollReleases

ETag 保存の分岐は upsertBudgetExhausted のみを見ている(src/poller.tsif (!upsertBudgetExhausted))。#149 で「upsert cap に当たった run では fresh ETag を書かない(次 cron が 304 で短絡して bodyHash 空のまま見送られた release を拾えなくなる)」として入った guard。

しかし見送りが発生する理由は upsert cap だけではない:

  • embed 失敗(result.failed)が guard の外processAndUpsertRelease は失敗時に bodyHash: "" を書いて retry の印を付けるが、この run は upsertBudgetExhausted が false のままなので fresh ETag が保存される。次 cron は If-None-Match が一致して 304 → pollReleases は早期 return → 印の付いた release は、いずれかの release が変更されるまで拾われない。
  • embedding cap(MAX_EMBEDDINGS_PER_RUN)の分岐も guard の外だが、MAX_RELEASE_UPSERTS_PER_REPO_PER_RUN = 10MAX_EMBEDDINGS_PER_RUN = 50 より小さいため upsert cap が必ず先に当たる。現在の定数では到達不能

docs 面(pollDocs

ETag 保存の分岐は holdEtag = fetchBudgetExhausted || deleteBudgetExhausted。release 面より一歩進んでいて 2 つの cap を見ているが、同じ穴が残っている:

  • embed 失敗(failed++)が guard の外。次 run は保持された ETag で tree を取り直すが、embed に失敗した doc は blobSha が store 側で未更新のまま「変更あり」として再検出される……のは tree fetch が 200 を返す場合のみ。fresh ETag を保存してしまうと 304 で早期 return し、再検出の機会そのものが来ない。
  • embedding cap の分岐は break するだけで flag を立てない。ただし MAX_DOC_FETCHES_PER_REPO_PER_RUN = 10 < MAX_EMBEDDINGS_PER_RUN = 50 なので、release 面と同様に到達不能

なお docs 面は fresh ETag を捨てる代わりに直前の ETag を保持する(issue 面は空にする)。どちらも機能する——cap に当たった run は 200 で tree を取れている=その時点で tree は storedEtag と異なるので、次 run も 200 になる。修正時にこの方式を変える必要はない。

監査済みで穴が無い面

修正方針

  1. 両面の ETag 保存判定を「やり残しがあるか」で決める。 #215holdEtag と同じ形に揃える——release 面は upsertBudgetExhausted || failed > 0、docs 面は fetchBudgetExhausted || deleteBudgetExhausted || failed > 0。3 面で判定の形が揃うことが要点で、面ごとに違う条件を持たせない。
  2. 到達不能な embedding cap 分岐を両面で決着させる。 削除するか、定数の関係(fetch/upsert cap < embedding cap)を明示して分岐が届かないことを読める形にするか。どちらでもよいが両面で同じ判断にし、理由をコメントに残すこと。 現状は「届かない分岐が黙って残っている」状態で、次に定数を触る者が関係に気づけない。

制約

対象ファイル

背景

#210 の原因特定中に release 面で見つかり、その後 #211 の着手前に ETag 保存箇所を全面監査して docs 面でも同型を確認した。3 面のうち 1 面(issue / PR)だけが #215 で正しい形になっており、残り 2 面が取り残されていた形。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug動いていない、壊れているdone役目完了、orchestration (review / merge / close) フェーズ待ちready本文が実装開始できる形まで収束している状態。ただし更新は継続可能

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions