What happens when a server-side increment lands on a key that does not exist, or on a value that is not a number?
answer
- the first change is the special case
- create at zero, or refuse
- initialize-then-increment is a race
- missing and reset look identical
basics
~20 sStores differ: many create the entry at zero and apply the change, so no initializing write is needed; others refuse. A value that is not a number fails when the increment arrives, not when it was written.
solid answer
~50 sThe first change is the special case. Many stores treat a missing key as zero, create the entry and apply the change, which is more than a convenience: it removes the initialize-then-increment step, and that step is the lost change in another costume — two callers both find the key missing, both write zero, and one of their counts is wiped. Stores that instead refuse on a missing key force you to create the entry first, and that create must be conditional so it cannot clobber a concurrent caller's work. A value that is not a number — padded, or written whole by another code path — makes the arithmetic fail at change time rather than being silently reinterpreted, which is the behaviour you want. On a volatile tier, "missing" is also the normal state after an eviction or a restart, so a counter can quietly begin again from zero.
go deeper
Recall that many stores treat a missing counter key as zero and create it on the first change, so no separate setup write is needed, and that an unparseable value makes the operation fail.
Explain why create-at-zero matters: the initialize-then-increment sequence it removes is itself a race in which one caller's count is overwritten. Say that a conditional create is the fix where it is needed.
Bring in the tier. Missing is also the state after eviction, expiry and restart, so on a create-at-zero store a lost counter simply looks like a small one, and no error reaches anybody.
Rule on ownership of the key: a counter key is written only by code that treats it as a counter, and any count that carries a decision states what it means for that number to be low or absent.
## Why the first change is the interesting one A counter spends almost all of its life in the easy case: the entry exists, it holds a number, and the change is applied. The two moments worth an interview question are the first change against a key that has never been written, and the change that lands on something that is not a number. Both are where designs quietly go wrong, and both are places where stores in this class genuinely differ. ## The key that does not exist yet There are two behaviours in the wild, and naming both is most of the answer: - **Treat the missing key as zero.** The store creates the entry, applies the amount, and hands back the result. The caller writes no initialization code at all, and the first change is not special. - **Refuse the operation.** The store answers that there is nothing to change, and the caller has to create the entry before it can count with it. The first behaviour is worth more than the keystrokes it saves. On a store that refuses, the obvious code is: 1. Try the change; it fails because the key is absent. 2. Write the starting value. 3. Try the change again. Step 2 is a whole-value write, and whole-value writes are unconditional. Two callers arriving together both fail at step 1, both write the starting value at step 2, and the one that got there first has its count erased by the other — **the same lost change the server-side increment existed to prevent, reintroduced in the initialization path**. The repair is a conditional create — a write that takes effect only if the key is absent — followed by the ordinary change, with the arrival order no longer mattering. Which conditional writes a store offers, and under what names, varies; that the create must be conditional does not. ## The value that is not a number The arithmetic has to interpret whatever is stored. Stores hold the number in some concrete representation, and the operation parses it. So a value can be present and still not be usable: - it was written whole by a different code path, as part of the shared value format rather than as a bare number; - it carries padding, a sign convention or a unit that the arithmetic does not expect; - it is a serialized object that happens to describe a count. The usual answer from the store is a **failure on the operation**, not a silent reinterpretation, and that is the behaviour to want. A store that quietly treated an unparseable value as zero would destroy data on every increment and hide the bug that caused it. Two consequences follow for design: - The failure surfaces at **increment time**, arbitrarily far from the write that caused it — often in a different service, on a different deploy. The error message is the only link back. - A counter's key should be **written only by code paths that treat it as a counter**. Mixing a whole-value write of a serialized structure into the same key as an arithmetic change is a defect that is invisible until traffic hits it. ## The volatile-tier twist "The key does not exist" is not only a first-run condition on this tier. It is also the state after an eviction under memory pressure, after a lifetime expires, and after a restart that came back empty. On a store that treats a missing key as zero, all of that reads as a counter that began again — no error, no signal, just a small number where a large one was. | Situation | What a reader sees | What it actually means | |---|---|---| | Nothing has been counted yet | no entry | genuinely zero | | The entry's lifetime ended | no entry | the period reset as designed | | The entry was evicted under pressure | no entry | the count is wrong and nobody was told | | A restart emptied the tier | no entry | every counter reset at once | A counter whose value carries a decision therefore needs the same question asked of it as any other value on this tier: what happens if this number is smaller than it should be, or absent, and who finds out? ## Answering it well Say that stores differ on the missing key and give both behaviours. Explain why create-at-zero matters — it removes an initialization step that is itself a race. Say that a non-numeric value fails the operation rather than being coerced, and that the failure lands far from its cause. Then close on the tier: on a store that creates a missing counter at zero, an evicted counter is indistinguishable from one that was never used.
- How do you count safely on a store that refuses an increment against a missing key?Create the entry with a write that takes effect only if the key is absent, then apply the ordinary change. The conditional create is the load-bearing part: an unconditional write of the starting value will erase a concurrent caller's count. If the create reports that someone else won the race, that is a success, not an error — apply the change and continue.
- Why is a failed increment better than a coerced zero when the stored value is not a number?Because coercion is silent data loss. Reinterpreting an unparseable value as zero would overwrite whatever was there, on every subsequent change, and would hide the code path that wrote the wrong shape in the first place. A failure is loud, is attributable to one key, and leaves the original value intact for inspection.
- If a counter is missing, can the reader tell why?Not from the store. An entry that was never created, one whose lifetime ended, one evicted under memory pressure and one lost to a restart all read the same way. If the distinction matters, it has to be carried elsewhere — for example by recording when the period began alongside the count, so a reader can tell a fresh period from a hole.
saying these in an interview costs you the question
- Assumes every store creates a missing counter at zero
- Thinks an unconditional initializing write is harmless under concurrency
- Treats an increment failing on a non-numeric value as a store defect
- Believes an evicted counter returns holding its previous number
- Expects a reader to distinguish a reset counter from an unused one