Courses / System Design Interview / Chapter 17
Module 2 · Building Blocks · Chapter 17 of 39
Blob Stores
Builds on Databases: Replication, Partitioning and Choosing a Store, Designing a Key-Value Store, Content Delivery Networks and Message Queues. Next up: Distributed Search.
Part 1 · What a Blob Store Is For
Files are not rows
It is past midnight after a wedding in Jaipur, and two hundred guests open the same photo backup app to save what they shot. A cousin's 2 GB video of the baraat reaches 93% when her phone switches towers, and the upload starts again from zero. In the app's data centre, one of the two file servers holding every family's photos reports a dead disk array, and the team spends the night restoring a week-old backup. A restart from zero, one machine holding everything, a lost week: each is a problem a blob store is built to solve.
A blob (binary large object) is a file stored as an opaque run of bytes: a photo, a video, a backup. A blob store, also called an object store, keeps billions of them, from a few KB to many GB each, and returns any one by name. This chapter follows the backup app as it grows from those two file servers to 10 billion files.
Keeping files in the database is the obvious first idea, and a poor one. Databases are built for small rows that are queried and updated in place; a 4 MB photo in a row slows backups and replication and pushes useful rows out of memory, for bytes nobody queries. So the usual split is simple: the file goes in the blob store, and a small row in the database points to it.
Here is how the three options compare.
| Database row | Files on one server's disk | Blob store | |
|---|---|---|---|
| Best size per item | Bytes to a few KB | Any | KB to many GB |
| Scale | Limited by the database | One machine's disks | Billions of objects, petabytes |
| Survives a dead server | With replicas | No | Yes, copies on several machines |
| Cost per GB | High | Low, but you run everything | Low |
Database rowBytes to a few KB
Files on one server's diskAny
Blob storeKB to many GB
Database rowLimited by the database
Files on one server's diskOne machine's disks
Blob storeBillions of objects, petabytes
Database rowWith replicas
Files on one server's diskNo
Blob storeYes, copies on several machines
Database rowHigh
Files on one server's diskLow, but you run everything
Blob storeLow
The blob store is the only column that survives a dead machine and still grows to petabytes, which is exactly what the backup app lacked that night.
Buckets, keys and metadata
From outside, the data model is deliberately small. A bucket is a named container, often one per app and environment, with its own access rules. Inside it, each object is identified by a key, a string such as u/42/2026/10/IMG_2041.jpg for user 42's photo.
The slashes look like folders, but the namespace is flat: the key is just a string, and listing all keys that start with u/42/2026/10/ gives a folder-like view of one month. Each object also carries metadata: system fields such as size, content type, creation time and a checksum, plus your own fields such as the phone it came from.
PUT /backup-prod/u/42/2026/10/IMG_2041.jpg Content-Type: image/jpeg Content-Length: 3412880 GET /backup-prod/u/42/2026/10/baraat.mp4 Range: bytes=0-1048575 HEAD /backup-prod/u/42/2026/10/IMG_2041.jpg GET /backup-prod?prefix=u/42/2026/10/&limit=100 DELETE /backup-prod/u/42/2026/10/IMG_2041.jpg
Put, get (whole or a byte range), metadata only, list by prefix, delete. That is nearly the whole API.
Objects are usually immutable: you replace a whole object rather than editing bytes in the middle, which makes caching and replication much simpler, because a copy can never be half old and half new. A byte-range read still lets a video player fetch only the minute it needs.
Some stores split the key space into sorted ranges, so if every key starts with today's date, all of today's uploads land on one partition. A short hash at the front of the key, such as 7f/u/42/2026/10/IMG_2041.jpg, spreads the load.
Amazon S3, Google Cloud Storage and Azure Blob Storage are the big clouds' managed stores; use the one where your servers run, since moving data between clouds costs egress. MinIO is an S3-compatible store you run yourself, when data must stay on premises. HDFS holds huge files read in bulk by analytics jobs, not photos served to phones.
Part 2 · Inside the Store
Metadata service and data nodes
Behind that API, a blob store has two very different jobs, so it uses two kinds of servers. The metadata service knows, for every object, which chunks it is made of and which data nodes hold each chunk. The data nodes store chunks on their local disks and do little else.
Checks who you are
Streams bytes in and out
Stateless, many copies
Key → chunks → nodes
Small, hot, must be consistent
A replicated, partitioned database
Store chunks on disk
Report health and free space
Thousands of them, all alike
Metadata is small but read on every request, so it lives in a replicated, partitioned database (the databases chapter) with strong consistency. Each partition's leader is chosen through consensus (the coordination chapter), so two servers never both decide where a key's chunks go. The bytes are huge but simple, so they sit on cheap disks.
The backup app shows the difference in size. Say it reaches 1 crore users with about 1,000 files each: 10 billion objects. Assume an average of 1 MB, since every photo also has small thumbnail objects beside it.
| Metadata | Data | |
|---|---|---|
| Per object | About 500 bytes | About 1 MB on average |
| For 10 billion objects | About 5 TB | About 10 PB |
| Needs | Fast, consistent lookups | Cheap disks and lots of bandwidth |
Metadata is about 2,000 times smaller than the data it describes, small enough for a database cluster, while the bytes need thousands of disks. Data nodes send a heartbeat to the metadata service every few seconds with their health and free space. That is how the metadata service knows where new chunks can go, and how it notices when a node stops answering.
Chunks and multipart upload
The cousin's 2 GB baraat video is where the design meets a real phone network. Sent as one piece, it is fragile: a tower switch at 93% means starting again, and one data node must hold the whole file. So large files are split into chunks of a fixed size, from a few MB to around 128 MB. In chunks, a file can be uploaded in parallel, retried a piece at a time and spread over many nodes.
The client side of this is called multipart upload, and it runs in five steps.
- 1Startthe client asks to begin and gets an upload id
- 2Upload partseach part carries its number; parts go in parallel and in any order, and each reply includes a checksum
- 3Retry what failedonly the failed parts are sent again
- 4Completethe client sends the list of parts, and the object appears all at once
- 5Or abortthe client cancels, or a cleanup rule removes uploads left unfinished for a few days
In numbers: the 2 GB video in 16 MB parts is 128 parts. Over home Wi-Fi uploading about 5 MB/s, it takes about 7 minutes, and a dropped connection costs at most one 16 MB part, about 3 seconds of work. The app saves the upload id and the finished part numbers on the phone, so it can resume even after being closed.
Part size is a trade-off. Small parts waste less on a retry but mean more requests and more metadata per object. Managed stores also cap the number of parts, commonly at 10,000 per upload, so a client picks bigger parts for bigger files. Once the parts arrive, each chunk must be stored safely, which raises the next question: what happens to the video when a data node dies?
Watch: an upload survives a dead node
Here is the whole life of a smaller file, a 48 MB clip from the same wedding: three chunks, three copies of each on four data nodes, then a disk failure the day after.
Predict before you watch: data node 2 holds a copy of all three chunks, then its disk fails. Can the video still be played, and what brings each chunk back to three copies?
- Yes. Every chunk still has two copies on other nodes.
- Missed heartbeats tell the metadata service, which has surviving nodes copy each lost chunk to a node that lacks it.
Watch for the node labels: a 48 MB video goes up as three 16 MB chunks
Read it as steps
The labels on the data nodes show which chunks each one holds.
The bytes never passed through the metadata service, only decisions did, and the video appeared only at "complete", so readers never see half a file. Losing a node cost no data because every chunk had copies on other machines. Repair ran from two survivors at once instead of waiting for one replacement disk, and that difference decides durability at scale.
Writing when a node is down or slow
The scene's writes met healthy nodes. Real writes meet dead or slow ones, and must decide quickly while the phone waits.
- A node is down. The metadata service has seen its heartbeats stop and does not pick it. If a node dies mid-write, the API asks for a replacement and writes the chunk there, so the chunk still has three copies before the client hears OK.
- A node is slow. The write waits for every copy, so one disk taking 2 seconds slows every upload it touches. Give each copy a timeout of a few times the normal p99, then write to a replacement.
- Confirming at two copies. Some stores reply after two copies and let repair make the third. Uploads get faster at the tail, but for a few minutes that chunk survives only one more loss, so do it only with fast repair.
- Erasure-coded writes follow the same rules per piece: the 9 pieces of a 6 + 3 chunk (explained on the next page) go to 9 different nodes, and a piece whose node fails is written elsewhere.
A half-finished write is never visible: a chunk that reached two nodes before the API crashed was never added to the metadata, so no reader finds it, and garbage collection removes the strays later. Three full copies are the simple answer to durability; the next page asks whether they are the cheapest one.
Replication or erasure coding
Durability is the one promise a blob store cannot break: how many copies, placed where, and how fast they are repaired. This page and the next two carry it.
With 10,000 disks failing at roughly 1 to 2% a year, you lose one every two to four days. Durability, the chance that an object survives a year, comes from redundant data on different machines.
Replication keeps full copies, usually 3, on different disks and racks. It is simple, reads are fast, and repairing a lost copy means copying one chunk. The price is 3× the storage.
Erasure coding works like a parity sum: from k data pieces it computes m extra parity pieces, so that any k of the k + m pieces can rebuild the chunk. Reed-Solomon coding is the classic method. With 6 + 3, a chunk becomes 9 pieces on 9 nodes, uses 9 ÷ 6 = 1.5× the storage and survives any 3 lost pieces. The price is work: encoding costs CPU, reading a whole chunk means fetching pieces from 6 nodes, and rebuilding one lost piece means reading 6 others over the network.
Side by side, for 1 PB of data:
| Scheme | Raw disk for 1 PB | Losses survived | Repairing one piece | Good for |
|---|---|---|---|---|
| 3 replicas | 3 PB | 2 | Copy one chunk | Hot data, small objects |
| Erasure coding 6 + 3 | 1.5 PB | 3 | Read 6 pieces to rebuild 1 | Warm and cold data |
| Erasure coding 10 + 4 | 1.4 PB | 4 | Read 10 pieces to rebuild 1 | Large, rarely read archives |
Raw disk for 1 PB3 PB
Losses survived2
Repairing one pieceCopy one chunk
Good forHot data, small objects
Raw disk for 1 PB1.5 PB
Losses survived3
Repairing one pieceRead 6 pieces to rebuild 1
Good forWarm and cold data
Raw disk for 1 PB1.4 PB
Losses survived4
Repairing one pieceRead 10 pieces to rebuild 1
Good forLarge, rarely read archives
Erasure coding halves the disk and survives more losses; it pays in network and CPU on every repair and degraded read. Many stores use both: the backup app's new uploads stay replicated while they are viewed and shared, and files untouched for 30 days are re-encoded to 6 + 3.
Disks occasionally return wrong bytes without an error. Every chunk carries a checksum, verified on each read and by a background scrub; a chunk that fails is treated as lost and repaired.
Placement and repair
Copies only help if they do not die together, and only while they are rebuilt faster than disks fail. A failure domain is a set of things that fail together: a disk, a machine, a rack sharing one power feed and one switch, a building. Three copies in one rack disappear together when that rack loses power, so placement puts each copy in a different rack.
Erasure coding needs the same care per piece. If a 6 + 3 chunk keeps 3 pieces in each of 3 racks, losing one rack uses up all its spare pieces, and one more disk failure during that hour loses the chunk. With one piece in each of 9 racks, a whole rack plus any two disks can fail. So a coded cluster needs at least 9 racks to get the full benefit.
Repair is a race. An object is lost only if every copy, or 4 of its 9 pieces, fails before repair finishes, so durability depends on repair speed as much as on the number of copies. Take a 20 TB disk that is 80% full: 16 TB of chunks to rebuild.
- Onto one replacement disk, writing about 200 MB/s: 80,000 seconds, most of a day exposed.
- Spread over 80 nodes, each re-copying 200 GB at 100 MB/s: about half an hour.
- With 6 + 3 coding, each lost piece is rebuilt by reading 6 others: about 96 TB of network reads. Over 40 nodes at 100 MB/s each, that is about 7 hours, unless repair gets more bandwidth.
Two rules keep repair from causing its own outage. Wait 10 to 15 minutes before declaring a node dead, because most silent nodes are rebooting, and rebuilding 16 TB for a node that returns wastes the network. And repair the chunks with the fewest copies left first, throttled so user reads keep their bandwidth. A chunk down to its last copy skips the wait entirely.
How many machines
The redundancy scheme now turns into hardware. Take the backup app's 10 billion objects, about 10 PB of data, and assume nodes of 24 disks of 20 TB each. The scheme changes the answer by a factor of two.
| 3 replicas | Erasure coding 6 + 3 | |
|---|---|---|
| Data stored | 10 PB | 10 PB |
| Raw disk needed | 30 PB | 15 PB |
| Usable per node: 24 × 20 TB, filled to 80% | ≈ 384 TB | ≈ 384 TB |
| Data nodes | ≈ 80 | ≈ 40 |
| Disks, failing at 1–2% a year | ≈ 1,900: one every 10–20 days | ≈ 960: one every 20–40 days |
Halving the disk halves the machines, but the smaller cluster has less spare bandwidth for its heavier repairs. Fill disks only to about 80%, because repair needs free space to rebuild into. The 40-node answer also has a placement catch from the previous page: those nodes must spread over at least 9 racks, about 4 or 5 nodes each.
Money follows the same shape. Managed object storage costs roughly $0.02 per GB-month (check current prices), so 10 PB is about $200,000 a month. Either way, for files many people download, internet egress at about $0.05–0.10 per GB often costs more than storing them, which matters once the app lets families share albums.
Part 3 · Deletes, Downloads and Uploads
Deletes and garbage collection
A delete has two halves. Removing the key from the metadata is quick, and from that moment the object is gone from reads and listings. Freeing the chunks on the data nodes happens later, in the background, because deleting across many nodes is slow and can fail halfway.
- 1Metadata marks the object deletedreads and listings stop seeing it at once
- 2A grace period passesa few days in which the delete can still be undone
- 3The garbage collector scansit compares the chunks on each data node with the chunks the metadata still refers to
- 4Orphan chunks are freeddata nodes delete them and reclaim the space
Deleted objects are not the only source of orphan chunks, chunks that nothing refers to. Abandoned multipart uploads leave parts behind, and a write that failed after reaching some nodes leaves stray copies. All are collected the same way. The backup app adds its own layer on top: deleted photos sit in a trash for 30 days, and only then does the app send DELETE to the store.
If you free the chunks and then the metadata update fails, the metadata points at data that no longer exists, and readers get errors. Remove the reference first; an orphan chunk only wastes space until the next collection.
Lifecycle rules automate the rest: move objects older than 30 days to a colder tier, delete old versions after 90 days, abort incomplete uploads after 7.
Serving files: signed URLs and a CDN
Next, how bytes reach phones. Do not stream files through app servers: a 2 GB download ties up bandwidth and a connection for minutes, and the app adds nothing. Let clients talk to the blob store directly, but only for files they are allowed to see.
A signed URL (often called a pre-signed URL) makes that safe. After checking that the user may access a file, your app server builds a URL containing the path, an expiry time and a signature: a keyed hash of those fields, made with a secret only your servers and the store know. The store recomputes it and serves the file only if it matches and the time has not passed, so nobody can change the path or expiry. Anyone holding the link can use it, so keep expiries to minutes.
POST /api/uploads (the app server checks login, quota and file type) 200 {"upload_url": "https://blobs.example.in/backup-prod/u/42/9f1c.jpg?expires=1792134000&sig=4b9e..."} PUT https://blobs.example.in/backup-prod/u/42/9f1c.jpg?expires=1792134000&sig=4b9e... Content-Type: image/jpeg 200 OK (the bytes went straight to the blob store)
The app server only signs; the bytes go straight between the phone and the store, for uploads and downloads alike.
When a family shares the wedding album with 500 relatives, the same photos are read thousands of times. Put a CDN in front of the bucket (the CDN chapter): the bucket becomes the CDN's origin, and edges near users serve repeat reads. Give files versioned names, such as a content hash in the key, so they can be cached for a year and never need invalidating; an edited photo simply gets a new name.
Stores can also publish an event when an object is created. On a queue (the message queue chapter), they let workers make thumbnails without the uploader waiting, and they answer a question this page skipped: how the app learns that an upload really happened.
From pending to ready: tracking uploads
A signed upload URL leaves your database in the dark. The phone may never upload, may send a different file, or may crash halfway. Treat each upload as a small state machine, with the database row as its record.
- 1Pendingthe app server inserts a row (photo 9f1c, owner 42, status pending) and signs a URL
- 2Uploadthe phone sends the bytes straight to the store before the URL expires, say in 15 minutes
- 3Object createdthe store publishes an event to a queue once the object is complete
- 4Readya worker checks the real size and type, makes thumbnails, then sets the status to ready
- 5Sweepa daily job deletes rows still pending after 24 hours, and any object they point to
Sign the limits into the URL itself: the exact key, the content type, a maximum size such as 50 MB, and the expiry. Where the store checks them, a 2 GB file or an executable is turned away at the door, because changing any of them breaks the signature. The worker still checks the real bytes.
Events arrive at least once and sometimes late, so the worker's last step is conditional: set ready only where the status is still pending. A duplicate event then changes nothing, and thumbnails are written to fixed keys, so redoing them is harmless. The album shows only ready rows. The app may call POST /uploads/9f1c/complete to mark it ready sooner, but the server must first confirm with a HEAD request that the object exists with the right size: never take the client's word that an upload finished.
Part 4 · Failures at Scale
What goes wrong at scale
Durability is not the only thing that breaks as the backup app grows.
| Problem | Symptom | Fix |
|---|---|---|
| Hot object | A viral video's chunks hammered on a few nodes | Serve through a CDN; add extra copies of hot chunks |
| Huge buckets | Listing 100 million keys is slow | Partition metadata by bucket and key; paginate listings |
| Tiny files | Per-object overhead dwarfs the data | Pack many small objects into one larger chunk with an index |
| Hot key range | All new uploads hit one metadata partition | Hash prefixes in keys; split busy ranges |
| Orphans and leftovers | Storage grows faster than live data | Garbage collection; lifecycle rules for uploads |
| Repair storm | A dead rack starts petabytes of copying | Wait before repairing; fewest copies first; throttle |
Almost every fix moves load away from one hot spot, whether a node, a partition or the network. The table leaves out one property you must decide and state: consistency, what a reader sees right after a write.
With a single metadata record per key, read-after-write for a new object is straightforward: once the PUT succeeds, every GET sees it, because the commit and the read go to the same partition leader. Two uploads to the same key resolve the same way: each commits a complete chunk list, the later commit wins whole, and the loser's chunks become orphans for the collector. Listings are often served from a separate index and can lag by a moment; document that.
Losing a whole region
Copies across racks and buildings survive a dead disk, a dead machine and a dead rack. They do not survive losing a whole region to a flood, a fire or a long power cut. For data you cannot lose, keep a cross-region copy in a second region hundreds of kilometres away.
The copy is made asynchronously: each new object is written in its home region, then queued and copied over, usually within minutes. Copying before replying would add a cross-region round trip, 30 ms or more, to every part of every upload. The price is a window of recent uploads that a regional disaster could take with it; that window is your recovery point objective (RPO), the most recent data you accept losing.
Four rules shape the second region:
- Each region is a complete store, with its own metadata, so the copy can serve reads alone if the home region goes dark.
- Copy what matters. Original photos and videos, yes; thumbnails, no, since workers can rebuild them. Keep the copy in a colder, cheaper tier.
- Respect data rules. Data that must stay in India, as payment data must, needs a second region that is also in India.
- A copy is not a backup. Deletes copy across too, so turn on versioning, and lock backups against deletion for a set period, such as 30 days.
Part 5 · Interview kit
The 45-minute answer
For "design a blob store", spend the 40 minutes of design time like this.
| Minutes | What you say and draw | Key number or decision |
|---|---|---|
| 0–5 | Requirements: put, ranged get, delete, list by prefix; KB to TB objects; durability first; read-after-write. | A durability target such as 11 nines a year; objects never change in place |
| 5–8 | Estimates that decide something: objects, bytes, metadata size, machines. | 10 B objects ≈ 10 PB; metadata ≈ 5 TB; ≈ 80 nodes at 3×, 40 at 6 + 3 |
| 8–13 | API (PUT, GET, HEAD, DELETE, LIST, multipart) and metadata: key → size, checksum, chunk list → nodes. | Chunks of 16–128 MB; an object appears only on complete |
| 13–22 | Stateless API, consistent partitioned metadata, data nodes with heartbeats, signed URLs, a CDN, events. | Bytes never pass through metadata or app servers |
| 22–37 | Deep dive: durability (placement, writes with a dead node, repair) or metadata at scale (partitions, hot prefixes, listings). | Rebuild a lost disk in under an hour across many nodes |
| 37–40 | Wrap-up: rack and region loss, monitoring (repair backlog, chunks with one copy left, orphan bytes), cost. | Async cross-region copy; egress often beats storage |
Pick durability if they ask how you never lose a file: placement, writes with a dead node, repair speed. Pick metadata if they stress billions of objects or fast listings: partitions by bucket and key, hash prefixes, strong consistency per key.
- Say
- "Metadata and bytes are separate: about 5 TB of metadata in a strongly consistent, partitioned database, and 10 PB of chunks on about 80 data nodes."
- Say
- "New objects keep 3 copies in 3 racks; after 30 days they move to 6 + 3 coding, which halves the disk but makes each repair read 6 pieces."
- Say
- "Repair is spread across the cluster, so a dead disk's 16 TB is rebuilt in about half an hour instead of most of a day."
- Avoid
- Three copies in one rack: they share power and a switch, so they fail together.
- Avoid
- Streaming uploads and downloads through your app servers instead of using signed URLs.
Follow-ups and variants
A senior interviewer then probes what is easy to draw and hard to run:
The same core also appears under other names:
Wrap-up
| Term | Meaning |
|---|---|
| Object | A file stored as opaque bytes under a key in a bucket |
| Metadata service | Maps each key to its chunks and the nodes holding them |
| Erasure coding | k data pieces plus m parity pieces; any k rebuild the chunk |
| Failure domain | Things that fail together: a disk, a node, a rack, a region |
| Signed URL | A short-lived link whose signature allows one action on one key |
| Orphan chunk | A chunk no metadata refers to, freed by garbage collection |
Next, the Distributed Search chapter shows how to find the right item among millions when a user types just a few words.
Part 6 · Check yourself
Quick check
Q1Why does a delete remove the metadata first and free the chunks later?
Think it through, then check.
- Removing the key hides the object at once.
- Freeing chunks first could leave metadata pointing at missing data if the second step failed.
- A collector frees unreferenced chunks after a grace period, which also lets a mistaken delete be undone.
Q2The backup app's storage bill grows 30% faster than the bytes its database says users uploaded. What are the likely causes?
Think it through, then check.
- Abandoned multipart uploads: parts billed but never completed. List them, and abort them after a few days.
- Old versions: with versioning on, every overwrite and delete keeps the previous copy. Check version counts and expire old ones.
- Rows stuck in pending and uncollected orphans: compare stored objects with ready rows.
Q3Shared albums push the app to 200 TB stored and 2 PB downloaded a month. At $0.02 per GB-month to store and $0.05–0.10 per GB of egress, which bill is bigger, and what follows?
Think it through, then check.
- Storage: 200,000 GB × $0.02 ≈ $4,000 a month.
- Egress: 2,000,000 GB × $0.05–0.10 ≈ $100,000–200,000 a month, 25 to 50 times more.
- So work on egress: a CDN with volume pricing, right-sized images and lower video bitrates on mobile.
Q4A rack holding 8 of your 80 data nodes loses power. Should repair start at once, and what happens meanwhile?
Think it through, then check.
- With one copy per rack, each chunk lost at most one copy: reads use the others and new writes go to other racks.
- Do not start at once: those nodes hold about 3 PB, and the rack will probably return. Wait about 15 minutes, then repair chunks with the fewest copies first, throttled.
- If placement ignored racks, some chunks had every copy there: unreadable until power returns.
Q5The CDN chapter cached files at the edge with long TTLs. A user crops a wedding photo and saves it under the same key, and relatives still see the old one for hours. Why, and what is the fix?
Think it through, then check.
- Edges cached the old object under that URL; overwriting it in the bucket does not reach them until the TTL runs out.
- Purging every edge works but is slow.
- Better: give each version a new key, such as a content hash, and point the database row at it.