System Design (High-Level Design) Module 25 – Case studies: location, mobility and logistics
Design activity tracking like Strava: GPS ingestion
Design GPS activity uploads like Strava: requirements, estimates and a first design for repeat-safe uploads, clean tracks, streams and home privacy.
What you will learn
- Estimate peak uploads, yearly file and stream storage and feed map bandwidth for an activity tracker
- Design an upload path for watches, bike computers and phones that never saves a repeated upload twice
- Compute distance, moving time and climb from noisy GPS samples, and check them against a known route
- Store activities as streams at several resolutions with a compact encoded line for maps
- Protect home locations against an attacker who studies many public activities
Before you start
What you’ll learn · Before you start
What you will learn
- Estimate peak uploads, yearly file and stream storage and feed map bandwidth for an activity tracker
- Design an upload path for watches, bike computers and phones that never saves a repeated upload twice
- Compute distance, moving time and climb from noisy GPS samples, and check them against a known route
- Store activities as streams at several resolutions with a compact encoded line for maps
- Protect home locations against an attacker who studies many public activities
Before you start
On this page
On a busy Sunday an activity tracker takes in about 670 uploads a second, each a run or ride from a watch, a bike computer or a phone, and keeps about 570 TB of new files and samples a year. Every upload must become a saved workout within seconds: a map, distance, moving time, climb and charts for followers.
The design rests on four ideas. Devices upload the finished file, which the service stores unchanged and processes later from a queue, so a retry or a second copy of the same run is recognised instead of saved twice. Processing is a pipeline over a time series: drop impossible jumps, smooth the track, then derive the statistics. Results are stored as columns of samples at several resolutions, with a short encoded line for each feed card’s map. And privacy is applied on the server before anything is shared, because a hidden start that is always the same circle still points at a home.
Not affiliated
Based on public information about Strava. MySmartCoPilot is not affiliated with Strava, and Strava’s real architecture may differ from what this lesson describes.
About 670 uploads a second, and 570 TB a year to keep
Settle these first, with the answers this design assumes:
- Which devices? Watches and bike computers upload files; phone apps record and upload themselves. Strava’s API accepts FIT, TCX and GPX files, gzipped or not, and processes each upload asynchronously (Strava).
- How big? 10 million uploads a day; a Sunday brings twice that, 12 % of it in the busiest hour.
- How fast? A new activity appears within seconds; Strava’s API documentation gives a mean processing time under 2 seconds.
- Live sharing? A recording can share its position with chosen contacts; Strava Beacon updates about every 15 seconds and needs mobile signal (Strava).
- Privacy? Each activity has a visibility, and starts and ends near home are hidden; Strava hides the first and last 200 m of new activity maps by default (Strava).
So the service accepts uploads that may repeat, computes the statistics, keeps the original so that activities can be recomputed when the algorithms improve, serves maps and charts, and never shows a hidden part to anyone but the owner. Uploads may arrive late and in bursts after a race; nothing may be lost once accepted.
| Quantity | Working | Result |
|---|---|---|
| Uploads, busiest hour | 10 million × 2 × 12 % ÷ 3,600 s | about 670 a second |
| Original files | 10 million a day × 100 KB × 365 | about 365 TB a year |
| Streams | 3,600 samples × 12 bytes × 1.3 for two smaller resolutions | about 56 KB an activity, 205 TB a year |
| Feed map thumbnails | 300 million card views a day × 20 KB × 8 bits ÷ 86,400 s × 3 | about 1.7 Gbit/s at the peak |
| Live positions | 200,000 people sharing, one position every 15 s | about 13,000 writes a second |
Storage, not traffic, is the big number: about 570 TB a year that is read rarely and must never be lost. The upload rate itself is modest, but each upload starts several seconds of processing.
Exercise · Easy · Python
Size peak uploads, yearly file and stream storage, and feed map bandwidth
Write four functions in sizing.py for the estimate sheet of an activity-tracking service. An hour has 3,600 seconds, a day 86,400, a year 365 days; 1 KB is 1,000 bytes, 1 TB is 10¹² bytes and 1 Gbit is 10⁹ bits.
peak_uploads_per_second(daily_uploads, busiest_day_factor, busiest_hour_share): uploads a second in the busiest hour of the busiest day, which hasbusiest_day_factortimes the usual day's uploads and putsbusiest_hour_shareof them into one hour.yearly_file_storage_tb(daily_uploads, kb_per_file): a year of original files, kept as uploaded.yearly_stream_storage_tb(daily_uploads, samples_per_activity, bytes_per_sample, resolutions_factor): a year of processed streams, where the smaller resolutions addresolutions_factor - 1to the full one.thumbnail_gbit_per_second(daily_views, kb_per_image, peak_factor): the bandwidth for map thumbnails in the feed at the busiest hour, which runs atpeak_factortimes the day's average.
The sample tests accept each figure within 10 %, as an estimate should.
Starter code · sizing.py
HOUR_SECONDS = 3_600
DAY_SECONDS = 86_400
def peak_uploads_per_second(daily_uploads, busiest_day_factor, busiest_hour_share):
"""Uploads a second in the busiest hour of the busiest day."""
# Replace this line with your code.
return 0
def yearly_file_storage_tb(daily_uploads, kb_per_file):
"""TB for a year of original files."""
# Replace this line with your code.
return 0
def yearly_stream_storage_tb(daily_uploads, samples_per_activity, bytes_per_sample, resolutions_factor):
"""TB for a year of processed streams at every resolution."""
# Replace this line with your code.
return 0
def thumbnail_gbit_per_second(daily_views, kb_per_image, peak_factor):
"""Gbit/s for feed map thumbnails at the busiest hour."""
# Replace this line with your code.
return 0 The sample tests · test_sizing.py
import math
from sizing import peak_uploads_per_second, thumbnail_gbit_per_second, yearly_file_storage_tb, yearly_stream_storage_tb
def near(actual, expected):
"""Within 10%, as an estimate should be."""
return math.isclose(actual, expected, rel_tol=0.10)
def test_peak_uploads():
"""the busiest day's uploads, the busiest hour's share of them, per second"""
assert near(peak_uploads_per_second(10_000_000, 2, 0.12), 667)
assert near(peak_uploads_per_second(1_400_000, 1.5, 0.1), 58)
def test_file_storage():
"""a year of files at their uploaded size"""
assert near(yearly_file_storage_tb(10_000_000, 100), 365)
assert near(yearly_file_storage_tb(2_000_000, 60), 43.8)
def test_stream_storage():
"""samples times bytes, plus the smaller resolutions"""
assert near(yearly_stream_storage_tb(10_000_000, 3_600, 12, 1.3), 205)
assert near(yearly_stream_storage_tb(1_000_000, 1_800, 10, 1.2), 7.9)
def test_thumbnail_bandwidth():
"""the day's image bits per second, at the peak"""
assert near(thumbnail_gbit_per_second(300_000_000, 20, 3), 1.67)
assert near(thumbnail_gbit_per_second(50_000_000, 30, 2.5), 0.35) A hint
Peak uploads: daily_uploads * busiest_day_factor * busiest_hour_share / 3_600. Storage multiplies the activities of a year by their bytes and divides by 10**12. Bandwidth spreads the day's bits over 86,400 seconds, multiplies by the peak factor and divides by 10**9.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Checkpoint
Check the estimate
The first design: store first, process from a queue
The first complete design of activity ingestion
Text description of the diagram
The diagram shows the upload path and the live path.
- A watch, bike computer or phone app sends the finished recording to the upload API, which checks whether it repeats an earlier upload.
- The upload API saves the original file, unchanged, in the file store first, then puts an upload id on the processing queue.
- Processing workers take uploads from the queue, parse and clean the track, derive the statistics and hide the private parts.
- The workers write the activity's statistics and visibility, its streams at several resolutions, and an activity event.
- Activity events feed the consumers that build the feed, match segments and draw map thumbnails.
- Separately, a phone that shares its position live sends a position about every 15 seconds to the live sharing service.
The device sends the finished file to the upload API, which checks for repeats, saves the original in the file store and queues an upload id. Workers parse, clean and measure the track, hide the private parts, and write the activity’s statistics, its streams and an event that the feed, segment matching and map thumbnails consume. Live positions take a short path of their own. Follow one run through it:
The drawing shows the whole scene; its 8 steps are listed below.
Every step as text
- A finished run. A watch finishes a run and uploads the whole file. The upload API first checks whether this upload repeats an earlier one.
- Saved first. The API stores the original, unchanged, in the file store before anything else, so nothing is lost once the upload is accepted.
- Queued. Then it puts an upload id on the processing queue and answers the device, which can show that the run is being processed.
- Processed. A worker takes the id from the queue, parses and cleans the track, derives the statistics and hides the private parts.
- Written. The worker writes the activity's statistics and visibility, its streams at several resolutions and an activity event.
- Read by others. The feed, segment matching and map thumbnails read the activity event, each on its own schedule.
- A retry. When an upload times out, the device sends the same file again. The API's check for repeats keeps it from becoming a second activity.
- A live position. A phone that shares its position while recording sends it about every 15 seconds on a short path of its own; the finished file still comes later as an upload.
The questions this design has to answer:
- Repeat uploads: a retry sends the same bytes, a re-export different bytes, and a second device a second recording of the same run.
- Cleaning a track: drop fixes that imply an impossible speed, then smooth what remains before measuring.
- Moving time: pauses recorded by the device win; otherwise a speed threshold decides when the athlete rested.
- Climb: a barometer’s readings, smoothed, or a terrain model; raw GPS altitude counts every wobble as climbing.
- Streams and polylines: columns of samples at several resolutions, and an encoded line of points for maps.
- Privacy zones: hide the start and end, with a hidden distance that varies, so repeated runs do not outline a home.
- Reprocessing: keep every original and compute old activities again as a background job.
Checkpoint
Check the first design
A content hash catches only retries; overlapping times catch a second device Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Dropping impossible jumps and smoothing within runs turns 14.15 km into 10.09
- Climb from a barometer, not from GPS altitude
- Streams as columns at three resolutions, and an encoded line for the feed
- A fixed hidden circle gives a home away to within a metre
- Reprocessing 5 billion activities takes about 96 cores for a month
- Live positions every 15 seconds take a short path of their own
- What gives way at 10× and 100×
- Failure drills: nothing accepted is ever lost
- Alternatives considered and rejected
- What each level is expected to cover
- A sample answer and a 45-minute self-mock
- Practise: the whole design, one question at a time
- Interview questions
- An athlete records a morning run on a watch and on a phone app at the same time, and both upload. Another athlete's app retries one upload three times after timeouts. Design how the service recognises each case and what it does with each.
- Runners in a city centre report that their 10 km runs show as 11 to 14 km, while runs in parks are right. What is happening, and how do you fix the distance without breaking the parks?
- A security researcher shows that, from a user's public runs, they can find the user's home to within a few metres, although the user hid 200 m around it. Explain how, and redesign the privacy feature.
- Key takeaways
3 diagrams · 5 runnable examples (Python) · 3 interview answers · about 54 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Read a complete sample first: Design ride hailing like Uber or Ola: matching
References
- Uploads (Strava API documentation) (Strava)
- GPX 1.1 Schema Documentation (TopoGrafix)
- Flexible and Interoperable Data Transfer (FIT) SDK (Garmin)
- GPS Accuracy (U.S. government (GPS.gov))
- Encoded Polyline Algorithm Format (Google Maps Platform)
- Moving Time, Speed, and Pace Calculations (Strava)
- Strava Beacon (Strava)
- How do I edit my map visibility on Strava? (Strava)
- Analysis of Privacy Protections in Fitness Tracking Social Networks, or You can run, but can you hide? (USENIX Security Symposium (Hassan, Hussain, Bates))
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress