fix(registry): retry failed startup discovery once in the background

A provider whose discovery failed during the warm fill at pod start
went without discovered models for a full discovery interval, which is
1 hour by default. It also stayed that way until a request arrived
after the interval. Production logs showed this for openai-audio and
anthropic when outbound calls were slow at startup.

A provider that fails the warm fill now gets one background retry
after 10s. The retry has its own timeout and uses the same refresh
bookkeeping. It is skipped when a refresh is already in flight or has
finished since the warm fill, and it is not scheduled when the parent
context was canceled. A recovery logs one INFO line. If the retry also
fails, the existing breaker behaviour applies unchanged.

The retry delay and the warm-fill timeout can be set per registry for
tests. yaegi-check probes the retry under the interpreter.

Claude-Session: https://claude.ai/code/session_01V5jYJNxpDFdJkyyExpADrv
This commit is contained in:
2026-09-24 06:04:43 +01:00
parent 8807f4d27d
commit ce39b8eb37
5 changed files with 846 additions and 13 deletions
+16
View File
@@ -1551,6 +1551,22 @@ one.
- **Dashboard**: the Models page's provider health panel shows a
provider's current state and, while open, when it will next be
probed — see [Admin](#admin).
- **Cold-start retry**: when a provider's synchronous warm-fill fetch
fails at startup, the gateway schedules ONE background retry after a
fixed 10-second delay (`warmFillRetryDelay`, not configurable). The
retry uses the same `finishRefresh` bookkeeping as every other
refresh, so the breaker state stays consistent with it. This recovers
a transient cold-start blip, such as slow egress on the very first
outbound call, not a provider that stays broken. If the retry also
fails, the provider falls back to the breaker behaviour described
above, unchanged. A successful retry logs one INFO line naming the
provider. A failed retry logs the existing ERROR line. The retry
shares its in-flight guard with the regular refresh cycle. It skips
if a refresh is already running, or if one already completed since
the warm-fill failure, so it never runs a second, overlapping
request against the same provider. A warm fill that fails because
the gateway itself is shutting down (its parent context canceled)
schedules no retry at all.
## Unified vs. passthrough