System requirements


Functional:

List functional requirements for the system (Ask the chat bot for hints if stuck.)...



Non-Functional:

List non-functional requirements for the system...





Capacity estimation

QPS read : 50k

QPS write : 30k

Extra QPS for spiky traffic : 10k


size of one record = 40 bytes

TTL = 2 years


Total capacity for 2 years = 40 * 30000 * 3600 * 24 * 365 = 40 Tb


Instance size estimation


QPS : 90k

2k per cluster

45 clusters

45*3*6 cores


Replication : cross dc replication


Consistency : we need local read after write consistency so we can use regional read after write winds strategy. This is eventually consistent and not strongly consistent


Hot shards : we will need to monitor load distribution at shard and parition level. If there is uneven load distribution we may benefit from caching the most frequent urls. There are disadvantages to caching too and we will need to monitor cache hit ratio.



API design

UpsertUser'sActivity(

entity_uuid uuid.UUID (required),

event_type string (required),

event_timestamp datetime,

tweetID int (required)

)(error)


GetUser'sActivity(

entity_uuid uuid.UUID (required)

event_type (optional)

) (activites, error)


Database design

schema


CREATE TABLE user_activity(

entity_uuid uuid.UUID (8),

event_type string (8),

tweetID long long int (8),

timestamp DateTime (8)

) ((entity_uuid), event_type, timestamp)

local indexed on event_type and timestamp

local indexed on timestamp


Max no of rows per partition : 10 no of events * 365 * 2 < 10k within the db limit


Redis cache :


The key will be sessionID and the value will be the timeline feed


High-level design


Clients interact with presentation service


presentation service talks to authentication service,

friends service and userActivity service which is the closest to the database layer


presentatation service has

  • addUser
  • addFriend
  • addTweet
  • addLike
  • RetrieveTimeline


userActivity service has 2 endpoints that read and write data to the db


For retrieve timeline we cache the timeline in a redis cache.



Request flows




Detailed component design


For the database :


our choice of schema limits the max no of rows thats y it was choosen but for a single timeline request we make 10 parallel calls to the db. We will add a cacheFront and monitor the cacheHit ratio.


For the cache :


we use redis cache with cross dc replication and lazy fallback and a TTL for 1 minute. Need to talk about failure scenarios :

TTL time is short or 1 minute so that timeline always hits the backend after a minute and we can show real-time updates


In case of redis down or failover happening or non-replicated data : we need to query db in case of redis miss. For stale data worst case is that we serve an outdated timeline



Trade offs/Tech choices

For schema : We can choose to compact the schema and store list of friends for every user. Then we have to make a single read call to database for every timeline request. The issue is that the individual size of row will exceed 1 MB and it is not a good practice


For redis : We can also use redis and cache the most active users data so that we do not have to hit the database. I chose to use cacheFront for this purpose to have a cleaner design, easier database integration, one less component and making overall design more scalable but we compromise on latency as we are making 2 extra calls. One to userActivity and another to the cacheFront. We can also choose to do both.


Failure scenarios/bottlenecks

If any of the components is down, presentation service or userActivity service or database then we cannot comply


In case of sudden bursts of traffic we will not be serving all requests and will rate-limit at the service level so that database is not affected


Future improvements

We can think more on reducing the read latency

we can handle spikes in traffic better