Managing Reads and Consistency
In one line: this lesson contains the design's central concession — the counter is never exactly current — and, more usefully, the argument that at this scale being exactly current is meaningless anyway.
Periodic aggregation
Deciding when to aggregate shard values is critical. With high write traffic, returning a perfectly current value is often impractical because the value may change immediately after it's read. Instead, the system periodically aggregates shard values and caches the result. Reducing the aggregation interval increases accuracy.
The justification is that a perfectly current value is not meaningful — not merely that it is expensive
This is the sentence to internalize: "returning a perfectly current value is often impractical because the value may change immediately after it's read."
That is a stronger claim than "summing is slow." It says the exact value is not a useful concept for a counter under heavy write load.
Consider a tweet receiving 10,000 likes per second. Suppose you paid the full cost of locking every shard and computing an exact total. By the time that number reaches the user's screen — a few hundred milliseconds of network and rendering — it is already wrong by thousands.
So the choice is not between accurate and approximate. It is between approximate-and-cheap and approximate-and-expensive. Once framed that way, periodic aggregation is obviously correct.
This is the same reasoning distributed search used for staleness: find the guarantee your domain can afford to weaken, and here exactness is not merely affordable to lose, it is unobtainable.
The aggregation interval is a freshness-versus-cost dial
"Reducing the aggregation interval increases accuracy." That is one parameter controlling the whole read path:
- Short interval → fresher counts, more aggregation work, more load on shards from the aggregator itself.
- Long interval → cheaper, staler counts.
And the right value depends on how fast the counter is moving, not on a global preference. A tweet gaining 10,000 likes/second is stale within milliseconds no matter what you choose; a tweet gaining one like/hour is exactly correct at any interval.
So the sophisticated version is an adaptive interval: aggregate hot counters frequently and cold ones rarely. That reuses the same load signal Lesson 4 needs for dynamic resizing — one feedback mechanism driving two decisions.
Structurally this is identical to distributed search's index-refresh threshold and distributed caching's TTL: a knob trading data currency against the cost of maintaining it.
No lock across shards, and the reason is the whole point
"Should we lock all shards of a counter before accumulating their values?"
No. Reads can occur concurrently with writes without requiring an across-shards lock. This lock will decimate write performance, which was the original reason we used sharded counters. Under a relaxed consistency model, there is no need for simultaneous read locks across all shards.
However, such a mechanism might be used when the read frequency is very low.
Follow the logic, because it is airtight. Locking all shards to read would serialize writes across every shard simultaneously — reintroducing exactly the contention Lesson 1 diagnosed, now spread across N locks instead of one. You would have paid the full cost of sharding and received none of the benefit.
So the design must read without locking, which means the sum is computed from shards read at slightly different instants. The result is a value the counter passed through, not one it definitively held.
The concession at the end is a good sanity check: locking is acceptable "when the read frequency is very low" — because then the write serialization is rare enough to tolerate. The cost of exactness is proportional to how often you demand it.
The user who just liked it
Eventual consistency on a counter is usually invisible, with one glaring exception: the person who just performed the action.
A count that is a few seconds stale is unnoticeable for everyone except the user whose own action is missing from it. To them it does not read as stale, it reads as broken — so they tap again, which is both a support complaint and extra write load.
The fix is almost always client-side, not architectural: display the value the server returned plus the user's own pending action, and let the next refresh reconcile. The server stays eventually consistent and the one reader who would notice sees a consistent story.
That is the general shape worth carrying: read-your-own-writes is often cheaper to fake at the edge than to guarantee in the system. Enforcing it server-side would mean routing that user's reads to the shard holding their write, or blocking until aggregation — both expensive, for a guarantee only one reader per write needs.
Where this model does not fit
"Can you think of a use case where sharded counters with this consistency model might not be suitable?"
We might not use sharded counters with a relaxed consistency model where we need strong consistency. An example is a read-then-write scenario where we first need to get the accurate value of something before deciding to modify it — in fact, such a scenario requires transaction support.
Read-then-write is the disqualifying pattern — learn to recognize it
The rule from Lesson 2, now with a precise test: sharded counters count things; they do not control things.
The signature of a disqualifying use case is a decision made from the counter's value:
- "Decrement inventory if stock remains" — read, compare, write. Two users can both read "1 remaining" and both buy it.
- "Charge the account if the balance covers it" — same shape, worse consequences.
- "Allow the request if under the rate limit" — which is why rate limiting used sharded counters only for capacity-protection limits and kept billing quotas exact.
- "Award the badge when the count hits 1,000" — the threshold may be crossed by several shards simultaneously.
All four are read-then-write, and all four need the read and the write to be atomic — a transaction. Sharding removes exactly that atomicity.
Meanwhile likes, views, retweets, and impressions are pure fire-and-forget increments. Nobody makes a decision from the value; it is displayed. That is why they shard beautifully.
Test to apply: does anything branch on this counter's value? If yes, sharding is the wrong tool.
Key takeaway
Summing on every read is too slow, so the system aggregates periodically and caches — justified not merely by cost but because an exact value is not a meaningful concept under heavy write load. Reads take no cross-shard lock, because locking would reintroduce the write serialization sharding removed. And the disqualifying pattern is read-then-write: if anything branches on the value, you need a transaction, not a sharded counter.
Interview signal by level
| Level | What a strong answer sounds like |
|---|---|
| L4 | "Add up all the shards when someone reads the counter." |
| L5 | Caches the aggregate: "summing on every read is expensive, so we aggregate periodically and serve a cached total — accepting that it's slightly behind." |
| Staff+ | Justifies the concession and names the boundary: "the exact value isn't a meaningful concept here — a tweet gaining 10,000 likes a second is wrong by thousands before the number reaches the screen, so the choice is approximate-and-cheap versus approximate-and-expensive. We can't lock across shards to read either, because that serializes writes on every shard and undoes the whole design. The disqualifying pattern is read-then-write: decrement-if-in-stock, charge-if-sufficient-balance, award-at-threshold. If anything branches on the value, you need a transaction." |
Next: using counters to find what's trending.