Capacity Estimation:
The format in which the tags are to be stored is UTF-8. The character size is as follows:
English letters (a, b, c) | 1 byte |
Accented (é, ñ) | 2 bytes |
Some symbols (₹, €) | 3 bytes |
Emojis (😀, 🔥) | 4 bytes |
We can have a comment of max length 300 letters. That would mean if a comment has all emojis for 300 letters then 300 * 4 Bytes = 1200 Bytes around ~1.2 KB per comment.
So if we have 10 million comments per day that would be 10 million * 1.2 KB it would be roughly around ~12GB per day.
That would mean 12 GB * 30 days 360 GB per month and 360 GB * 12 months makes it around ~4.3 TB per year.
Since we are assuming 10 million comments per day that would be 10 million / 86400 seconds would be around ~116 requests/sec
For API we have to consider the following actions that can be performed per comment:
Create Comment
Update Comment
Delete Comment
Upvote a comment
Downvote a comment
Reply to a comment.
POST /api/v1/CreateComment
Request Body:
Comment: Text the user inputs to comment
Response 201 Created.
CommentID
Comment
Upvote
Downvote
CreatedAt
UpdatedAt
422 Unprocessable Entity if comment longer than 300 words.
PUT /api/v1/UpdateComment
Request Body:
CommentID
Comment
Response 200 OK
CommentID
Comment
Upvote
Downvote
UpdatedAt
400 Bad Request
DELETE /api/v1/DeleteComment
Request Body:
CommentID
Response 204 No Conent
404 Not Found
PUT /api/v1/Upvote
Request Body:
CommentID
Response 200 OK
404 Not Found
400 Bad Request
PUT /api/v1/Downvote
Request Body:
CommentID
Response 200 OK
404 Not Found
400 Bad Request
POST /api/v1/Reply
Request Body:
CommentID
Comment
Response 201 Created
404 Not Found
400 Bad Request
Database Design
We will be going with Postgres for our DB of choice. This is because the comments are going to have one to many relationship meaning one comment can have more than one nested comment to it.
We will be having the following schema:
Comment:
CommentID: Id of the comment. Primary Key
Comment: The actual comment. Cannot be empty
Upvote: upvote count. default to 0
DownVote: downvote count. Default to 0.
ParentID: id of the comment its nested in. If not nested it will be null.
CreatedAt: ISO 8601 timestamp for when the comment was created
UpdatedAt: ISO 8601 timestamp for when the tag was updated.
We can create an index using commentID + CreatedAt. Using this index we can make the queries efficient.
We can use CDN for hosting our frontend which will also cache the reads making read operations give subsecond responses < 20ms
API Gateway is used to route the requests to respective api endpoints keeping this balanced and this can also help in rate limiting the requests which prevents DDos and Brute Force attack.
From API Gateway we will go to load balancer which will balance out the load and keeps sending traffic to services in a uniform way.
We have a comment service which deals with creation, updation and deletion of comments. We use vote service to deal with upvote and downvote.
The writes work in the following way. Whenever a write request comes. The comment service and vote service do they are supposed to and send a notification to the kafka message queue which will send the data to both postgres and Redis at the sametime.
By doing this we are keeping the cache upto date since when a comment is created user expects it to appear on the screen so its a read action having this in redis makes the response subsecond keeping things efficient.
We can load the comments using the infinite loading UI-driven lazy loading along with pagination in the backend to keep the retrieval limited to what is required.
We can have a notification service separately running which would send out notifications whenever there is a reply to the comments so that user can know he got a reply.
Since we have kafka queue even if the service goes down it can pick up where it left out due to having kafka queues.
API Gateway: Helps in routing the api hits as they come smoothly and can also rate limit the requests making the app resilient against DDoS attachs and brute force attacks.
The postgres DB is used to store our data. It supports one to many relationship which is required in this usecase. Since joins are possible we can do complex queries to get our results. It also scales for the throughput that is required to write to the DB as well. The only trade off is the read which might get hindered a bit if the query is complex. we can use self joins for getting nested comments.
Kafka Queue has been used to simultaneously fillout postgres db and the redis cache. The pro is that it is efficient and the trade off is the complexity kafka brings to the design.
We will be using lazy loading especially in frontend we will use infinite scrolling method and in the backend we will be using pagination to efficiently retrieve only what is needed.