Object Storage System Design Template (S3-Like, Erasure Coding)
S3-like blob store design: consistent metadata, packed volumes, replicas then RS(9,6) erasure coding across AZs, durability maths, repair, deletes
उपयोग कैसे करें: Use it when designing or reviewing an object or blob store, or preparing the object storage design interview. Fill the object size distribution and the durability target first, then choose the code and placement so one AZ never holds more fragments than the code can lose. The main pitfall is trusting independent-failure durability numbers while ignoring correlated failures and operator deletes.
प्रीव्यू
System Design: [Object storage service name]
Reference design for an S3-compatible object (blob) store: a strongly consistent metadata service, data placement across failure domains, packing of small objects into large volumes, replication for new data and erasure coding for data that has cooled, durability arithmetic, repair and deletion. It draws on the published Haystack (OSDI 2010) and f4 (OSDI 2014) papers and on the public Amazon S3 documentation for API behaviour. It follows the structure of the System Design Document template, adds a 45-minute interview path, access-pattern and failure-mode tables and a cited "How real systems do it" box, and is pre-filled with a worked design to adapt. Replace every
[bracketed]value and every row marked Example; values marked Example are assumptions, not benchmarks.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-OBJ-001] |
| System / feature | [Object storage service] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Owner | [@owner] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal] |
| Difficulty | Advanced |
| API compatibility target | [S3 REST API subset, listed in 4.1] |
| Related | [Backup policy], [Data retention schedule], [ADR: erasure code parameters] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| SRE / operations | [@name] | [Decision] | YYYY-MM-DD |
| Security | [@name] | [Decision] | YYYY-MM-DD |
| Hardware and capacity planning | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Changed erasure code from RS(10,4) in one zone to RS(9,6) across three zones |
| 0.3 | YYYY-MM-DD | [@name] | Example: Added crypto-shredding for deletes in erasure-coded volumes |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: Product teams store user uploads, backups and analytics files on a network file system that holds 3 copies of everything. It costs three times the logical bytes, listing a directory with millions of files takes minutes, and two disk-shelf failures in one rack last year made 4 TB unavailable for a day. The company needs an internal object store with an S3-compatible API, lower storage overhead and a stated durability target.]
1.2 Proposed solution
[Example: A regional service across three availability zones (AZs). Stateless front ends speak the S3 REST API. A metadata service, sharded by bucket and key range on consensus-replicated groups, maps each object version to its data location and gives strong read-after-write consistency. New objects are written as three replicas, one per AZ, into large append-only volumes; small objects are packed many to a volume so a read costs one disk seek. After 30 days without change, volumes are converted to a Reed-Solomon RS(9,6) code with five fragments per AZ, which survives the loss of a whole AZ plus one more disk at 1.67 times the logical size. Background scrubbers verify checksums and a repair service rebuilds lost fragments.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Replication or erasure coding? | Both: 3 replicas while hot, RS(9,6) across 3 AZs after 30 days | 7.3, 10 |
| Q-02 | Example: Consistency for GET and LIST after PUT? | Strong read-after-write for object operations; bucket configuration eventually consistent | 7.1 |
| Q-03 | Example: How are small objects stored? | Packed into 100 GB append-only volumes with an in-memory index | 7.2 |
| Q-04 | Example: How do deletes reclaim space in coded volumes? | Per-object key deleted at once (crypto-shredding); space reclaimed by periodic rewrite | 7.6 |
| [Q-nn] | [Question] | [Option] | [Section] |
1.4 45-minute interview path
Use this order when the document serves an interview or a short design review. The written sections below are the long form of each step.
| Minutes | Cover | Output on the whiteboard |
|---|---|---|
| 0 to 5 | Scope: PUT, GET, DELETE, LIST, multipart; consistency; durability target; below-the-line items (2.3) | Five operations and two numbers |
| 5 to 10 | Estimates: objects, bytes, request rates, raw capacity under replication and erasure coding (section 3) | "Metadata is 100 TB, data is 87 PB raw" |
| 10 to 15 | Interface: PUT and GET with conditional headers, multipart upload (section 4) | Three calls |
| 15 to 25 | High-level design: front end, metadata service, storage nodes, placement, repair (section 6) | One diagram |
| 25 to 40 | One deep dive: durability and erasure coding (default, 7.3), or metadata and LIST (7.1), or small-object packing (7.2), or deletes (7.6) | Options, choice, reason |
| 40 to 45 | Failures (disk, AZ, metadata shard, silent corruption), the 10x row in section 10 | Two failure rows |
Start the default deep dive with the naive answer, three full copies, and work out its cost (150 PB raw for 50 PB of data). Then introduce a Reed-Solomon code, show why the fragments must be spread so one AZ holds no more fragments than the code can lose, and only then compute durability.
2. Requirements
2.1 Core functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: PUT, GET (with byte ranges), HEAD, DELETE objects in buckets | Must |
| FR-02 | Example: Multipart upload for large objects, with parts uploaded in parallel and resumed | Must |
| FR-03 | Example: LIST objects by prefix with pagination, in lexicographic key order | Must |
| FR-04 | Example: Conditional writes (If-None-Match: * to create only, If-Match on ETag) | Must |
| FR-05 | Example: Object versioning per bucket and retention locks (write once, read many) | Must |
| FR-06 | Example: Lifecycle rules: expire objects and versions after a set age | Should |
| FR-07 | Example: Asynchronous replication of a bucket to another region | Should |
| FR-08 | Example: Event notifications on create and delete | Could |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | Durability | Example: Design target of at least 11 nines per object per year, under the model in 7.3 | Model plus scrubber and repair metrics |
| NFR-02 | Availability | Example: 99.99% of GET and PUT requests succeed per month; survives loss of one AZ | Front end success ratio, AZ evacuation drill |
| NFR-03 | Consistency | Example: A GET or LIST started after a successful PUT or DELETE sees its effect | Linearizability tests on the metadata service |
| NFR-04 | Latency | Example: First byte p99 at or below 100 ms for objects under 1 MiB | Front end timing |
| NFR-05 | Cost | Example: Raw-to-logical ratio at or below 1.8 averaged over the fleet | Capacity reports |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Below the line (out of scope)
Name these explicitly so reviewers and interviewers do not pull the design toward them.
- [Example: a POSIX file system interface or in-place partial updates of objects]
- [Example: content delivery to end users (a CDN sits in front)]
- [Example: tabular or vector bucket types and query in place]
- [Example: cross-region strong consistency (replication is asynchronous)]
- [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
| Input | Symbol | Value | Source |
|---|---|---|---|
| Logical data stored | Example: 50 PB | Current file systems x growth | |
| Objects stored | Example: 200 billion | Inventory of current files | |
| Mean object size | 250 KB | Derived; median is far smaller | |
| Peak PUT rate | Example: 5 000/s (average 2 000/s) | Upload logs | |
| Peak GET rate | Example: 100 000/s | Access logs | |
| Share of bytes in the replicated (hot) tier | Example: 5% | Objects younger than 30 days | |
| Drive size and target fill | , | Example: 20 TB, 80% | Hardware plan |
| Metadata per object version | Example: 500 bytes | Key, version, size, checksum, location, tags | |
| Annual failure rate of a drive | AFR | Example: 2% | Fleet statistics |
| Time to detect and repair a lost fragment | Example: 24 h | Repair service target |
3.2 Bandwidth
Three replicas on write mean 3.75 GB/s of disk writes at peak, and the later conversion to RS(9,6) writes another GB/s on average when it catches up with peak intake. At the average PUT rate the gross intake is about 1.3 PB a month before deletes. Consequence: reads, not writes, size the network; the front end tier is sized on 200 Gbit/s egress and a cache absorbs the hottest objects.
3.3 Raw capacity
| Scheme | Overhead (raw / logical) | Raw for 50 PB | Survives |
|---|---|---|---|
| 3 replicas, one per AZ | 3.0 | 150 PB | Loss of one AZ plus one more copy |
| RS(10,4) inside one AZ | 1.4 | 70 PB | Four lost fragments, but not the loss of that AZ |
| RS(6,3) across 3 AZs (3 per AZ) | 1.5 | 75 PB | One AZ, and nothing more while it is down |
| RS(9,6) across 3 AZs (5 per AZ) | 1.67 | 83.3 PB | One AZ plus one more fragment |
| Chosen blend: 5% at 3.0, 95% at RS(9,6) | 1.73 | 86.7 PB | As above |
Consequence: erasure coding saves about 63 PB of raw disk against three replicas, which pays for the extra CPU and repair traffic many times over.
3.4 Metadata
At 2 TB per metadata shard that is 50 shards, each a three-member consensus group. Every GET needs one metadata lookup (100 000/s at peak) unless the front end caches the location of immutable versions. Consequence: metadata does not fit in memory on one machine and must be range-partitioned from day one.
3.5 Disk operations
- Average GET load per drive: reads/s, well below the roughly 100 random reads a second an HDD sustains, but only if a GET costs one seek (7.2).
- Objects per drive: . With a 32-byte in-memory index entry per object that is 1.2 GB of index per drive, about 28 GB per 24-drive server. Consequence: the storage-node index fits in memory, so no disk read is spent on finding the object.
3.6 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Egress | 25 GB/s (200 Gbit/s) peak | Front ends sized on bandwidth; hot-object cache |
| Raw capacity | 86.7 PB (5 417 drives, about 226 servers) | RS(9,6) for cold data, 3 replicas for 30 days |
| Metadata | 100 TB per copy | 50 consensus-replicated range shards |
| Seeks per GET | 1 | Packed volumes and in-memory volume index |
4. API design
4.1 Interfaces
| Operation | Request | Notes |
|---|---|---|
| Put object | PUT /{bucket}/{key} with Content-MD5 or a checksum header | Single request up to Example: 5 GiB; returns ETag and version ID |
| Get object | GET /{bucket}/{key}?versionId= with optional Range | Range reads touch only the needed fragments |
| Head object | HEAD /{bucket}/{key} | Metadata only |
| Delete object | DELETE /{bucket}/{key} | Adds a delete marker in versioned buckets |
| List objects | GET /{bucket}?list-type=2&prefix=&delimiter=&continuation-token= | Up to 1 000 keys per page, lexicographic order |
| Multipart upload | POST ?uploads, PUT ?partNumber=&uploadId=, POST ?uploadId= (complete), DELETE ?uploadId= (abort) | Parts uploaded in parallel and retried independently |
| Conditional create | PUT with If-None-Match: * | Fails with 412 if the key exists; makes "create once" safe |
For reference, the public Amazon S3 documentation (checked October 2026) lists multipart parts of 5 MiB to 5 GiB, at most 10 000 parts and a maximum object size of 48.8 TiB; choose your own limits and publish them, and check the provider page if you need exact compatibility.
4.2 Multipart flow
Parts become visible only when the completion commits the version in one metadata transaction, so readers never see half an object. Incomplete uploads expire by lifecycle rule (Example: 7 days) and their parts are garbage-collected.
4.3 Error example
A conditional create that loses the race gets 412 and must not retry the same create; the client reads the existing version instead.
HTTP/1.1 412 Precondition Failed
Content-Type: application/problem+json
{"type": "https://storage.example.com/problems/precondition-failed", "title": "Object already exists", "status": 412, "detail": "If-None-Match: * was set and the key reports/2026/q3.parquet already has a current version.", "instance": "/analytics/reports/2026/q3.parquet"}
5. Data model
5.1 Entities
A volume is an append-only container (Example: 100 GB). While open it is written as three replicas; once sealed and cold it is encoded as nine data and six parity fragments. Objects point at volumes, not disks, so moving or re-encoding a volume never touches object metadata.
5.2 Storage choices
| Data | Store | Key | Main queries | Why |
|---|---|---|---|---|
| Object versions and extents | Example: Range-partitioned key-value store on consensus groups | (bucket, key, inverted version) | Point lookup, prefix scan for LIST | Strong consistency and ordered scans |
| Volume and fragment map | Separate placement service, replicated | volume_id | Where are the fragments of volume V | Small (millions of volumes), changes on repair |
| Volume index on storage nodes | Memory, rebuilt from an index file on disk | (volume_id, object offset) | Offset and length of an object in a volume | One seek per read |
| Object bytes | Storage nodes, raw disks or large files | Volume, offset | Sequential append, positioned read | No file-per-object metadata overhead |
5.3 Access patterns
| Access pattern | Frequency | Key or index that serves it | Partition key rationale |
|---|---|---|---|
| GET latest version of a key | 100 000/s peak | Metadata point lookup on (bucket, key), newest version first | Range partitions keep keys in order; hot ranges split automatically |
| PUT a new version | 5 000/s peak | Insert (bucket, key, version) after data is durable | Same partition as reads of that key, so read-after-write is local to one group |
| LIST by prefix | Thousands/s | Ordered scan of (bucket, key) from the prefix | Hash partitioning would scatter a prefix; range partitioning keeps it on one or few shards |
| Find fragments of a volume | Every GET miss in cache, every repair | Placement map by volume_id | Small map, cached in front ends |
| Expire objects by lifecycle | Daily batch | Per-bucket scan by creation time index | Batch work, never on the request path |
| Repair a lost disk | Rare, large | Placement map reverse index disk_id to fragments | Lets repair start in seconds after a failure |
Sequential key names (timestamps as prefixes) concentrate writes on the last range. The metadata service splits a range when its write rate stays above a threshold, and documentation tells clients that prefixes spread load.
6. High-level design
| Component | Responsibility | Scales by |
|---|---|---|
| Front end | Authenticate, sign-check, stream data, compute checksums, call metadata | Bandwidth and connections |
| Metadata service | Object versions, extents, LIST order, conditional writes | Shards (key ranges) |
| Placement service | Map volumes to fragments and disks; choose disks for new volumes | Volumes (small) |
| Storage nodes | Append to open volumes, serve positioned reads, keep volume index in memory | Disks |
| Encoder | Convert sealed cold volumes from 3 replicas to RS(9,6) | Background throughput |
| Scrubber | Read every fragment periodically and verify checksums | Disk read budget |
| Repair service | Rebuild missing or corrupt fragments on other disks | Repair bandwidth |
| Garbage collector and lifecycle | Expire versions, delete keys, reclaim volume space | Batch |
7. Detailed design
The deep dives in this section, each with options, choice and reason:
| Deep dive | Options considered | Choice | Reason | Section |
|---|---|---|---|---|
| Metadata and consistency | Eventually consistent hash-partitioned store, relational database, range-partitioned consensus groups | Range-partitioned consensus groups | Strong read-after-write and ordered LIST | 7.1 |
| Small objects | File per object, packed volumes | Packed append-only volumes | One seek per read, no per-file metadata | 7.2 |
| Redundancy | Replicas only, erasure code only, replicas then code | 3 replicas for 30 days, then RS(9,6) across AZs | Cheap writes and reads while hot, 1.67x when cold | 7.3 |
| Deletes | Rewrite volumes on every delete, tombstones and compaction, per-object keys | Per-object keys plus periodic rewrite | Immediate logical delete in coded volumes | 7.6 |
7.1 Metadata service and consistency
- Keys are ordered by
(bucket, key)and split into ranges. Each range is a consensus group of three members in three AZs; a write commits when two members accept it. - A PUT writes the data first, then commits the version in metadata. Only the commit makes the object visible, so a crash between the two leaves orphan data for garbage collection, never a visible broken object.
- Reads of the latest version go to the range leader (or to a follower with a read lease), so a GET after a successful PUT always sees it. LIST scans the same ordered ranges, so it also sees new keys at once.
- Two concurrent PUTs to the same key both succeed; the later commit becomes
the latest version.
If-MatchandIf-None-Matchlet clients avoid lost updates. There are no transactions across keys. - Bucket configuration (policies, lifecycle, versioning state) is cached in front ends and is eventually consistent for a short interval; document it.
7.2 Packing small objects into volumes
A file per object costs inodes, directory lookups and extra seeks. Instead, each storage node appends objects ("needles") to a large volume file and keeps an in-memory map from object ID to offset and length. A GET is then one metadata lookup (often cached) plus one disk read. Each needle carries a header with its ID, size and checksum, so the in-memory index can be rebuilt by scanning the volume, and a periodic index checkpoint keeps restarts fast. Objects larger than Example: 16 MiB skip packing: they are split into 8 MiB extents and erasure-coded directly, because a large object gains nothing from replication-first writes.
7.3 Redundancy and durability
Placement. For RS(9,6) the 15 fragments of a volume go to 15 different servers in 15 different racks, five per AZ. Losing an AZ removes five fragments; nine of the remaining ten are enough to read, so data stays available and one more failure is still tolerated.
Durability model. With independent drive failures, the probability that one drive holding a fragment fails during a repair window of hours is
A stripe of fragments that tolerates losses is lost when more than fragments fail within the same window:
| Scheme | per stripe | Nines | |
|---|---|---|---|
| 3 replicas | 10.2 | ||
| RS(6,3) | 12.4 | ||
| RS(10,4) | 15.4 | ||
| RS(9,6) | 23.5 |
Read this table with care. The model assumes independent failures and a fixed repair window; real losses come from correlated events the model ignores: a power or cooling failure in a rack, a bad firmware batch, a bug in the encoder, an operator deleting the wrong volume. The design therefore spreads fragments across racks and AZs, mixes drive batches, verifies every encoded volume by decoding it before the replicas are dropped, and keeps versioning and retention locks as the defence against logical deletion. The extra margin of RS(9,6) also matters while an AZ is down: with five fragments already gone, a remaining stripe is lost only if two of the other ten fail in a window, about per 24-hour window, which is why AZ outages trigger priority repair.
Repair speed. Rebuilding a 20 TB drive (16 TB used) onto one spare at 150 MB/s takes about 29.6 hours; spreading the rebuild across 100 drives at 50 MB/s each takes about 0.9 hours. Distributed repair is what keeps small. Rebuilding one RS(9,6) fragment reads nine fragments, so repair traffic is nine times the lost bytes and must be throttled against client traffic.
7.4 Write and read paths
The front end acknowledges only after all three replicas have synced and the metadata commit succeeded. If one replica fails, the front end writes the object to a different open volume rather than accepting two copies. A GET reads one replica (nearest AZ first) or, for coded volumes, the one data fragment that holds the object; it falls back to a decode from any nine fragments when that fragment is missing, and verifies the checksum before returning bytes.
7.5 Scrubbing and silent corruption
Every fragment is read and checksummed at least once every Example: 30 days. A mismatch marks the fragment bad and queues a repair from the other fragments. Front ends verify checksums on every read as well, so a corrupt fragment is never served even before the scrubber finds it.
7.6 Deletion, versioning and space reclamation
- A DELETE in a versioned bucket adds a delete marker; older versions stay until lifecycle rules expire them. Retention locks block deletion and overwrite until a date, including for administrators in compliance mode.
- In replicated volumes, deleted needles are marked in the index and the volume is compacted by copying live needles to a new volume.
- In coded volumes, each object is encrypted with its own data key stored in metadata. Deleting the key makes the bytes unreadable at once (crypto-shredding). Space is reclaimed later by rewriting volumes whose live share has fallen below Example: 60%.
7.7 How real systems do it
How real systems do it (paraphrased, not copied). The Haystack paper (Beaver, Kumar, Li, Sobel and Vajgel, OSDI 2010) describes the photo store of a large social network that, at the time, served over a million images a second at peak. The authors found that a network file system spent disk operations on file metadata, so Haystack packs photos into large volume files (about 100 GB each) and keeps a compact in-memory map from photo to offset, using on average about 10 bytes of memory per photo, so a read needs at most one disk operation. The f4 paper (Muralidhar and co-authors, OSDI 2014) adds a warm tier: once data is older and read less, volumes move from Haystack's replicated layout (effective replication 3.6, three copies on RAID-6) to Reed-Solomon(10,4) inside a cell, with blocks of a stripe on different racks, and an XOR of two volumes in a third region for disaster tolerance, which brings the effective replication factor to 2.1. f4 handles deletes by removing a per-object encryption key. This design keeps the same ideas: packed volumes (7.2), hot then cold redundancy (7.3) and key deletion (7.6). Read the originals in 12.3.
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| GET rate | One seek per read, location cache in front ends, hot-object cache | Seek budget of about 100/s per HDD |
| Metadata rate | Range shards split on load | Example: about 20 000 writes/s per shard before a split |
| Sequential key hot spot | Automatic range split, guidance on prefixes | One key range accepting all new keys |
| Large objects | Direct erasure coding in 8 MiB extents, parallel parts | Front end bandwidth |
| Repair traffic | Distributed rebuild, throttled | Nine bytes read per byte rebuilt for RS(9,6) |
| Capacity growth | Add servers per AZ in balanced groups; placement prefers empty disks | Uneven AZ capacity breaks five-per-AZ placement |
Latency budget for a GET of a 200 KB object (target p99 100 ms to first byte):
| Step | Budget |
|---|---|
| Authentication and request parsing | 2 ms |
| Metadata lookup (cache miss) | 10 ms |
| Placement lookup (cached) | 1 ms |
| Storage node seek and read | 40 ms |
| Checksum and first byte to client | 5 ms |
| Headroom | 42 ms |
9. Reliability, security and operations
9.1 Failure modes
| Component | Failure | Detection | Blast radius | Mitigation | RTO |
|---|---|---|---|---|---|
| Disk | Drive failure | I/O errors, missed heartbeats | Fragments on that drive; no data unavailable | Distributed repair from other fragments | Example: about 1 h of repair |
| Server or rack | Power or switch failure | Heartbeats per rack | One fragment per stripe (one rack per fragment) | Placement rule, repair after a grace period of 15 min to avoid needless rebuilds | Example: 15 min plus repair |
| AZ | Whole AZ lost | Multiple rack alarms, provider status | Reads decode from 10 of 15 fragments; writes go to two AZs | Priority repair; new hot writes use two AZs plus a later third copy | Example: reads continue; full redundancy restored in days |
| Metadata shard | Leader loss | Consensus heartbeats | Keys in one range for a few seconds | Leader election within the group | Example: 5 s |
| Encoder | Bug writes bad parity | Decode verification before replicas are dropped | Volumes encoded since the bug | Keep replicas until a full decode check passes | Example: none (replicas kept) |
| Fragment | Silent corruption | Read-path checksums, scrubber | One fragment | Repair from other fragments | Example: minutes |
| Operator | Wrong bucket or volume deleted | Audit log, delete-rate alarm | Data under that bucket | Versioning, retention locks, delayed physical deletion (7 days) | Example: hours, by undelete |
| [Component] | [Failure] | [Signal] | [Scope] | [Mitigation] | [Time] |
9.2 Security
| Threat | Control |
|---|---|
| Public exposure of private buckets | Buckets private by default; account-level block on public access |
| Stolen credentials | Short-lived signed requests, scoped keys, anomaly alerts on egress |
| Data at rest on stolen or returned disks | Per-object data keys wrapped by a key management service; crypto-shredding on decommission |
| Ransomware or malicious deletion | Versioning, retention locks, separate admin roles, delayed physical deletion |
| [Threat] | [Control] |
9.3 Observability
| Signal | Metric or log | Alert threshold |
|---|---|---|
| Durability risk | Stripes with fewer than full fragments, by fragments missing | Any stripe missing 4 or more |
| Repair backlog | Bytes waiting for repair, oldest item age | Oldest above 24 h |
| Scrub coverage | Share of fragments verified in 30 days | Below 100% |
| Request health | Error and latency by operation and AZ | p99 first byte above 100 ms for 10 min |
| Capacity | Fill per AZ and per disk class | Above 80% |
9.4 Cost estimate
| Item | Unit cost | Quantity | Monthly cost |
|---|---|---|---|
| Storage servers | [$/server-month] | Example: about 226 servers with 24 x 20 TB drives | [$] |
| Metadata servers | [$/server-month] | Example: 150 (50 shards x 3) | [$] |
| Front ends and caches | [$/server-month] | Sized on 200 Gbit/s | [$] |
| Network between AZs | [$/GB] | Replica writes plus repair traffic | [$] |
| Total | [$] |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Redundancy | 3 replicas; RS(10,4) in one AZ; RS(9,6) across AZs | Replicas while hot, RS(9,6) when cold | AZ tolerance at 1.67x; fast hot reads | Encoding work, repair reads nine fragments |
| Metadata store | Eventually consistent hash store; consensus range shards | Consensus range shards | Read-after-write and ordered LIST | Cross-AZ latency on every write |
| Small objects | File per object; packed volumes | Packed volumes | One seek per read | Compaction and index rebuild logic |
| Deletes in coded volumes | Rewrite at once; per-object keys | Per-object keys | Immediate, cheap logical delete | Key management on the read path |
| Cross-region | Synchronous; asynchronous per bucket | Asynchronous, opt-in | No cross-region latency on PUT | Recovery point equals replication lag |
| What changes at 10x / 0.1x | 10x (500 PB, 2 trillion objects): about 54 000 drives and 1 PB of metadata per copy, so split into independent cells per region with buckets assigned to cells, and consider a wider code such as RS(12,6) to cut overhead to 1.5. 0.1x (5 PB): three replicas everywhere is simpler and the extra disk is affordable; erasure coding can wait | Review at each doubling of logical bytes | Metadata shards and repair bandwidth grow first | [Note] |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Front end, metadata, replicated volumes, PUT, GET, LIST | API conformance tests pass; consistency tests pass | YYYY-MM-DD |
| M2 | Multipart, versioning, lifecycle | One pilot team migrated | YYYY-MM-DD |
| M3 | Encoder, scrubber, distributed repair | AZ evacuation drill with no data loss | YYYY-MM-DD |
| M4 | Retention locks, crypto-shredding, cross-region replication | Security review signed off | YYYY-MM-DD |
11.2 Rollout and rollback
[Example: Teams copy data in with checksums verified on both sides and keep the old file system read-only for 60 days. The encoder starts with verification only (encode, decode, compare, keep replicas) for 30 days before any replica is dropped. Rollback of the encoder means keeping replicas, which costs capacity but no data.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: Correlated drive failures from one firmware batch | Medium | High | Mix batches per stripe; firmware updates in waves | [@name] |
| R-02 | Example: Hot key ranges overload one metadata shard | Medium | Medium | Load-based splits; prefix guidance | [@name] |
| R-03 | Example: Repair traffic hurts client latency after an AZ loss | Medium | Medium | Repair throttles by priority; durability-first override | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
12. Appendices
12.1 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Do we need a locally repairable code to cut repair reads below nine fragments? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Which buckets need cross-region replication and in which regions (data residency)? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.2 Glossary
| Term | Meaning |
|---|---|
| Volume | Large append-only container that holds many objects |
| Needle | One object inside a volume, with its own header and checksum |
| Stripe | The k data and n minus k parity fragments computed together |
| RS(k, m) in this document | Reed-Solomon code with k data and m parity fragments, written RS(9,6) for 9 plus 6 |
| Effective replication factor | Raw bytes stored divided by logical bytes |
| Scrubbing | Reading stored data to detect corruption before a client does |
| Crypto-shredding | Deleting data by destroying the key that encrypts it |
| [Term] | [Meaning] |
12.3 References
| Reference | Use |
|---|---|
| Beaver, Kumar, Li, Sobel, Vajgel, "Finding a needle in Haystack: Facebook's photo storage", OSDI 2010 | 7.2, 7.7 |
| Muralidhar and co-authors, "f4: Facebook's Warm BLOB Storage System", OSDI 2014, pages 383 to 398 | 7.3, 7.6, 7.7 |
| Amazon S3 data consistency model, AWS documentation, checked October 2026 | Behaviour reference for 7.1 |
| Amazon S3 multipart upload limits, AWS documentation, checked October 2026 | Limits reference for 4.1 |
| RFC 9457 Problem Details for HTTP APIs | Error body (4.3) |
Distributed Key-Value Store System Design (system-design/design-distributed-kv-store) | Consensus and partitioning for the metadata service |
Photo Sharing System Design (system-design/design-photo-sharing) | A main client of this store |
CDN System Design (system-design/design-cdn) | Delivery in front of the store |
Backup Policy (ops/backup-policy) | What versioning does not replace |
Back-of-Envelope Estimation and Capacity Model (system-design/back-of-envelope-estimation) | Method for section 3 |
Standard and licence note
Based on common industry practice for object storage, the published Haystack (OSDI 2010) and f4 (OSDI 2014) papers and the public Amazon S3 documentation cited in 12.3, used only as a behaviour reference. The papers are paraphrased in 7.7 only; no text, figures or code were copied. The durability figures come from the simple independent-failure model in 7.3 and are illustrative, not a guarantee; all capacity numbers are examples computed from the stated assumptions. Original text written for this collection. Template licence: MIT.
संबंधित टेम्पलेट
Ad Click Aggregation System Design Template (Reference Design)
45-minute path, click dedupe, event-time windows, exactly-once counting, exact and Count-Min top-K, batch reconciliation for billing, failure modes, 10x row
सिस्टम डिज़ाइनZanzibar-Style Authorization Service System Design Template
Relationship-based authorization service: relation tuples, namespace config, Check/Expand/List/Watch APIs, consistency tokens, caching, sharding, failure modes
सिस्टम डिज़ाइनCDC Pipeline System Design Template
CDC pipeline: log-based capture, watermark chunked snapshot, per-key order, schema evolution, outbox, search, cache and warehouse sinks, replay