The problem
"Design a weather service." The app shows the forecast and the weather right now for wherever you stand, a radar map, and warnings when a storm is coming.
This practice problem reuses the pattern of Design Google Maps: a map cut into tiles and sent from a CDN, here the radar map. What's new: forecasts computed in batches and then only read, lookups by position, the weather now from stations, and warnings pushed to an area. Park the weather archive and users' reports.
The numbers
From the interviewer: 100 million people check the weather about 10 times a day, in the app and its widgets, and the model runs 4 times a day. Round a day to 100,000 seconds (86,400 really); busy hours run about 5× the average.
- Lookups: 100 million × 10 = 1 billion a day, 10,000 a second on average (about 11,600 with 86,400), so about 50,000 a second at busy hours.
- Cells: say the land covers about 150 million km² and the model's cells are 10 km on a side, 100 km² each: 1.5 million cells.
- Forecasts: about 10 KB per cell (hourly for 2 days, daily for 10), so 1.5 million × 10 KB ≈ 15 GB: the whole grid fits in memory.
- Writes: 4 runs × 1.5 million = 6 million forecast writes a day against 1 billion lookups, about 170 reads for every write.
Cache keys: one entry per grid cell
A lookup carries the phone's exact position, and the API checks a cache first, as in Caching. Keyed by exact latitude and longitude, every key is new and every lookup misses. But the model forecasts each 10 km cell of a grid, so everyone in a cell gets the same answer. Snap the position to its grid cell and use the cell as the key: in one example minute, 900 lookups over nine cells need only 9 entries, and 891 of them hit, 99%.
lookups in one minute, per cell
20 60 10
150 500 40
10 30 80
The nearest city's name is a worse key: one city spans hills, coast and suburbs, and a farm may be far from any city. Better still, let the app snap first and ask for GET /forecast/cells/{id}, a URL a CDN can keep, like a map tile. The US National Weather Service's public API works in a similar way: a lookup by latitude and longitude first returns the grid cell, and forecasts are fetched per cell.
Filling the cache: every cell after each run
Forecasts change only when the model runs, so don't wait for misses. A forecast job takes each run's output and writes every cell's new forecast into the cache. Say a cache server takes about 100,000 writes a second: the 1.5 million cells are written in about 15 seconds, old entries are replaced in place, and no lookup misses.
Two tempting alternatives fail. Counting one cell per km² gives 150 million cells and makes writing ahead look impossible, but a 10 km cell covers 100 km². And filling on a miss with a 6-hour life serves old forecasts: an entry filled at 11:59 keeps the 6:00 forecast until 17:59. Entries filled together in the morning rush also expire together, and their misses hit the model's output all at once, a stampede (When Caches Go Wrong).
Freshness: forecasts, now and warnings
Each thing the app shows goes stale at its own pace:
- Until the next run: tomorrow's high and low, the hourly rain chance, the 10-day outlook.
- About 5 minutes: the temperature and wind right now, and the latest radar picture.
- Pushed at once: a tornado warning.
Say 100,000 stations each send a reading every 5 minutes: about 330 readings a second, kept as a time series per station, as in Metrics and Alerting. Every 5 minutes the job turns the latest readings into a small "now" entry per cell, about 200 bytes: 300 MB each time, instead of rewriting all 15 GB of forecasts. Each new radar picture's tiles get new names, as in CDNs, so cached tiles never need clearing.
Warnings: fan-out by area
A tornado warning arrives as a shape on the map, with about 2 million of our 100 million phones inside, and each must hear within a minute. Each phone tells the alert service its cell when it moves. The alert service finds the cells inside the shape, looks up their phones and queues a push for each, as in Notification System. Say the push workers send about 100,000 a second: all 2 million go out in about 20 seconds.
Pushing to every phone and letting the app decide sends 100 million pushes, 50 times too many, and the people in danger wait in line with everyone else: about 1,000 seconds, over 16 minutes. Letting apps ask every minute is about 1.7 million requests a second, nearly all answered "nothing", and phones limit how often a background app may run.
Same pattern as Google Maps: tiles and a CDN
The radar map is Google Maps' tiles: pictures cut into squares by zoom, x and y, the same file for everyone, sent from CDN edges near the users. The forecast grid is the same idea for data: a position snaps to a cell the way a map view snaps to tiles, so many people share one answer, cached in memory and on a CDN until the next run.
Summary
- Scope: forecasts, the weather now, radar, warnings; park the archive.
- Numbers: 50,000 lookups a second at busy hours; 1.5 million cells, about 15 GB.
- Cache keys: the grid cell, so neighbors share one entry.
- Filling the cache: a job writes every cell after each model run.
- Freshness: forecasts until the next run, "now" every 5 minutes.
- Warnings: the shape's cells, their phones, pushes off a queue.