How We Serve a Global Weather Model on a Budget
How We Serve a Global Weather Model on a Budget
Forge processes 50,000 weather requests per day. Each one needs sub-100ms latency and geographic accuracy within 5 kilometers. We could solve this by calling a raw weather model API for every request, but that's expensive and slow. Instead, we built a smart caching and interpolation system. Here's how.
The Naive Approach (And Why It Fails)
If we queried our underlying weather data provider for every Forge request, costs would be prohibitive. A free-tier user making 100 requests per day would cost us more than they paid. We needed a different model, one that amortizes the cost of data fetches across many user requests.
Grid-Based Storage
Weather data doesn't change smoothly—it has discontinuities at fronts and edges. But over small distances, it's relatively uniform. We discretize Earth's surface into a regular 10-kilometer grid and cache weather predictions at grid points. For the continental United States and Europe, we cache every 4 hours. For less-trafficked regions, every 8 hours.
Grid points are stored in a columnar database (we use DuckDB for this). Each column is a variable: temperature, humidity, wind speed, etc. Each row is a time-location pair. This structure makes time-series queries fast and enables efficient compression.
Interpolation on the Fly
When a user queries a specific latitude and longitude, we:
- Find the four nearest grid points (forming a square around the coordinate)
- Perform bilinear interpolation between those points
- Return the interpolated result
Bilinear interpolation is cheap—a few floating-point operations per request. It gives us the illusion of having weather data at infinite resolution without storing it. For a request at (51.515, -0.131), we interpolate from grid points at (51.51, -0.13), (51.51, -0.14), (51.52, -0.13), and (51.52, -0.14).
For time-domain interpolation (e.g., requesting weather for 3:15 when we only have data for 3:00 and 4:00), we use linear interpolation. Temperature and humidity change smoothly over time, so this works well.
Smart Caching
Our three-tier cache strategy:
- L1: In-process (Rust server): Each server keeps a 5-minute sliding window of the most-requested grid cells in memory. Hit rate: 73%.
- L2: Redis: All grid cells for the past 12 hours, with TTL. Hit rate: 18%.
- L3: DuckDB: Full historical grid for 30 days. This is our source of truth but slower. Hit rate: 9%.
Most requests never leave memory. A request that misses L1 but hits L2 takes 8ms. A cache miss to L3 takes 35ms but happens infrequently.
Grid Generation Pipeline
Every 4 hours, our ingestion pipeline:
- Fetches raw data from upstream weather models (NOAA, ECMWF)
- Resamples it onto our 10km grid
- Computes quality metrics (confidence, source)
- Writes to DuckDB
- Notifies all edge servers to invalidate their L1 cache
This whole process takes 90 seconds for a global dataset, running on two c6i.4xlarge instances.
Handling Sparse Coverage
Antarctica and the Pacific Ocean have less weather data. We handle sparse regions by increasing grid spacing there. Antarctica uses a 50km grid instead of 10km. We also blend satellite data with model predictions to fill gaps.
Accuracy Trade-offs
How accurate is our interpolation? We validate quarterly using real-world weather station observations. Our median error is 1.8 degrees Celsius for temperature, 7% for humidity. The trade-off is worth it—users get instant responses, and our costs stay reasonable.
Cost Breakdown
For 50,000 weather requests per day:
- Upstream data costs: $3,200/month
- Compute (ingestion pipeline): $200/month
- Storage (DuckDB): $150/month
- Redis cache: $100/month
- Total: $3,650/month for 1.5M requests
That's $0.0024 per request, with our profit margin built in. It works because interpolation and caching eliminate redundant data fetches.
Future Work
We're experimenting with machine learning to improve sparse-region predictions and with finer-grained spatial resolution in high-traffic areas. We're also exploring different interpolation methods for wind direction (which has circular boundary conditions) versus temperature.
Serving global data at scale is a hard problem. We're proud that Forge does it without requiring users to think about any of this complexity.