User visits facebook.com. Gets home feed.
User follows another user.
User follows a page (specific topic).
On user's newsfeed, user will see:
Response time. After user visits the page, newsfeed should appear within 0.5 second.
Client can be a browser or a mobile app.
Availability.
Scalability. Increasing data e.g. videos and images.
We can relax a little bit on consistency. E.g. if one user sees a post at time 0, but another one sees the same post seconds later, that'd be acceptable.
Key Points:
System receiving huge amount of media files (videos and images), while maintaining short response time for users.
DAU: 500M users
Each user accesses homefeed twice a day
Each user posts a status update, image or video once a day.
Data:
Video: 10MB
Image: 1MB
Status update: 1KB
500M / 3 * 10MB = 1.7PB / day
Key observations on data:
For relationship, write path is follows APIs.
User profile - 2KB
2B users.
4TB data
Document based NoSQL, e.g., MongoDB, would be a good choice. It has configurable consistency models. Compared to RDB, it would allow us to be more horizontally more scalable. It would be possible to trade ACID consistency for scalability and performance.
RESTful API.
follow_user(follower_user_ID, followed_user_ID)
follow_page(follower_user_ID, followed_page_ID)
get_newdfeed(user_ID)
upload(user_ID, media_content)
post_update(user_ID, update_content)
Essential parts of data models:
User:
Post:
Page:
# this is a join table which represents the posts made by user
User_Post:
# this is a join table which represents the posts made on a page
Page_Post:
Newsfeed Service will use the tables above to create feed. For example,
select Post_ID from User_Post where User_ID in (N users user is following) and timestamp (last 24 hours)
and aggregate the posts with some heuristics, e.g.,
score(post) = weight1 * recency + weight2 * popularity_of_followed_user + weight3 * number_of_likes_on_post
We would store some of this calculation in cache, so that Newsfeed Service does not have to do this query and calculation on every request.
Tracking recent posts by a user
{
User_ID: ,
Posts: [
Type: text/image/video,
Text: text content of status update,
URL: URL pointing to the media file
]
}
Media will be a parent URL. E.g. "https://sth.facebook.com/media/0123". Media Service will use this as a key to look up child URLs that contain media in different format and quality, e.g., http://sth.facebook.com/media/0123_480x320_mpeg or something like that.
Write flow:
Read flow:
{
user_ID,
timestamp: ,
[
type: text/image/video,
text: text content
URL: links to image/video
]
}
Let's discuss Newsfeed Service more deeply.
The job of Newsfeed Service is to generate a list of posts catered for each user. Something like in JSON:
[
{
post_ID: 123,
textContent: ...,
mediaURLs: [],
comments: [],
]
and so on. The list would be sized at least 5 and can be dynamically generated to be larger size, as user scrolls their newsfeed downward to see more posts.
The top K posts are chosen by a scoring function, e.g.,:
score() = weight_1 * popularity_of_author + weight_2 * number_of_likes_on_post + weight_3 * number_of_sharing_on_post + weight_4 * number_of_comments_on_posts
An interesting tradeoff is when to compute this list of recommendations (discussed in Detailed component design section).
In one extreme, it gets generated when a user accesses the Newsfeed (lazy evaluation). This is efficient in terms of utilizing server side resources. However, it might take too long (e.g., more than a couple of seconds) and the user might get frustrated.
In another extreme, we can pre-compute this list periodically (e.g. every 5 min) for every user and cache the result. This gives a responsive user experience, but it requires huge amount of resources.
We can strike the balance between these two by having a category of users. For example, if a user is actively engaged with Newsfeed, it would make sense to refresh this user's list often. It would make sense to take real time update from followed users or famous people, using pub/sub messaging system.
But if a user is dormant - has not checked Newsfeed for months - then it would be fine not to calculate Newsfeed for these users. On-demand calculation would take some time, but the user would likely understand and accept. For the business, these are not very important users to take care of.
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?