study

Storage

Object storage, tiering, the upload pattern

The problem

Not all data is rows. Images, videos, backups, and logs need homes optimized for big dumb bytes — and the two homes that are always available (a database BLOB column, the app server's disk) are both famously wrong. The database stores rows in pages sized for kilobyte records: blobs bloat every backup, replica, and cache page, and each read hogs a connection. The app server's disk makes that server special — the sticky-session disease, photo edition.

Object storage — the bytes barn

S3 (and siblings GCS, Azure Blob): give it a key, it stores the bytes; give the key back, it returns them over HTTP. That's the whole API. In exchange: effectively unlimited capacity, very cheap, and "eleven nines" durable. The other two species, for completeness: block storage is a virtual hard drive bolted to one server (the fast disk your database sits on); file systems (NFS) are shared folders, mostly legacy and ML artifacts. Object storage wins everything else.

The pattern that answers 95% of upload questions

Three sentences: the file goes to object storage. The database stores only metadata — owner, size, and the S3 URL. A CDN sits in front, so viewers hit an edge, not your origin. Say it exactly like that in an interview and the topic is handled.

Hot, warm, cold

Today's uploads get hammered; a 2014 photo nobody opens still must never be lost. Storage classes ladder the price: hot (standard, fast, priciest) → warm (infrequent access) → cold (Glacier archival, ~20× cheaper, minutes-to-hours retrieval). The mover is dumber than you'd guess: lifecycle policies — per-object age/access rules ("untouched 90 days → warm; a year → cold"), evaluated automatically across billions of objects. Mentioning lifecycle policies when a design stores media forever is the operational-cost-awareness box, checked.

The orphan problem

An upload is two writes into two systems that can't share a transaction: bytes into S3, metadata into the database. A crash between them leaves either a DB row pointing at nothing (user sees a broken image — the bad one) or an unreferenced S3 object (invisible; wasted money — the tolerable one). Two defenses, layered: order the writes so failure lands soft (upload bytes first, DB row last — now only the harmless orphan is reachable), and a background garbage-collection job that reaps unreferenced objects past a grace window. The belt-and-suspenders version — DB row as status=pending, flip to active after upload, GC reaps stale pendings — is the transactional upload pattern.