Skip to content

fix: infrastructure cost optimizations across caching, polling, and batching#273

Closed
elzalem wants to merge 6 commits into
koala73:mainfrom
elzalem:fix/infra-cost-optimizations
Closed

fix: infrastructure cost optimizations across caching, polling, and batching#273
elzalem wants to merge 6 commits into
koala73:mainfrom
elzalem:fix/infra-cost-optimizations

Conversation

@elzalem

@elzalem elzalem commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Five targeted optimizations to reduce infrastructure costs as the app scales. Each change is a separate atomic commit.

1. Request coalescing on cache misses

When multiple concurrent requests hit the same cache key during a miss, only the first triggers the upstream API call — the rest await the same in-flight promise. Eliminates duplicate work under burst traffic.

2. AIS maritime polling: 10s → 30s

Vessel positions do not change meaningfully in 10 seconds at sea. Stale threshold bumped to 45s to match.

3. Military flights bbox cache quantization

Previously, every map pan/zoom produced a unique bounding box → unique cache key → cache miss → fresh OpenSky API call. Now coordinates snap to a 1-degree grid (~111km), so nearby viewports share cache entries. Upstream fetch bbox is expanded to cover the full grid cell.

4. ETF flows: sequential → parallel fetch

The existing code wrapped each ETF fetch in a single-element Promise.allSettled inside a for/await loop — effectively sequential. Now all 10 ETFs queue concurrently through the Yahoo rate-limit gate.

5. Redis pipeline batch GET

Added getCachedJsonBatch() using the Upstash pipeline API to fetch N keys in a single HTTP round-trip. Refactored the aircraft details batch handler from 20 sequential Redis GETs to 1 pipelined request.


Estimated impact

At 500K monthly active users, assuming 1% have the maritime panel open at any given time (5,000 concurrent users):

Area Before After Reduction
Railway relay requests ~5,000 req every 10s ~5,000 req every 30s 66%
OpenSky API calls (military) ~1 per pan/zoom per user ~1 per grid cell per 2min ~90%
ETF endpoint wall time ~12s (sequential) ~6s (parallel) ~50%
Redis calls (aircraft batch) 20 per request 1 per request 95%
Duplicate upstream calls (burst) N concurrent misses = N fetches N concurrent misses = 1 fetch up to 90%

Files changed (5 files, +109 / -22)

  • server/_shared/redis.tscachedFetchJson() coalescing + getCachedJsonBatch() pipeline
  • src/services/maritime/index.ts — poll interval 10s → 30s
  • server/worldmonitor/military/v1/list-military-flights.ts — bbox grid quantization
  • server/worldmonitor/market/v1/list-etf-flows.ts — parallel Promise.allSettled
  • server/worldmonitor/military/v1/get-aircraft-details-batch.ts — pipeline batch GET

Test plan

  • Verify maritime panel still receives AIS updates (slightly less frequent)
  • Verify military flights layer shows aircraft when panning/zooming
  • Verify ETF flows panel loads correctly
  • Verify aircraft detail tooltips still load on hover
  • Confirm no TypeScript errors via tsc --noEmit

🤖 Generated with Claude Code

elzalem and others added 5 commits February 23, 2026 21:01
Concurrent cache misses for the same key now share a single upstream
fetch instead of each triggering redundant API calls. This eliminates
duplicate work within Edge Function invocations under burst traffic.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Vessel positions do not change meaningfully in 10 seconds at sea.
Reduces Railway relay requests by 66% with negligible UX impact.
Stale threshold bumped to 45s to match the new interval.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Precise bounding box coordinates caused near-zero cache hit rate since
every map pan/zoom produced a unique key. Snapping to a 1-degree grid
lets nearby viewports share cache entries, dramatically reducing
redundant OpenSky API calls.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The loop awaited each ETF chart fetch individually, blocking on every
Yahoo gate delay. Using Promise.allSettled lets all 10 fetches queue
concurrently through the Yahoo gate, cutting wall time from ~12s to ~6s.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add getCachedJsonBatch() using the Upstash pipeline API to fetch
multiple keys in a single HTTP call. Refactor aircraft details batch
handler from 20 sequential GETs to 1 pipelined request.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@vercel

vercel Bot commented Feb 23, 2026

Copy link
Copy Markdown

@elzalem is attempting to deploy a commit to the Elie Team on Vercel.

A member of the Team first needs to authorize it.

@koala73

koala73 commented Feb 23, 2026

Copy link
Copy Markdown
Owner

Thank you @elzalem
Much appreciated

Will review

18 tests covering: cachedFetchJson request coalescing (in-flight dedup,
cache-before-fetch ordering, cleanup), getCachedJsonBatch pipeline API,
aircraft batch handler pipeline usage, bbox grid quantization (1-degree
step, expanded fetch bbox), and ETF parallel fetch.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@koala73

koala73 commented Feb 23, 2026

Copy link
Copy Markdown
Owner

Merged under #284

@koala73 koala73 closed this Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants