Q: Object storage (S3) — when, what guarantees, gotchas.

Answer:

S3-like object storage is the cheapest, most durable way to keep blobs at scale. It's not a filesystem — it's an immutable key-value store with HTTP API.

What It Is

Bucket: my-bucket
  Key: photos/2025/04/img1.jpg → bytes
  Key: photos/2025/04/img2.jpg → bytes
  Key: backups/db_20250401.sql.gz → bytes

Flat namespace. Slashes in keys are just characters; folder prefixes are a convention.

Properties

  • Durability: 11 9s (S3 standard). Multiple copies across AZs.
  • Availability: 99.99% (S3 standard).
  • Strong read-after-write consistency (since Dec 2020 for S3).
  • Cost: cheap storage (~$23/TB/month standard), per-request fees, egress is expensive.
  • Throughput: 3,500 PUT/copy/delete and 5,500 GET per second per partition prefix.

Storage Classes

ClassUseCost
StandardHot, frequent access$$$
Intelligent-TieringAuto-move based on access$$
Standard-IAInfrequent access$
Glacier InstantArchive, immediate retrieve$
Glacier FlexibleArchive, minutes to hours retrieve¢
Glacier Deep ArchiveArchive, hours to retrieve¢¢

Move data via lifecycle policies (e.g., "after 30 days → IA, after 1 year → Glacier").

What Object Storage Is GOOD For

  • Large files (photos, videos, backups).
  • Static website assets.
  • Data lake (Parquet, ORC) for analytics.
  • Logs / archives.
  • ML training data sets.
  • Software releases / installer files.

What It's BAD For

  • Frequent updates to the same object (no in-place modify; full rewrite).
  • Tiny files (per-request overhead dwarfs payload).
  • Low-latency reads (~10–100 ms per GET vs microseconds for Redis).
  • Filesystem semantics (no rename — just copy + delete).

Multipart Upload

Large files split into parts (5 MB – 5 GB each):

1. Initiate multipart upload → get UploadId.
2. Upload parts in parallel.
3. Complete (with list of parts + ETags).

Resume: re-upload only failed parts. Cap: 10,000 parts → max object 5 TB.

Presigned URLs

Time-limited URL granting access without sharing credentials:

url = s3.generate_presigned_url('put_object',
    Params={'Bucket': 'b', 'Key': 'k'}, ExpiresIn=300)
# client uploads directly to S3 with this URL

Use for direct browser uploads (skip your backend); secure media delivery.

Versioning

Enable on bucket → every PUT creates a version. Delete = adds a "delete marker"; old versions remain until explicitly removed.

Protects against accidental overwrites/deletes. Cost: storage of all versions.

Object Lock / Compliance

Write-Once-Read-Many. Object can't be deleted for a retention period (governance or compliance mode).

Used for audit logs, regulatory archives, ransomware protection.

Cross-Region Replication

Bucket-level: every object replicated to another region (sync or async).

Used for DR or for serving traffic close to users.

Access Patterns

Read patterns:

  • Direct from client via presigned URL (most common).
  • Through CDN (CloudFront) for caching + edge.
  • From backend (analytics jobs).

Write patterns:

  • Direct from client via presigned URL.
  • From backend (uploads, ETL).

For browser apps: presigned URL + CDN. Backend rarely intermediates the bytes.

Cost Optimization

  • Lifecycle policies → cold tier old data.
  • Intelligent-Tiering for unpredictable access.
  • Egress is the most expensive line — use CloudFront / Cloudflare R2 for free egress.
  • Avoid LIST over millions of keys; use S3 Inventory reports.

Eventual vs Strong Consistency

Pre-Dec-2020 S3 was eventually consistent for reads after writes. As of now: strong read-after-write consistency for all operations.

You can read your write immediately. Same for delete.

Common Pitfalls

PitfallFix
Many tiny filesPack into bigger objects (e.g., tar/parquet)
Hot prefix at scaleSpread by hashing key prefix (a1b2c3/... vs 2025/04/...)
Storing PII unencryptedEnable SSE-S3 or SSE-KMS by default
Public bucket leakBlock Public Access + bucket policies + IAM
Long polling for new filesUse S3 event notifications (SNS / SQS / Lambda)
LIST to find filesS3 is KV; maintain own index if you need search

S3 Event Notifications

PUT object in bucket → SNS / SQS / Lambda trigger

Common patterns:

  • Trigger image-resize on upload.
  • Index new files into Elasticsearch.
  • Notify users of new content.

Multi-Region Architecture

client uploads → nearest region's bucket
            │
            ▼ async replicate
       other region buckets

Read from nearest region; cross-region reads possible but slower.

Common Mistakes

MistakeReality
Treating S3 as filesystemNo rename, no folder ops; just keys
Synchronous PUT for every eventBatch into bigger objects
Public bucket "for convenience"Data breaches
Not setting lifecycle policyStorage bill grows forever
Logging every requestCost; use CloudTrail or aggregate

[!NOTE] S3 is the cheapest hard drive on earth. Architect around its strengths (immutable, durable, KV) and don't fight its weaknesses (latency, mutations).

Interview Follow-ups

  • "How would you build a CDN-fronted bucket for video?" — CloudFront in front; signed URLs; bucket private; signed Cookie for whole-folder access.
  • "How to manage millions of small files?" — Pack into parquet/tar; store index in DB; reduces S3 request cost dramatically.
  • "S3 vs DynamoDB?" — S3 = large blobs, infrequent access pattern, eventual-now-strong consistency. DynamoDB = small structured records, milliseconds, transactions.