Let's count we have 100 million of active users.
Every user work out 2 time a week for 1 hour.
User transmits such information every second about 100 bytes:
So it's 720kb per week for one user
72 terabytes for 1 week.
150 terabytes for 1 year
300 terabytes(one copy for replication) for 1 year or 1500 terabytes for 5 years.
startTrain(apiKey,location,timestamp) starts new training.It returns trainingId.
track(apiKey, trainingId, timestamp, user metrics) passes the user metrics to server.
finishTrain(apiKey, trainingId,timestamp) finishes the current training, it returns the final result of the workout.
getSummary(apiKey, startDate, endDate) returns the workout summary for the given time interval.
The primary data model for this system is three tablets: one table is for the users, the second is for workouts, and the third is for workout data.
It doesn't require strong consistency for users and workouts. We write the user and workout data to NOSQL like AWS Dynamo.
User table:
id(10 bytes)
first and last names (200 bytes)
phone number (50 bytes)
email (100 bytes)
createdAt (8 bytes)
Workout table:
id (10 bytes)
description(300 bytes)
userId (10 bytes)
startDate(8 bytes)
endDate (8 bytes)
Workout data:
id (8 bytes)
userId (8 bytes)
trainingId (8 bytes)
latitude: (2 bytes)
longitude (2 bytes)
date (8 bytes)
heartbeat (2 bytes)
elevation (8 bytes)
Goals table:
id (10 bytes)
userId (8 bytes)
date (8 bytes)
text (1000 bytes)
Analytics table:
id (10 bytes)
userId (8 bytes)
trainingId (8 bytes)
start date (8 bytes)
end date (8 bytes)
min speed (2 bytes)
max speed (2 bytes)
average speed (2 bytes)
distance (3 bytes)
The analytics table will be updated after the user finishes the workout.
It needs to create the indexes to search in DB faster.
See the diagram from the high-level architecture.
API Gateway provides DDoS protection and TLS termination, and forwards request to the right service nodes.
The user data service processes the request about the user and her workout data. This service stores the data in an RDBMS, such as MySQL or Postgres. The tracing service receives the workout data like the user speed, distance, heartbeats, and geolocation and stores that in NO SQL, which makes writing very quick. Then workout data are ingested by the Analytics service and uses MapReduce to get workout summaries and stores that in its own No SQL.
startTrain(apiKey, geolocation, timestamp) opens a new workout in the User data service and creates new records in workout tables, the user data service returns the new unique trainingId that would be used to access workout data.
track(apiKey, trainingId, timestamp, user metrics) sends the workout data to Tracking service. The tracking service persists those data: the user speed,geolocation, heartbeats, distance to No SQL. In this case, No SQL is the best solution to make writing. We may use AWS Dynamo to store that data.
finishTrain(apiKey, trainingId, timestamp) finalizes the current workout and updates endDate in the user data service DB.
The Analytics service requests the workout data from the tracking service to prepare different analytics report data for the user application dashboards.
The user may use the mobile or web client application to track the workout progress.
Performance and scalability of tracking endpoint is extremely important for this system. As such, we employ No SQL to scalable user load evenly. Also, we may Kubenates horizontal autoscaler to regulate node number. No SQL uses replication and partitioning to be fault-tolerant. Using indexes (traningId, timestamp) would help for Analytics service to process the workout data quickly.
startTrain and finishTrain would be rarely used comparing to track
getSummary would returns the workout summary for the given interval. Most frequent data would be cached in Redis.
Database and Cache should be partitioned for improved scalability by traningId. getSummary may use userId as the partition key.
It wouldn't give us even distribution but the system load is low and it doesn't hurt the system performance.
Explain any trade offs you have made and why you made certain tech choices...
Try to discuss as many failure scenarios/bottlenecks as possible.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?