Scalable: The whole system can be scaled up smoothly to handle more request.
Security: Prevent ddos. include WAF.
Consistence: Eventually consistency. View data from different devices, eventually same. Make sure there isn't duplicated write. All the records should be stored.
Availability: High Availability. User can always fetch the data without any down time.
Latency: get request should be P99: 100ms, P80: 200ms.
Peak: write request: 100k qps. Get request 1M qps. 10 Million active users
API Design
Write workout log:
Post API /log/create?userId=string CreateWorkOutPlan(startDate: Int, EndDate: Int, Log: List<String>): true
Patch API /log/userId: Update (startDate: int, EndDate: int, Log: List<String>);
View workout log:
GET API /log/userId: Get(StartDate: int, EndData: int): Json Map: <date: List<String>>
View Stats:
Get api/stats/weeklyCheckin?userId=xxx: get(): int. Return the days user weekly check in the workout.
High-Level Design
Client: all type of device to send rest api.
CDN: I'd assume most of request is get request. To review the workout logs or stats. These can be cached at CDN. I also don't think workout log will be updated quit often. A user updates the log most likely will be per week or month. This also prevent the DDOS. CDN can detect bot request.
Api gateWay: This can use as rate limiter, also the auth for each api token. Then forward the quests to the right server.
LB. When the peek comes, it distributes the request to different replicate.
We have 3 big servers if needs.
Log write manage server
Log read manage server
Status read server.
Read servers have the origin caches which stores the cdn(cache miss) records
We have a kafka in place when a workout log status has been change, the write service will send a topic to kafka. Each aggregation consumer can consume the message and do the aggregation and calculation.
10 million active users. One DB per region, should be enough.
Detailed Component Design
Log DB -- this can be a relationDB.
User table : userId(uuid), gender, name, age, body type, weight, high.
Index log table, userId, logId. start date, end date, status.
Stats DB -- nosql (key-value)
User Id, year-week(string). total. key is userId-year-week
Cache:
CDN: I don't think user would update the workout log that often. Max once a day. So we can set the ttl is 24 hours, and stale-revalidation is 30s & we update the record when there is a write request comes. Most user would just read today's workout log. So the CDN, just store the today's ttl.
Read log cache: it can be ttl 1 min cache. If the cache doesn't have the record. The read server would read from db and write to cache. If there isn't record return 500.
Read status cache: it can be ttl weekly cache. The status doesn't update as often requests.
Kafka: If there is a check in write server will post a top to kafka, and kafak will pass the event to the consumer worker and the worker will update the db, based on the week and then update the total time.
Failure scenarios/bottlenecks
If write server is down, or error. We can send the request to a retry worker and retry for 5 times.
The write server could have a high peek. We could adding a message queue.