Conversation
pullImage() made a single docker pull attempt and, on failure, only handled
the no-matching-manifest case (falling back to linux/amd64). Any transient
registry failure — 5xx from Docker Hub, rate limiting, TLS/connection
timeouts — propagated straight out and aborted the benchmark during setup,
with no retry and no cache.
Add failure classification and bounded retries:
classifyPullError() -> 'manifest' | 'transient' | 'fatal'
- 'manifest' no native-platform manifest; go straight to the amd64 fallback
(deterministic, so retrying the native pull is pointless)
- 'transient' 5xx, 429/rate limit, connection/TLS timeouts, unexpected EOF;
retried up to DOCKER_PULL_MAX_ATTEMPTS (3) with exponential
backoff from DOCKER_PULL_BASE_DELAY_MS (5s)
- 'fatal' auth failures, missing repos, unknown manifests; fail fast so a
bad image reference is not hammered three times
A transient failure is never masked behind a platform fallback: if retries are
exhausted the original error is rethrown. Backoff is injectable (sleep), and
retries are surfaced via onProgress plus an optional onRetry hook.
The existing manifest-fallback contract is unchanged, and pullImage's default
call signature still works.
Verified against a corpus of 276 benchmark logs where Docker Hub flakiness
aborted runs mid-setup.
Refs #76
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
pullImage()made onedocker pullattempt. On failure it handled only the no-matching-manifest case (falling back tolinux/amd64) and rethrew everything else:Docker Hub is intermittently unreliable (5xx during their incidents, unauthenticated pull rate limits, TLS/connection timeouts on CI runners). Any of those aborted the run during container setup with no retry — a benchmark that had already spent real model spend and wall-clock time was thrown away by a transient registry hiccup.
Change
Classify the failure, then retry only what retrying can actually fix.
src/lib/docker.tsmanifestno matching manifest,no match for platformlinux/amd64fallback.transient503/502/504,429/toomanyrequests,connection refused/reset,TLS handshake timeout,i/o timeout,unexpected EOF,Gateway Time-outDOCKER_PULL_MAX_ATTEMPTS(3) with exponential backoff fromDOCKER_PULL_BASE_DELAY_MS(5s) → 5s, 10sfatalTwo deliberate design decisions:
linux/amd64on a 503 would silently hide the real cause and make the run slower under emulation for no reason.Backoff is injectable via
options.sleepso tests run instantly, retries are surfaced throughonProgress(and an optionalonRetryhook), andpullAndStartContainers()now reports retry activity for both images.Backwards compatibility
pullImage(image, onProgress?)still works with no third argument.manifest→linux/amd64fallback contract and itsbooleanreturn are unchanged.PullRetryOptionsis entirely optional.New exports:
classifyPullError,PullRetryOptions,DOCKER_PULL_MAX_ATTEMPTS,DOCKER_PULL_BASE_DELAY_MS.Tests
tests/unit/docker-retry.test.ts(22 new tests) covers:[1000, 2000])DOCKER_PULL_MAX_ATTEMPTSonRetryarguments, custommaxAttemptsOne existing test in
tests/unit/docker.test.tsneeded updating: its "non-manifest error" example was'connection refused', which is now correctly classified as transient and retried. The test's actual intent — no amd64 fallback for non-manifest errors — still holds and is now asserted directly (every attempt is a native pull), with a no-op sleep injected so it doesn't wait.Verification
Before: 421 tests. After: 441 tests, all passing.
Risk
Low, and opt-out-free by design. Retries only add bounded delay (max ~15s of backoff) in cases that previously failed outright. Fatal errors still surface immediately on the first attempt, so misconfigured images fail as fast as before. The one behavioural change to be aware of: a transient error now appears after ~15s and 3 attempts instead of instantly.
Closes #76