perf(edge): reduce unnecessary Vercel edge invocations#1176
Conversation
Phase 1 — Client-only fixes: - Remove predictions double getHydratedData read from data-loader.ts; fetchPredictions() handles hydration internally - Fix UCDP delete-on-read race: read hydratedUcdp once in data-loader, pass to both fetchUcdpClassifications() and fetchUcdpEvents() Phase 2 — Batch RPCs (proto + server + client): - Add GetHumanitarianSummaryBatch RPC: replaces 20-request HAPI fanout with single batch call (getCachedJsonBatch + per-key Redis caching) - Add GetFredSeriesBatch RPC: replaces 7-request FRED fanout with single batch call (same pattern) - Both batch RPCs have 404 deploy-skew fallback to per-item calls Phase 3 — Seed gap: - Add seed-service-statuses.mjs standalone seed script - Add 15-min warm-ping loop in AIS relay for service statuses - Remove serviceStatuses from ON_DEMAND_KEYS in health.js Net savings: up to 28 edge calls eliminated on cold miss per page load.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c2281ee90
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const resp = await hapiBatchBreaker.execute(async () => { | ||
| return client.getHumanitarianSummaryBatch( | ||
| { countryCodes: [...HAPI_COUNTRY_CODES] }, | ||
| { signal: AbortSignal.timeout(30_000) }, | ||
| ); |
There was a problem hiding this comment.
Preserve 404 deploy-skew fallback for HAPI batch calls
This hapiBatchBreaker.execute(...) call cannot trigger the catch fallback below, because CircuitBreaker.execute swallows thrown errors and returns the provided default value instead of rethrowing (see src/utils/circuit-breaker.ts). In the rollout-skew case where get-humanitarian-summary-batch still returns 404, this path now yields an empty batch response and skips the existing per-country fallback RPCs, causing humanitarian summaries to disappear until all backends are updated.
Useful? React with 👍 / 👎.
| const resp = await fredBatchBreaker.execute(async () => { | ||
| return client.getFredSeriesBatch( | ||
| { seriesIds: FRED_SERIES.map((c) => c.id), limit: 120 }, | ||
| { signal: AbortSignal.timeout(30_000) }, | ||
| ); |
There was a problem hiding this comment.
Preserve 404 deploy-skew fallback for FRED batch calls
The fredBatchBreaker.execute(...) invocation also prevents the catch-based 404 fallback from ever running, because breaker failures are converted into emptyFredBatchFallback rather than thrown. When clients hit an older backend that lacks /get-fred-series-batch, this code will return no FRED series instead of falling back to the existing per-series getFredSeries calls, so macro data can go blank during deployment skew.
Useful? React with 👍 / 👎.
P1: Fix dead 404 deploy-skew fallback — circuit breaker was swallowing the ApiError before the catch block could detect it. Move 404 fallback inside the breaker callback so it executes before the breaker catches. P2: Replace 172-line seed-service-statuses.mjs (duplicated parser logic) with a 60-line warm-ping that triggers the existing RPC handler. P2: Extract shared ISO2_TO_ISO3 mapping to conflict/v1/_shared.ts, eliminating duplication between single and batch HAPI handlers. P3: Remove unnecessary UPSTASH_ENABLED guard from relay warm-ping (it calls Vercel RPC, not Redis directly). P3: Clean up unused per-series FRED breakers and fetchSingleFredSeries (replaced by batch breaker). Update getFredStatus() accordingly.
HAPI batch: replace serial loop with groups of 5 concurrent fetches using Promise.allSettled for partial-success resilience. Bump client timeout to 60s (4 rounds × 15s upstream timeout worst case). FRED batch: replace serial loop with fully parallel Promise.allSettled (max 10 series, each hits separate FRED endpoint). Both changes prevent empty-result regression on cold cache that the serial approach caused when upstream latency exceeded the client timeout.
* perf(edge): reduce unnecessary Vercel edge invocations (koala73#6 findings) Phase 1 — Client-only fixes: - Remove predictions double getHydratedData read from data-loader.ts; fetchPredictions() handles hydration internally - Fix UCDP delete-on-read race: read hydratedUcdp once in data-loader, pass to both fetchUcdpClassifications() and fetchUcdpEvents() Phase 2 — Batch RPCs (proto + server + client): - Add GetHumanitarianSummaryBatch RPC: replaces 20-request HAPI fanout with single batch call (getCachedJsonBatch + per-key Redis caching) - Add GetFredSeriesBatch RPC: replaces 7-request FRED fanout with single batch call (same pattern) - Both batch RPCs have 404 deploy-skew fallback to per-item calls Phase 3 — Seed gap: - Add seed-service-statuses.mjs standalone seed script - Add 15-min warm-ping loop in AIS relay for service statuses - Remove serviceStatuses from ON_DEMAND_KEYS in health.js Net savings: up to 28 edge calls eliminated on cold miss per page load. * fix(edge): address code review findings (P1–P3) P1: Fix dead 404 deploy-skew fallback — circuit breaker was swallowing the ApiError before the catch block could detect it. Move 404 fallback inside the breaker callback so it executes before the breaker catches. P2: Replace 172-line seed-service-statuses.mjs (duplicated parser logic) with a 60-line warm-ping that triggers the existing RPC handler. P2: Extract shared ISO2_TO_ISO3 mapping to conflict/v1/_shared.ts, eliminating duplication between single and batch HAPI handlers. P3: Remove unnecessary UPSTASH_ENABLED guard from relay warm-ping (it calls Vercel RPC, not Redis directly). P3: Clean up unused per-series FRED breakers and fetchSingleFredSeries (replaced by batch breaker). Update getFredStatus() accordingly. * fix(edge): use concurrent fetches in batch handlers HAPI batch: replace serial loop with groups of 5 concurrent fetches using Promise.allSettled for partial-success resilience. Bump client timeout to 60s (4 rounds × 15s upstream timeout worst case). FRED batch: replace serial loop with fully parallel Promise.allSettled (max 10 series, each hits separate FRED endpoint). Both changes prevent empty-result regression on cold cache that the serial approach caused when upstream latency exceeded the client timeout.
* perf(edge): reduce unnecessary Vercel edge invocations (koala73#6 findings) Phase 1 — Client-only fixes: - Remove predictions double getHydratedData read from data-loader.ts; fetchPredictions() handles hydration internally - Fix UCDP delete-on-read race: read hydratedUcdp once in data-loader, pass to both fetchUcdpClassifications() and fetchUcdpEvents() Phase 2 — Batch RPCs (proto + server + client): - Add GetHumanitarianSummaryBatch RPC: replaces 20-request HAPI fanout with single batch call (getCachedJsonBatch + per-key Redis caching) - Add GetFredSeriesBatch RPC: replaces 7-request FRED fanout with single batch call (same pattern) - Both batch RPCs have 404 deploy-skew fallback to per-item calls Phase 3 — Seed gap: - Add seed-service-statuses.mjs standalone seed script - Add 15-min warm-ping loop in AIS relay for service statuses - Remove serviceStatuses from ON_DEMAND_KEYS in health.js Net savings: up to 28 edge calls eliminated on cold miss per page load. * fix(edge): address code review findings (P1–P3) P1: Fix dead 404 deploy-skew fallback — circuit breaker was swallowing the ApiError before the catch block could detect it. Move 404 fallback inside the breaker callback so it executes before the breaker catches. P2: Replace 172-line seed-service-statuses.mjs (duplicated parser logic) with a 60-line warm-ping that triggers the existing RPC handler. P2: Extract shared ISO2_TO_ISO3 mapping to conflict/v1/_shared.ts, eliminating duplication between single and batch HAPI handlers. P3: Remove unnecessary UPSTASH_ENABLED guard from relay warm-ping (it calls Vercel RPC, not Redis directly). P3: Clean up unused per-series FRED breakers and fetchSingleFredSeries (replaced by batch breaker). Update getFredStatus() accordingly. * fix(edge): use concurrent fetches in batch handlers HAPI batch: replace serial loop with groups of 5 concurrent fetches using Promise.allSettled for partial-success resilience. Bump client timeout to 60s (4 rounds × 15s upstream timeout worst case). FRED batch: replace serial loop with fully parallel Promise.allSettled (max 10 series, each hits separate FRED endpoint). Both changes prevent empty-result regression on cold cache that the serial approach caused when upstream latency exceeded the client timeout.
Summary
getHydratedDataread —fetchPredictions()handles hydration internally (saves 1 edge call)GetHumanitarianSummaryBatchRPC — replaces 20-request HAPI fanout with single batch call (saves 19 edge calls on cold miss)GetFredSeriesBatchRPC — replaces 7-request FRED fanout with single batch call (saves 6 edge calls on cold miss)hydratedUcdponce in data-loader, pass to both consumers (saves 1 edge call)Net savings: up to 28 edge calls eliminated on cold miss per page load.
Batch RPCs
Both batch handlers use
getCachedJsonBatch()for Redis pipeline lookups +cachedFetchJson()for cache misses with 100ms inter-request delays. Deploy-skew 404 fallback to per-item calls ensures backward compatibility during rollout.Test plan
tsc --noEmitpasses (verified)getHydratedDatareadcurl -X POST api.worldmonitor.app/api/conflict/v1/get-humanitarian-summary-batch -d '{"countryCodes":["AF","IQ","SY"]}'curl -X POST api.worldmonitor.app/api/economic/v1/get-fred-series-batch -d '{"seriesIds":["WALCL","FEDFUNDS","T10Y2Y"],"limit":10}'