FjellSystem Design Lab
Lesson 10.3• 10 min read

Storage, sharding and the timeline cache

Snowflake 64-bit IDs, sharding by user or by tweet, and the Redis home timeline cache.

What you will learn

  • Read Snowflake's 64-bit ID layout, and what "roughly time ordered" means.
  • Choose a shard key for stored tweets.
  • Keep home timelines in a capped Redis list per active user.

IDs: Twitter's Snowflake

Tweets need unique IDs generated on many machines without a central counter. Twitter's requirements for Snowflake (2010): IDs that are "roughly time ordered", that fit in 64 bits, and a generator at least as available as the services that use it.

The layout, from Snowflake's source:

| 1 bit: unused (sign) | 41 bits: ms since a custom epoch | 5 bits: datacenter | 5 bits: worker | 12 bits: sequence |
  • Time: 41 bits of milliseconds since Twitter's epoch, 1288834974657 (4 November 2010). 2412^{41} ms is about 69.7 years.
  • Machine: 5 bits of datacenter and 5 of worker, 1,024 generators in all.
  • Sequence: 12 bits, so 4,096 IDs per millisecond per generator. The README says it "rolls over every 4096 per machine (with protection to avoid rollover in the same ms)".

Because time is in the high bits, sorting by ID is roughly sorting by time. The README promises "k-sorted" IDs within about a second, not exact order: two generators' clocks differ slightly. It also depends on clocks not running backwards. Snowflake "will refuse to generate ids" until the clock passes the last time it used.


Where tweets live

The primer stores each user's own tweets (the user timeline) in a relational database. At this write volume, a single primary "would overwhelm a single SQL Write Master-Slave". The primer points to federation, sharding, denormalization and SQL tuning, or moving some data to NoSQL. It also suggests keeping only a month of tweets in the Tweet Info Service.

If you shard, the key decides which queries stay on one shard:

  • By user_id: a user's own timeline is a single-shard read. But a very active or very popular user concentrates load on one shard.
  • By tweet_id: writes spread evenly. But a user's timeline then needs a secondary index or a query to every shard.

Media goes to an object store.


The home timeline cache

The primer's Redis list per user holds 17-byte entries (tweet ID, user ID, metadata):

LPUSH timeline:123 <tweet id>     # O(1) per element added
LTRIM timeline:123 0 799          # O(N), N = entries removed (usually 1 here)
LRANGE timeline:123 0 19          # O(S+N): S = offset from the head, N = entries returned

Reading the first page is cheap because S=0S = 0 and NN is the page size. Twitter capped each home timeline at 800 entries.

Keep timelines only for active users. If someone returns after 30 days away, rebuild from the user graph and the SQL database. That user's first request is slower, which is the price of not holding everyone's timeline in RAM.

Snowflake IDs and a feed buffer
64-bit structure
Twitter Snowflake 64-bit integer layout
ID: 1856487137080668161
1b
0
Sign
41 bits
Epoch milliseconds (~69y)
Δ 442621025343 ms
5 bits
DC: 3
0–31
5 bits
Node: 7
0–31
12 bits
Seq: 1
4,096/ms
Datacenter:
Worker machine:
Redis user feed cache (timeline:usr_123)
User status:
@alice_devDeploying Raft consensus cluster to Kubernetes...
Snowflake: 1856729104820192257
@bob_systemsLSM-trees vs B-Trees: write amplification benchmarks.
Snowflake: 1856729104820192258
@carol_archConsistent hashing with 256 virtual nodes.
Snowflake: 1856729104820192259
Timeline memory and index log
Timeline cache engine ready. Snowflake IDs provide 64-bit time-ordered unique keys.
Figure 3: Sharded data tier separating relational user graphs from horizontally partitioned Tweet stores and Redis feed buffers.
  • API Server Cluster: 8 × App Compute Server
  • Snowflake ID Generator Pool: 4 × App Compute Server
  • User Graph DB (Followers): 3 × Primary Relational DB
  • Sharded Tweet DB (MySQL/Cassandra): 4 × Primary Relational DB
  • Redis Timeline Cluster (lists): 4 × Redis In-Memory Cache
  • Cold Feed Rebuild Workers: 2 × App Compute Server
  • API Server Cluster sends requests to Snowflake ID Generator Pool
  • API Server Cluster sends requests to User Graph DB (Followers)
  • API Server Cluster sends requests to Sharded Tweet DB (MySQL/Cassandra)
  • API Server Cluster sends requests to Redis Timeline Cluster (lists)
  • Cold Feed Rebuild Workers sends requests to Sharded Tweet DB (MySQL/Cassandra)
  • Cold Feed Rebuild Workers sends requests to Redis Timeline Cluster (lists)

Key takeaways

  • Snowflake packs 41 bits of milliseconds, 10 bits of machine and 12 bits of sequence; sorting by ID matches time order to within about a second, not exactly, because clocks differ.
  • Sharding by user_id keeps a user's timeline on one shard but concentrates busy users; sharding by tweet_id spreads writes but scatters each timeline.
  • Each home timeline is a capped list of IDs (Twitter kept 800); returning inactive users get a slower first request while their timeline is rebuilt.

Sources

Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.