fix(historical): bound batch miss resolution latency - #52
Conversation
|
/review-workflow |
|
Review started (review-pr-workflow): https://github.com/yearn/yearn-prices/actions/runs/33520588951 |
Move the DeFiLlama 429 no-retry from the client constructor to the request-path client, so cron and backfill scripts keep their Retry-After backoff. Isolate each batchHistorical payload group: one failing group no longer cancels the groups after it; the error only surfaces when nothing resolved. Drop the batch-source filter on the miss fallback, so a pair the 6h batch matcher rejects is still tried through the single-coin lookup.
|
/review |
|
Review started (review-pr): https://github.com/yearn/yearn-prices/actions/runs/33524360875 |
SummaryReduces Checks not run: gitconfig-mask: sentinel Issues
VerdictREQUEST_CHANGES How This Was ReviewedThis review was conducted using the review-pr skill. |
…re cache - fetch DeFiLlama payload groups concurrently so a hung group cannot consume the route deadline and starve the single-coin fallback - halve request-path client timeout to 2.5s, leaving the fallback the other half of the 5s budget - validate batchHistorical coins before the edge-cache read so an over-limit payload cannot be served a cached 200 - document the exact route's shared no-retry/timeout policy
…lve branch - resolveBatch: pair whose batch group failed skips the DeFiLlama single lookup - batch route always uses resolveBatch; mocks and registry tests updated - docs/comment: 2.5s timeout is per attempt, 5xx still retries
murderteeth
left a comment
There was a problem hiding this comment.
Summary
Adds a provider-native DeFiLlama batch path for batchHistorical misses, with unresolved pairs falling through to the source chain, a five-second route-entry deadline that returns completed partial results, and a retryRateLimits opt-out on the request-path client. The pre-cache parseBatchCoins guard in src/index.ts correctly closes the over-limit-payload hole that day-normalized cache keys would otherwise open.
Issues
-
src/registries/historical.ts:116 - Fallback still starts only after the batch stage finishes (high) — If DeFiLlama is slow or returning 5xx, the batch call can consume the whole five-second budget before chainlink or on-chain resolution is ever attempted, so pairs those sources could have priced come back empty and unpersisted. Parallelizing the groups in 2bf1461 bounds one group, not the stage: the 2.5s cap is per attempt, and 5xx retries three times with 1s/2s sleeps — 10.5s against a 5s deadline.
- Done when: with the DeFiLlama batch endpoint failing on every attempt, a
batchHistoricalrequest containing a chainlink- or on-chain-priced miss still returns that pair's price inside the route deadline, with a test covering it. - Provenance: dd19f4e
- Done when: with the DeFiLlama batch endpoint failing on every attempt, a
-
src/sources/defillama/historical.ts:95 - A partly-failed batch still fans out one request per pair (medium) — The group error is reported only when every group came back empty. So if one group succeeds and another is rate-limited, each of the rate-limited group's pairs gets a fresh single-coin call — the fan-out this PR exists to remove, aimed at a provider that just returned 429, now with 429 retries disabled.
docs/routes.md:284promises the opposite.- Done when: with one payload group failing and another returning at least one usable price, no DeFiLlama single-coin request is issued for the failed group's coins during that same request.
- Provenance: 39a84b9
-
src/registries/historical.ts:121 - A succeeding group's pairs are blamed for another group's failure (medium) — When the batch call throws, every target is marked failed, including pairs whose own group returned 200 but matched no sample. Those skip the single-coin lookup that uses a wider search window and would likely have priced them, and their
resolve-miss-failedlog carries the other group's error instead of their own not-found.- Done when: when one group fails and another returns 200 with no matched sample, the second group's pairs are still tried against the DeFiLlama single-coin lookup and log their own NOT_FOUND.
- Provenance: dd19f4e
Verdict
REQUEST_CHANGES
How This Was Reviewed
Reviewed with the review-pr-workflow skill —
5 review lenses, each finding independently verified by codex. 3 candidate findings were refuted and dropped.
Batch stage now races a 2.5s budget so a slow or retrying DeFiLlama call cannot eat the route deadline; pending pairs fall through to chainlink/on-chain. Group failures are reported per group via onFailed instead of one global throw, so a succeeding group's unmatched pairs keep their single-coin retry and a failed group's pairs skip it.
The 2.5s batch stage cap marked every pending pair failed, including pairs whose own payload group had already answered 200 with no match. Those skipped the DeFiLlama single-coin lookup, and unsupported-chain targets lost their own NOT_FOUND to the batch UNAVAILABLE. onFailed becomes onSettled: a group reports its targets when it is done, with an error when it failed and without one when it answered. The timeout now blames only pairs whose group never answered.
|
All three findings addressed — plus one regression the fix for the first one introduced, caught before you had to. 1. Fallback started only after the batch stage — 2. Partly-failed batch still fanned out per pair — 3. Succeeding group's pairs blamed for another group's failure — Side effect of that first fix, found in a self-review of Fix:
|
|
Merging this just to reduce the latency for danil on yearn.fi - will revisit. |
Summary
Miss-heavy
batchHistoricalrequests issued one DeFiLlama request per pair and retried rate-limited calls on the same schedule. That increased provider fan-out and kept the response waiting on the slowest fallback.Changes
429, apply a 2.5-second per-attempt provider timeout, and return completed partial results when the five-second resolution deadline expires. Offline backfill keeps its explicit retry policy.[start, end]arrays.VALUESinstead of arequestedCTE.Testing
npm run typechecknpm run lintnpx vitest run --exclude test/enso.integration.test.ts(463 tests passed)npm test(463 tests passed; 2 live Enso tests failed because the external API returned401)