SwadhyAI Notes

All System Design notes

System Design Storage

Blob / Object Storage

A multi-megabyte Instagram photo never travels through every API server on the way to a worldwide carousel. The phone got a short-lived upload path, wrote bytes into , and background workers built thumbnails while a served the public read.

Object storage is the default home for large immutable bytes: photos, videos, backups, and logs that you replace as a whole rather than patch in place. The design craft is , durability, consistency assumptions, cost, and the async path around the blob.

Upload journey

1

App authorizes a key

Mint identity and destination metadata so the client knows where to write.

2

The client talks to storage, not your app tier, so gigabytes skip the servers.

3

Bytes, key, and metadata land durably so later workers can find the object.

4

Event → workers → CDN

Thumbnails, scans, and public reads follow once the object exists.

What actually happens

The Instagram path above is the default whenever the payload is large and the client is untrusted. Your API authorizes an and mints a ; the phone writes straight to a ; an object-created event wakes workers that write domain state to a metadata database (DB); public viewers hit a nearby edge instead of the origin.

An object is body bytes, that unique key, and (size, checksums, timestamps, plus small user-defined headers). Requests hit an Application Programming Interface (API) tier. A metadata plane maps keys to chunk locations. A data plane spreads those chunks across disks, racks, and Availability Zones (AZs), often with instead of three full copies.

Large files use so a dropped network retries one part, not the whole video. Aging data follows a into colder tiers. Dollar figures later are illustrative: confirm current regional pricing before you commit an estimate in an interview or a budget review.

Try it: walk a multipart object upload

Large blobs upload as numbered parts under one session ID. Parallel PUTs improve throughput on good networks, while per-part retry contains failure blast radius on flaky ones.

Session
None
Phase
idle
Uploaded volume
0 MB
Parts pending / failed
5 / 0

Client

presigned PUT

Holds upload ID, tracks completed part numbers, and sends CompleteMultipartUpload when the ordered list is ready.

Part buffers

parallel PUT
  • Part 1 · 128 MBpending
  • Part 2 · 128 MBpending
  • Part 3 · 128 MBpending
  • Part 4 · 128 MBpending
  • Part 5 · 128 MBpending

Final object

after complete

Readers still see the previous version until complete succeeds.

Multipart is resumability and parallelism, not a different durability model. Each part is stored independently until complete stitches metadata into a single object.

Start a multipart session, upload numbered parts in any order, then complete with the ordered ETag list. A failed part retries alone instead of restarting the whole object.

Simulate a dropped connection. Initiate, upload two parts, fail the third, then retry only that part. Notice how earlier parts remain billable storage but do not need re-upload.

Complete vs abort. Finish all parts and complete to flip the object visible for readers. Abort instead when the user cancels; production buckets also need lifecycle rules to reap stale multipart sessions after a timeout.

How to choose object storage patterns

The path above only fits some bytes. Three questions decide whether is the right home, and which upload and tier pattern to pair with it.

1. Are the bytes mostly replace-as-a-whole? Photos, videos, backups, and log files fit object storage. Databases that need in-place 4 KB writes usually want block volumes instead. Shared path semantics belong on a file system.

2. Who holds the credentials? Prefer a so app servers stay thin and Identity and Access Management (IAM) keys never sit on the device. Use when files are huge or networks drop mid-flight.

3. How hot is yesterday's data? Keep interactive media hot; automate a for aging logs and old originals. Measure retrieval patterns before locking deep archive. Put ownership and access-control lists (ACLs) in a metadata DB: stores bytes, not product state.

Simple rule of thumb

Put unstructured blobs in object storage with IAM-scoped , client-side uploads, lifecycle tiers, and a for read-heavy media. Use events for async processing. Add cross-region copies only where Disaster Recovery (DR) or residency justifies the bill. At very large spend you can evaluate specialized stacks, but most teams win with managed object storage and a clear retention policy first.

From problem to mechanism

Here is the Instagram upload again, slowed down so you can see where a naive proxy fails and what the storage split actually changes.

Problem. A multi-megabyte photo (and later a video) must land durably, sprout thumbnails, and be readable worldwide, without turning every API server into a bandwidth pipe.

Naive system. The app accepts the file body, writes it to disk or a volume, and serves reads from the same tier. There is no , no , and no event that tells workers the object exists. Credentials either sit on the client or every byte transits your autoscalers.

Break it. The app tier proxies gigabytes and saturates under peak uploads. A dropped mobile upload restarts the whole file. A miss burst hits origin request limits. Ownership lives only beside the bytes, so a metadata outage still looks like "the photo is gone."

Mechanism. The API authorizes a key and signs a constrained request. The client PUTs (often in parts) straight to . A metadata plane maps the to chunk locations; a data plane spreads chunks across disks, racks, and AZs, often with . An object-created event fans out to thumbnail, scan, and transcode workers that write the source of truth to a metadata DB. Public GETs stop at the edge.

The invariant is simple: large bytes never need your app process, and product state never lives only in the bucket. The diagrams below are that same split drawn as planes and as the upload-once, process-async loop.

ObjectBlob + + unique key. Typically replaced as a whole rather than byte-patched in place. Per-object size limits vary by product (multi-TB max is common).
BucketNamespace / policy boundary: access rules, versioning, lifecycle, encryption defaults.
KeyFlat namespace; photos/2024/img.jpg is one string, not implicit nesting.
MetadataSystem fields (size, checksums, timestamps) plus small user-defined headers: stored and replicated separately from bytes.
Client (PUT/GET)→Edge / API tier
↓
Metadata planekey → locations, versions
↓
Data planechunks across disks / racks / AZs

Upload playbook and numbers to know

The mechanism says "keep large bytes off the application servers." This is the loop that does it: , direct , then events into a metadata DB and a for reads. Later sections reuse these figures instead of restating them.

Presign loop

Async path

Read path

CDN for public reads

How multipart and presigned URLs differ

A presigned request delegates a narrowly scoped operation, such as uploading to one chosen key before expiry; it does not mean the uploaded object is trustworthy. Record the expected owner, size policy, and processing state in application metadata. After completion, verify the resulting object and checksum where supported before marking the upload ready. Keep pending and rejected objects separate from content the application exposes publicly.

They solve different problems. Multipart means the service knows about "part 7 of 42": you retry individual slices and complete when all succeed. Presigned URLs are short-lived signed HTTPS requests: your backend vouches once; the browser or app uploads or downloads straight to object-storage endpoints.

Interview soundbite: we avoid proxying gigabytes through app servers. Multipart is for throughput and resume; a presigned PUT is for trust boundaries and cost.

Multipart upload

Multipart upload creates an upload session, stores individually numbered parts, and assembles the object only after a successful completion request. A client can retry one failed part without retransmitting the rest. Persist enough session information to resume safely, verify completion instead of assuming every part implies a finished object, and abort abandoned sessions so incomplete parts do not accumulate storage charges.

Use when objects exceed comfortable single-PUT limits or when networks drop mid-flight. Exact part-size rules vary by provider (minimum size except last part, maximum parts, and so on). Server-side initiation keeps IAM credentials off the client.

  1. Initiate. Create a multipart session; receive an upload id and optionally a recommended part size.
  2. Upload parts. PUT each numbered part in parallel; each returns an ETag (part identifier) you must retain. Retry only failed parts: a massive win versus restarting a multi-GB file.
  3. Complete or abort. Send the ordered list of parts; the service concatenates into one object key. Abort unfinished uploads to avoid stranded billable storage. can garbage-collect stale multipart ghosts.
  • More parts give more concurrency; balance client CPU and API rate limits.
  • Use provider checksum headers when correctness of the assembled object matters.
  • Persist upload id and completed parts so a reconnect can resume.

Presigned URLs

Your service signs a constrained request (method, bucket/key, headers, expiry). Anyone holding the URL can perform only that operation until it expires, so design the constraints deliberately narrow.

ClientYour APImints URL + records metadataObject storagedirect PUT / POST body

Follow with an event notification, webhook, or client callback so your domain model knows the object landed.

  • Minutes to tens of minutes beats never-expires, limiting leaked-link damage.
  • Sign exactly one key and verb; optional content-type and length constraints.
  • Treat URLs like bearer tokens; never log query strings with secrets.
  • Bytes bypass app autoscalers; you still pay PUTs and download egress.

Browser uploads of very large files often use multipart upload initiated server-side, then presigned URLs for each part (or a presigned POST policy for smaller files). You keep IAM credentials on the backend while clients stream slices in parallel.

Numbers to know

Estimate payload bytes separately from and request volume. Small objects can be metadata-heavy; videos are dominated by payload and derived renditions.

1M active users

Profile photos

Media uploads

Metadata DB

Analytics and logs

  • 0.5–2 KB metadata per object covers key, owner, timestamps, checksum, class, tags, and index rows.
  • 1B tiny log objects at 1 KB metadata each is about 1 TB before payload.
  • 1M users × (2 MB original + 200 KB thumbnails) ≈ 2.2 TB payload, plus 1–2 GB metadata and versions.
  • 100K uploads/day × 50 MB ≈ 5 TB/day raw; three renditions at 30%, 20%, and 10% add ~3 TB/day.
  • 1M users × 200 events/day × 1 KB ≈ 200 GB/day raw; retention still reaches multi-TB per month.
  • 100K objects/day × 1.5 KB metadata × 2× index/event factor ≈ 300 MB/day catalog growth.
  • 50 MB file, 10 MB parts, 100K/day, 5% retry: about 525K part PUTs/day plus complete calls.
  • If 20% of 100K videos arrive in one peak hour: 20K/hour, or about 5.6 uploads/second.
  • Five parts per video and three parallel parts stay modest on average; size queues for that peak.

Interview template: daily bytes = users x objects/user/day x average size x replica/derivative factor. Then add metadata, request queries per second (QPS), retention days, and lifecycle transitions.

daily_payload = uploads_per_day x average_object_size
derived_payload = daily_payload x derivative_factor
metadata_bytes = object_count x metadata_bytes_per_object x index_factor
tier_bytes = daily_bytes x days_in_tier x compression_or_derivative_factor

Small-object warning: 1B objects of 1 KB payload is only about 1 TB of body data, but 1 KB metadata per object adds another 1 TB before indexes, versions, events, and replication. Object count can dominate listing latency even when payload looks modest. For analytics, compact tiny records into larger files (or columnar layouts) so listing, catalog, and planning work does not dominate reads. Avoid tiny per-event objects without lifecycle and prefix planning.

Retention from those daily bytes

Using the ingest rates above: 5 TB/day of originals for 30 hot days keeps 150 TB hot. Keeping originals for 365 days adds about 1.8 PB total before replication. If derived playback files are 3 TB/day and kept hot for 90 days, add 270 TB. For logs at 200 GB/day compressed 5x, 13 months keeps about 15.6 TB compressed; seven-year archive keeps about 102 TB compressed.

A typical timeline: Day 0–30 Standard (hot); Day 30–90 warm infrequent; Day 90–365 instant archive or cool; Day 365+ deep archive; Year 7+ delete per retention policy. Keep latest photo derivatives hot and expire orphaned previous versions after the restore window. Move upload originals warm after processing if playback uses derived objects, then archive the source after the compliance window. Logs stay hot 7–30 days for debugging, compressed analytics for 13 months, then archive or delete.

ClassIllustrative $/GB-moRetrievalTypical use
Standard~0.02ImmediateHot content, interactive workloads
Warm infrequentLower storageImmediate + per-GB retrieval feeInfrequent but urgent reads
Instant archiveMuch lowerMillisecondsArchive that still might spike reads
Flexible archiveLower stillMinutes to hoursBackup / compliance
Deep archiveFraction of a centHoursCold legal hold

Cold tiers save storage dollars but add retrieval latency and per-GB egress charges. Measure retrieval patterns before locking deep tiers (see the class table).

Durability and consistency

Once bytes are off the app tier, the next question is how the store keeps them and when a read is allowed to see them. and the provider consistency contract are the two answers; neither is a filesystem.

Replication vs erasure coding

With a 6-data-plus-3-parity layout, nine fragments store the equivalent of six fragments of original data, giving 1.5 times raw storage before metadata. A suitable erasure code can reconstruct from any six surviving fragments. Place fragments across the failure domains the service claims to survive; nine fragments on one failed host provide no protection. Recovery also consumes network and compute, so storage efficiency is only one part of the trade-off.

Triple replication is simple but expensive (~3× raw bytes). Erasure coding splits an object into k data fragments plus m parity fragments so any k of (k+m) can reconstruct it, buying similar fault tolerance with fewer raw bytes. Decode cost favors larger objects; tiny objects sometimes stay on full replication for simplicity. The data plane is engineered for rack and AZ failure, at the price of encode/decode work and fan-out reads.

Storage providers often quote extreme annual durability figures (many "nines"). Treat those as marketing shorthand tied to their replication story. You still want versioning, cross-region copies, and restore drills for important data.

Reed–Solomon style schemes (illustrative)

  • Split the object into k data fragments plus m parity fragments.
  • Any k of (k+m) fragments can reconstruct the original object.
  • Example sketch: 8 data + 3 parity, so any three fragment losses are tolerated.
  • Tiny objects sometimes stay on full replication for operational simplicity.

What durability, availability, and recovery each mean

Durability concerns permanently losing the bytes; availability concerns being able to read them now. An archived object can be highly durable while unavailable for immediate playback until restoration completes. A backup also needs usable encryption keys, metadata, and enough restore bandwidth. Test a complete application-level restore rather than only confirming that an object exists in a backup bucket.

  • Durability is about not losing bytes: replication, erasure coding, checksums, versioning, backups.
  • Availability is about serving requests right now: multi-AZ design and regional failover.
  • Recovery still needs restore tests; deletes, bad lifecycle rules, and stolen credentials are yours.
  • Choose by access pattern: objects for immutable blobs, block for random writes, file for paths.

Consistency model

Know your storage provider's guarantees for new writes, overwrites, deletes, and listings. Application bugs often come from assuming filesystem or immediate cross-bucket semantics that object APIs do not provide. Confirm the current contract before you rely on a behavior for correctness.

PUT new → GETTypically read your writes once acknowledged.
Overwrite / DELETEVendor-specific; do not assume immediate global visibility everywhere.
LISTEventually consistent listings have burned many pipelines: use prefixes, markers, or event feeds.

Object-storage semantics vary by provider and operation. Amazon S3 provides strong consistency for object writes, deletes, and listings, so a completed write is not generally hidden from a subsequent S3 LIST. That does not imply atomic updates across several objects or immediate visibility through a separately cached CDN or asynchronous cross-region replica. Traditional flat-key object stores also do not give ordinary filesystem directory-rename semantics; check the exact API and bucket type.

Replication, keys, events, and the CDN

Durability inside one region is not the whole delivery story. Copies, key layout, object-created events, and a decide whether the Instagram carousel feels local and whether workers ever see the upload.

Replication

Same-region copies isolate accounts or aggregate logs. Cross-region async replication aids DR and locality: budget storage twice plus replication API and egress. Optional replication-time Service-Level Agreements (SLAs) exist as paid features; read the fine print. Replication PUTs, cross-region transfer, and destination storage all bill.

Performance patterns

Avoid designing key layouts from an assumed universal prefix limit. Some stores benefit from distributing requests across prefixes, while services such as S3 automatically partition and scale prefixes; verify the current service guidance and measure the workload before randomizing keys. Byte-range requests, multipart transfer, and CDN caching address different bottlenecks. Keep queryable ownership and lifecycle metadata in a database instead of treating object listing as a general-purpose index.

  • Prefer ab/2026/05/user_id/object_id where ab is a hash prefix, to avoid a thundering herd.
  • Avoid monotonically increasing prefixes such as 2026/05/16/... for all writes.
  • Edge ingest onto the provider backbone can help distant uploads; benchmark versus direct PUT.
  • Range GET enables parallel partial downloads for video players and big archives.
  • Push-down filters over row or column files return fewer bytes than whole objects.

Migrating a bad prefix safely: dual-write new objects to the hashed layout, backfill old keys in batches, keep a redirect map from old key to new key, then expire old keys after readers and caches have moved. That avoids a risky big-bang rename, which is usually copy plus delete for every object.

Event-driven pipelines

An object-created notification means processing can begin, not that the video is already playable. Give each processing job a stable object version and transformation identifier, then make repeated notifications harmless. A worker writes derivatives to new keys and updates metadata to ready only when the required outputs exist. Failed jobs remain visible for retry or repair. Periodic reconciliation can find uploaded objects whose processing event was delayed or whose workflow stalled.

Object notifications fan out to queues or functions for thumbnails, virus scan, or warehouse ingestion, which decouples producers from processors. Pair with dead-letter queues and idempotent handlers: events can duplicate.

Upload completes→Storage event→Queue / bus
↓
Workers→Derived assets + metadata DB

CDN in front

Serve public reads from an edge cache with restricted origin access; signed URLs or cookies protect paid streams. The origin stays private.

Client→CDN edge→ miss →Private origin
  • High cache hit ratios materially reduce origin GET cost and origin load.
  • Origin access policies keep buckets non-public so only the edge can fetch.
  • Modern edges can resize or transcode images on the fly.

When the store is fine and the product still fails

Object storage is durable for bits, but product availability still depends on , the , signing, and processing pipelines. Plan what users get when those fail, then drill the restores that marketing "nines" do not cover.

Degraded modes

  • Origin or region slow: serve CDN-cached public media, lengthen signed-URL time-to-live (TTL) carefully, and pause non-critical rather than retry storms.
  • Metadata DB unavailable: bytes may still exist, but ownership and ACL lookups fail. Degrade to read-known public objects only; block new uploads that need durable metadata commits.
  • Processing pipeline down: accept originals into a durable inbox, show "processing" states, and avoid deleting source objects until derivatives succeed.
  • Accidental delete or bad : versioning and object-lock windows matter. Without restore drills, "eleven nines" marketing does not save a wrong retention rule.

What to watch and drill

  • Quarterly restore: sample objects from version or replica; verify checksum and ACL.
  • Cold-cache drill: force a CDN miss burst and confirm origin request limits hold.
  • Separate durability of bytes from availability of the metadata and delivery path.

Still fails when…

  • A LIST misses a completed PUT, so a pipeline that polls prefixes never starts work.
  • A folder rename is copy-plus-delete and leaves half the keys behind mid-failure.
  • Billions of 1 KB objects drown the catalog long before disk fills (see Numbers to know).

Trade-offs

  • Triple copies are simple; erasure coding saves bytes and costs encode plus fan-out reads.
  • Cold tiers cut storage dollars and add retrieval latency plus per-GB restore fees.
  • Cross-region copies buy DR and residency, and roughly double storage plus egress.

Wrap-up

You have seen the upload path, the capacity worksheet, and the ways the product still fails when the bucket is healthy. Use these exercises to apply the same numbers, then follow the primary sources.

Exercises

  1. Photos. Using the 1M-user profile-photo figures in Numbers to know, place ownership in a metadata DB, derive thumbnails async, and put a in front. What do you expire after the restore window?
  2. Videos. Using the daily ingest and peak-hour rates in Numbers to know, sketch , the processing pipeline, segmented objects, and signed delivery for private playback.
  3. Logs. Using the event-volume line in Numbers to know, roll events into time partitions, compress for analytics, and apply the 7–30-day / 13-month / seven-year lifecycle already listed.

Topic-specific references

Key takeaway

Put unstructured blobs in with IAM-scoped buckets, presigned client uploads, lifecycle tiers, and a CDN for read-heavy media. Keep ownership in a metadata DB, process derivatives from events, and treat restore drills as part of durability, not a footnote to the nines.

Practice and further reading

Use these exercises to connect the lesson to a design decision. State your assumptions, trace what happens to one request, and explain the rule that must remain true even during a failure. That rule is the design's correctness invariant.

Focus for this lesson: Design object upload, metadata, multipart cleanup, checksums, versioning, retention, authorization, lifecycle, restore time, and egress cost.

Explain the decision

Why is an S3 ETag not always a content MD5?

Read the reasoning

Multipart uploads and encryption can change ETag semantics; store and verify an explicit supported checksum when integrity matters.

Apply it to a production scenario

Clients complete object upload but the metadata transaction fails. What cleans up?

Read one approach

Record upload intent, finalize metadata idempotently, and lifecycle-abort or garbage-collect unreferenced objects after a safe grace period.

Work through the numbers

Storing 4 PB with three logical copies and 2% monthly restore reads implies what raw copy footprint and restore volume?

Read the worked calculation

12 PB of copy footprint and 80 TB/month restored from logical data, before erasure coding, versioning, and metadata.

Reason through a failure

What must a legal-hold delete path preserve?

Read the recovery reasoning

No actor can bypass retention/object-lock policy; metadata deletion cannot make protected bytes irrecoverable before policy allows it.