Requirements, estimates and the graph model
The primer’s use case and estimates (5 billion friend-list entries, 400 searches per second), friend lists on sharded Person Servers, and TAO’s objects and associations.
What you will learn
- State the social graph search's use case and assumptions as the primer does.
- Estimate the size of the friend lists and the search rate.
- Store a graph too big for one machine without a graph database.
The problem
The primer's Design the data structures for a social network asks for one core feature: a user searches for someone and sees the shortest path to them through mutual friends. The service also needs high availability.
Assumptions (from the primer)
- Traffic is uneven: some searches are far more popular than others.
- Graph data won't fit on a single machine.
- Edges are unweighted.
- 100 million users, 50 friends each on average.
- 1 billion friend searches per month.
- Use traditional systems: no graph database such as Neo4j and no GraphQL, so the design itself has to handle the graph.
Back-of-the-envelope estimates
The primer counts each user's friend list, so a friendship appears once in each person's list. That is 5 billion list entries, or 2.5 billion distinct friendships. At 8 bytes per friend id, the lists take about 40 GB before indexes and replication.
using the primer's conversion of 400 requests per second ≈ 1 billion a month.
Real networks are much bigger and denser. Backstrom et al. measured Facebook's whole active network in 2011: about 721 million users and 69 billion friendship links. The average distance between two users was 4.74 hops, "the world is even smaller than we expected". So paths are short, but each hop multiplies the number of people reached.
Modeling the graph without a graph database
Store each person once, keyed by id, with a list of friend ids:
class Person(object):
def __init__(self, id, name, friend_ids):
self.id = id
self.name = name
self.friend_ids = friend_ids
The data is too big for one machine, so the primer shards people across Person Servers. A Lookup Service maps a person's id to the server that holds them.
Facebook's TAO (USENIX ATC 2013) is the industrial version of this idea: a graph served from sharded MySQL through a cache. It has two primitives:
- Objects are "typed nodes", each identified by "a 64-bit integer (id) that is unique across all objects".
- Associations are "typed directed edges between objects", identified by (
id1,atype,id2), with a time and data fields. "At most one association of a given type can exist between any two objects."
A friendship is stored as two associations, one in each direction.
API
The primer's REST API returns the path as a list of people:
GET /api/v1/friend_search?person_id=1234
→ [ { "person_id": "100", "name": "foo", "link": "https://social.com/foo" },
{ "person_id": "53", "name": "bar", "link": "https://social.com/bar" },
{ "person_id": "1234", "name": "baz", "link": "https://social.com/baz" } ]
For calls between internal services, the primer suggests RPC.
- Social Graph Clients (10k RPS): 1 × Clients & Users
- Route53 Anycast Geo-DNS: 2 × Anycast DNS / Route53
- Graph API Gateway LB: 2 × Application Load Balancer
- Graph Traversal & Ranking Workers: 14 × App Compute Server
- TAO / Redis Graph Cache: 5 × Redis In-Memory Cache
- Kafka Graph Mutation Stream: 3 × Message Queue / Kafka
- S3 Graph Snapshots & Dumps: 2 × Object Storage (S3)
- Primary Sharded DB (Objects & Edges): 2 × Primary Relational DB
- Read Replicas (Graph Metadata): 4 × Database Read Replica
- Social Graph Clients (10k RPS) sends requests to Route53 Anycast Geo-DNS
- Route53 Anycast Geo-DNS sends requests to Graph API Gateway LB
- Graph API Gateway LB sends requests to Graph Traversal & Ranking Workers
- Graph Traversal & Ranking Workers sends requests to TAO / Redis Graph Cache
- Graph Traversal & Ranking Workers sends requests to Kafka Graph Mutation Stream
- Graph Traversal & Ranking Workers sends requests to S3 Graph Snapshots & Dumps
- Graph Traversal & Ranking Workers sends requests to Primary Sharded DB (Objects & Edges)
- Primary Sharded DB (Objects & Edges) sends requests to Read Replicas (Graph Metadata)
- Graph Traversal & Ranking Workers sends requests to Read Replicas (Graph Metadata)
- Kafka Graph Mutation Stream sends requests to Graph Traversal & Ranking Workers
Key takeaways
- 100 million users with 50 friends each give 5 billion list entries (2.5 billion friendships), about 40 GB of friend IDs; 1 billion searches a month is about 400 a second.
- Store each person once with a list of friend IDs, shard people across Person Servers, and find a person's server through a Lookup Service.
- Facebook's TAO does the same at scale with objects and typed associations, a friendship being two associations, one each way.
Sources
- System Design Primer: Design the data structures for a social network
- Bronson et al., “TAO: Facebook’s Distributed Data Store for the Social Graph”, USENIX ATC 2013
- Backstrom, Boldi, Rosa, Ugander and Vigna, “Four Degrees of Separation” (2011)
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.