skip to content

How does holding a number written 20,000 times a second differ from holding one read 20,000 times a second?

level: middleimportance: nice to knowfreq 36%

answer

  1. the ratio is the design input
  2. the server cannot merge what it never received
  3. accumulate in the caller, apply once
  4. staleness and a wider loss window
  5. reads never lose a change

basics

~20 s

A write-heavy number costs one round trip per change, so savings come from the caller accumulating a delta and applying it as one addition, paying staleness. A read-heavy number is cheap; the question becomes where it lives.

solid answer

~50 s

The two traffic shapes stress different things. A number changed 20,000 times a second costs 20,000 round trips and 20,000 units of server work attributed to one entry, and the server cannot merge changes it never received — so the only reduction available without changing the shape is in the caller: accumulate a delta in the process and apply it as one addition by that amount every so often. That trades exactness in time and a wider loss window — the un-applied delta lives in one process's memory and dies with it — for round trips. A number read 20,000 times a second and changed rarely has none of those problems: reads do not disturb it and cannot lose a change. The interesting question there is whether it deserves its own entry at all, or whether it belongs inside a value the readers already fetch.

go deeper

for a junior

Recall that every change to a stored number is its own round trip, and that reading a number does not change it — so heavy reads and heavy writes are not the same kind of load.

for a middle

Explain the caller-side lever: accumulate a delta locally and apply it as one addition by an amount, and state both prices — bounded staleness and a delta that only lives in one process's memory.

for a senior

Drive the choice from the requirement. Decide whether the number decorates or gates, because a gating number cannot be behind, and that forbids the cheap design outright.

for a principal

Set the contract before the shape: state the accuracy and the freshness the number owes its readers, so teams are not free to invent a batching interval that quietly turns an exact count into a lower bound.

## One number, two very different problems This is the one place in this category where the read-to-write ratio on a single key is the subject rather than a detail. A stored number is cheap in memory and simple in shape, so the design pressure comes entirely from traffic, and the two directions pull opposite ways. ## The write-heavy number Every change is a separate operation: a round trip, and a unit of server work on one entry. The server sees only what arrives, so it has nothing to coalesce — twenty thousand changes a second are twenty thousand pieces of work no matter how small each one is. The lever that exists at this shape is on the caller's side, and it depends on the counter primitive offering **addition by an amount**, not only a step of one: 1. Each process keeps its own running delta in memory. 2. Every interval, or every fixed number of events, it applies that delta as a **single** change and resets its local total to zero. 3. Twenty thousand changes a second across the fleet become a handful of operations a second, and the stored number is still exact once every writer has flushed. What that costs is worth naming precisely, because it is the trade being graded: - **Staleness with a bound.** Every reader is behind by up to one flush interval, plus whatever has accumulated since. - **A wider loss window.** The un-applied delta lives only in one process's memory. The tier was already volatile; now part of the count is somewhere even less durable than the tier, and a process that exits ungracefully takes its delta with it. - **Nothing can act per event any more.** Crossing a threshold is observed at flush granularity, so anything that had to react to the exact moment a total was reached no longer can. Beyond that lever, the remaining options change the shape itself — more than one entry holding parts of the number — and that turns every read into several reads plus a sum, which is a different design with its own placement questions. ## The read-heavy number A number read constantly and changed rarely is the easy case, and saying so is part of a good answer. Reads do not disturb the value, cannot lose a change, and cost one small reply each. What remains is a modelling question: - If the readers already fetch a larger value in the same request, the number may belong **inside that value** — as one named field where the server can address fields, or as part of the shared value format where it cannot — so the request makes one round trip instead of two. - The moment it lives inside an opaque value, changing it costs a read-modify-write round trip again, with the lost-change window back. That is acceptable at one change a minute and unacceptable at a thousand. - If the number is read far more often than the underlying truth changes, ask whether the readers need the live number at all, or a figure that is a few seconds old — because that answer decides whether the caller-side coalescing above is available to you. | | write-heavy, rarely read | read-heavy, rarely changed | |---|---|---| | Dominant cost | one round trip and one unit of work per change | one small reply per read | | Available lever | accumulate in the caller, apply as one addition | co-locate the number with a value already being read | | What it costs | bounded staleness and a wider loss window | a read-modify-write round trip on each change | | Concurrency exposure | high: every writer is on the same entry | low: readers cannot lose each other's work | ## The question behind both Both branches come back to the same thing: **how exact does this number have to be, and at what instant?** A count that decorates a page tolerates being a few seconds behind, which unlocks the cheap design. A count that gates an action does not, which forces every change through the server one at a time and makes the write rate a real constraint rather than an accounting detail. Ask which one it is before choosing the shape, because the shape cannot be changed later without changing what the number means. And keep the tier in view. On a volatile tier this number can vanish entirely; a design that tolerates being a few seconds behind usually also tolerates starting again, while one that cannot tolerate either is telling you it does not belong here alone.

  • What makes caller-side accumulation possible at all?
    That the primitive adds an arbitrary amount, not just a step of one. Applying a delta of 3,482 as a single change is the same operation and the same cost as adding one, so an interval's worth of local counting collapses into one round trip. Without addition by an amount there is nothing to collapse into, and the caller would be back to one operation per event.
  • Where does the un-applied delta actually live, and what does that mean for the number's meaning?
    In one process's memory, unreplicated and unrecoverable. So the stored number becomes a lower bound on the true count rather than the count, until every writer has flushed. That is fine for something displayed and wrong for anything that must not under-count, which is the honest line to draw when proposing the technique.
  • When should a read-heavy number not get its own entry?
    When every reader is already fetching a larger value in the same request. Folding the number into that value removes a round trip per read. The price is paid on the write side: if the server cannot address parts of that value, each change becomes a whole-value read, an edit and a whole-value write, so this only pays while changes stay rare.

saying these in an interview costs you the question

  • Assumes the store coalesces rapid changes to the same entry
  • Proposes caller-side batching without naming the loss window
  • Thinks heavy read traffic can lose a change the way writes do
  • Treats a displayed count and a gating count as the same requirement
  • Believes a small value cannot be a bottleneck because it is small