System Design

Patterns

Tiered Storage

Read probability collapses with age. Your storage bill does not, unless you make it. Move data down a ladder of storage classes as it cools, and price the move against what it will cost you to get the data back.

The shape

Most data is read almost entirely in the first hours of its life, then almost never. A photo, an order, a log line, a message: the access curve drops by orders of magnitude within days and then flattens into a long tail that never quite reaches zero.

Storage prices do not follow that curve on their own. By default everything sits in the class you wrote it to, so you pay hot prices forever for bytes whose read probability approached zero last March.

Tiered storage is the decision to move data down a ladder of storage classes as it cools. Each step down is cheaper per byte and worse in some other dimension, usually retrieval latency, retrieval cost, or minimum retention. The pattern is choosing the steps, choosing when something moves, and saying plainly what the move takes away.

Some examples:

  • Keep the original upload, or only what you serve? A storage class question dressed as a product question.
  • How long do logs stay searchable? A hot search index such as Elasticsearch, then object storage such as S3, then an archive class such as Glacier.
  • Do old orders stay in the operational database? Moving them out is tiering; the "cold" tier is a warehouse.

What unifies them: you are not deciding whether to keep the data. You are deciding what you are willing to pay to get it back.

The mechanism

Three things define a tier: cost per byte, time to first byte, and any minimum you are charged regardless of actual use.

The curve on the left is the only justification for the ladder on the right. Every step below the top is bought with retrieval time you did not have to pay before.

The move itself is usually a lifecycle rule the storage provider runs for you, keyed on object age. That is the easy part and it is why this pattern gets adopted casually. The parts that need thought:

The threshold should come from the access curve, not from a round number. Measure reads by object age. Then, for each age, compare two numbers. One is what you save by moving an object of that age down a step. The other is what you pay in retrieval fees to read it from down there. The age where the saving overtakes the retrieval cost is your threshold, and it is a calculation rather than a matter of taste.

Retrieval cost is per gigabyte, and it is charged on the way out. Cold tiers make storage cheap and reads expensive. A tier that is 5x cheaper to store but bills you per gigabyte on the way out is a bad trade for anything read even occasionally. The answer flips with read frequency, so a tier that is right for one class of object can be wrong for another sitting next to it.

Minimum retention is billed whether you use it or not. Cold classes typically bill a minimum number of days whether or not the object survives that long. Move something down and delete it a week later and you pay for the full minimum anyway, so tiering short-lived data actively loses money.

Back of envelopeWhen moving down actually saves money
A photo service keeping every original, 3 MB each:

  new objects per day                            100M
  bytes per day    100M x 3 MB               = 300 TB
  bytes per year   300 TB x 365              = 109.5 PB

Two options for those bytes:

  hot storage                    ~5x the price of archive
  archive storage                 1x, plus a retrieval fee
                                     and a minimum retention

Storing them for a year:
  all hot                        =  5 units of cost
  all archive                    =  1 unit  + retrievals

Break-even on the retrieval fee alone:
  if you retrieve X% of it per year, the archive
  saving of 4 units is gone once

     X x (retrieval fee) > 4 x (archive storage cost)

Data that is read essentially never keeps almost all of
that saving, so archive wins by roughly 5x.

Data that is read even 20% a year may not. Get the
retrieval fee from your provider and do this division
before writing the lifecycle rule.

The decisive question is not "how big is it" but
"what fraction of it gets read back, per year."

What the steps are actually called

The ladder is generic and every provider names it differently, so here is the same ladder with real products on it, hottest first.

stepwhat it is calledwhat you get
device cachethe browser's HTTP cache, an app's own local storageinstant and free, and you cannot take it back once it is there
edge cacheCloudflare, Fastly, Akamai, CloudFronta copy near the user, evicted on its own once it goes cold
memory cacheRedis, Memcached, ElastiCache, Memorystorethe fastest reads and the highest cost per byte, gone on restart
hot object storageS3 Standard, Cloud Storage Standard, Azure Blob Hotmilliseconds, no retrieval fee, and the class you get by default
infrequent accessS3 Standard-IA, Cloud Storage Nearline, Azure Coolas fast as hot and cheaper to keep, but you pay per GB to read
coldCloud Storage Coldline, Azure Coldcheaper again, with a longer minimum billing period
archive, instant readsS3 Glacier Instant Retrievalarchive prices with millisecond reads, at the highest retrieval fee
archive, with a waitS3 Glacier Flexible Retrieval, Cloud Storage Archive, Azure Archiveyou ask for the data and it arrives minutes to hours later
deep archiveS3 Glacier Deep Archivethe cheapest per byte anywhere, and the wait runs to hours
tape and offlineLTO cartridges in a vaultdays, and a person has to go and fetch it

The same ladder exists outside object storage. Elasticsearch and OpenSearch have hot, warm, cold and frozen data tiers, and the frozen one reads straight from object storage. For an operational database the cold step is usually a warehouse, Snowflake or BigQuery or Redshift, loaded from the database and queried instead of it.

Two things to look up before picking any of these, because they differ by provider and change over time. One is the retrieval fee per gigabyte. The other is the minimum number of days you are billed for, whether the object lives that long or not. Neither appears in the headline price per gigabyte, and together they decide the arithmetic above.

How to tell you have this problem

Your storage bill grows linearly with age while your traffic does not. Draw two charts over the same period, one of bytes stored and one of bytes served. Diverging lines mean you are accumulating cold data at hot prices.

Reads drop sharply as objects get older. Instrument retrievals with the object's creation date and bucket them. A steep drop is the signal that a ladder would pay; a flat line is the signal that it would not.

Nobody can say what would break if old data disappeared. That question has an answer, and finding it usually reveals that the data is kept for a specific, rare event: a re-encoding campaign, a legal request, a backfill. Those events tolerate slow retrieval, which is exactly what the cold tier charges you less for.

What it costs

Restore is a path you have never tested. The reason to keep cold data is a rare event, so the retrieval path runs rarely, which means it is broken more often than anyone expects. Restoring petabytes has a rate limit, a cost, and a duration measured in days. Test it on a schedule, or you do not actually know whether the data is still there.

Latency becomes bimodal, and callers are not ready. A read that is usually 20 ms and occasionally four hours cannot sit behind the same interface without the caller knowing. Either the API becomes asynchronous, returning a job, or somebody's timeout produces an incident.

Lifecycle rules act on age, but your reason is access. Age is a proxy. An old object that is suddenly popular gets demoted anyway and every read pays retrieval. Access-based tiering classes exist to fix this and cost a per-object monitoring fee, which only pays for itself on objects big enough that the storage saving beats it.

Deletion gets more expensive to reason about. With the same data spread across the tiers plus backups, "delete this user's data" now means deleting it in several places with different consistency and different timelines, and proving it under a legal clock.

Where it shows up in the course

articlethe form it takes there
Choosing a Databasethe restore-you-never-rehearse problem, and where retention belongs once a data store outgrows one tier
Instagramoriginals are 9x the bytes of everything served and are read essentially never, so they go cold while derivatives stay hot
URL Shortenerraw click events at 3 TB a day kept 90 days, against rollups small enough to keep forever
YouTubesource files at 2.6 PB a day, read essentially never after the first hour and impossible to recreate

Getting it wrong

Tiering by age when the driver is access. Age is a proxy for coldness. When it is a bad proxy you pay retrieval fees on your most popular old objects.

Ignoring minimum retention. Moving short-lived data to a cold class costs more than leaving it. Check the minimum before writing the rule.

Forgetting the retrieval fee in the comparison. The storage saving is advertised; the retrieval charge is what decides how much of it you actually keep.

Never testing restore. Cold data you cannot get back on a schedule you can live with is not a backup or an archive. You are paying to store something you cannot use.

Exposing a bimodal read through a synchronous API. Make the slow path explicit. A caller that cannot tell the difference will discover it during an incident.

Tiering because there is a lot of data. Size is not the trigger. The drop in reads with age is the trigger, and a large pile of data that is read evenly should stay where it is.

Spot a mistake, or something unclear? . Or get in touch.

Article

Send feedback

Thank you for helping. We read every report and fix what is wrong.

How do you feel about oneby7?

Sign in to oneby7

Keep your progress, your streak and your code, on any device.

New here? This creates your account too.