Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
Here are some db schema definitions:
workout history db:
consumer_id: int,
workout_id: int,
workout_type: string,
workout_duration: int,
workout_calories: int,
workout_start_time: timestamp,
workout_end_time: timestamp,
workout_metadata: jsonb
There are 2 indexes built for this db. 1 is for just consumer_id, second is for consumer_id + workout_id
consumer db:
consumer_id: int, (index)
age: int,
height: int,
weight: int,
device_ids: list
device db:
consumer_id: int,
device_id: int,
device_type: string
Now here are the APIs:
GET v1/workout_history {
consumer_id: int
}
This API is used to get the workout history & basic stats for each workout for a given consumer.
GET v1/workout_details {
consumer_id: int,
workout_id: int,
}
This API is used to get the details of a specific workout.
POST v1/workout_start {
consumer_id: int,
workout_type: string,
workout_start_time: timestamp,
}
POST v1/workout_end {
consumer_id: int,
workout_type: string,
workout_end_time: timestamp,
}
These 2 APIs are used by consumers to log workouts.
POST v1/sync_to_device {
consumer_id: int,
workout_id: int
}
This API is used to sync the latest workout data to all devices of
a given consumer.
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
How do users log workout?
When a user logs the start and end of a workout, the API calls of workout_start and workout_end are made separately to the workout service. For the caching layer, we use a read-through cache, so reads are from the cache, but writes are written simultaneously to cache and database.
How do users view workout history and details?
When users request to view workout history and details, we send a request to workout service, which reads the metadata of the workouts requested from the cache, and sends back to users.
What happens when users finish workout? How do we sync the data to other devices?
When a user finishes workout, as soon as they are online, we send the latest workout data to a message queue, to be consumed by the notification service. We fetch all of their registered devices from the cache, and we use notification service which triggers different protocols based on devices, to update their devices to fetch latest data.
How do we prevent duplicate data being logged? What if requests to sync data fail?
When each workout finishes, we use consumer_id, workout_type, start_time and end_time to generate a unique workout_id as the unique identifier for the current workout. So when duplicate requests are sent and process, it should be idempotent given we identify the workout as the same workout based on workout_id. If a request fails, we do retries on the message queue.
How do we handle spike in traffic?
First of all, we have load balancer that distributes traffic to different servers.
For bot traffic, we have authentication, circuit breaker and rate limiter implemented at the API gateway level.
For data storage, we use redis as the caching layer, given it can be easily scaled horizontally. We use cassandra as the database, given noSQL databases can be scaled easily to handle heavy write traffic.
For syncing between devices, kafka message queues can be scaled easily as well.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
How will we handle out-of-order or noisy GPS data?
When each workout finishes, we use consumer_id, workout_type, start_time and end_time to generate a unique workout_id as the unique identifier for the current workout.
If requests come out-of-order, on the workout service level, we can build a buffer to store requests for a certain period of time, and then trigger the update events to users' devices when we receive all the recent requests. We can sort them with timestamps first before pushing the updates to users devices. When users became offline and later come online, we can also use the timestamps to sort the workouts to preserve the correct sequence.
How will reduce data size?
We will use compact encoding methods like base64 to reduce the request size. This will help with lower latency during transmission. We will decode the data before finally writing to database.
How will we handle offline storage and merging when device reconnects?
When devices become offline, we store the events data temporarily in users devices. As soon as devices become online, we send the requests to workout service. Workout service maintains a temporary buffer when devices become back online, to process and sort all the requests based on their timestamps, and then trigger kafka queue updates. As soon as the requests are done being processed, we remove the data stored in users' local device.