skip to content

Two stores both guarantee that one operation on one entry is atomic - how does run-to-completion execution deliver that, and how does per-entry locking?

level: middleimportance: must knowfreq 60%

answer

  1. one promise, two routes
  2. schedule versus lock
  3. no other operation executes, or entry held
  4. cores help one model, not the other
  5. difference is who waits, not what is promised

basics

~10 s

Run-to-completion executes one operation fully before starting the next, so nothing interleaves. Per-entry locking lets worker threads run concurrently, each holding the entry it operates on for that operation's duration. Same promise, different consequences.

solid answer

~50 s

Two designs reach one guarantee. Under **one-operation-at-a-time execution (run-to-completion)**, the server finishes an operation before starting the next, so atomicity needs no locking at all - it falls out of the schedule. Under **per-entry locking**, several worker threads execute operations concurrently and each takes a lock on the entry it touches (sometimes on a stripe of entries) for the duration of one operation; atomicity is bought, not free. A caller cannot tell the two apart from the guarantee alone - both promise that one operation is applied whole and never observed half-done. They differ in what follows: under the first, one long operation makes every caller wait, whatever entry they wanted; under the second, only callers contending for that entry (or its stripe) wait, but one hot entry becomes a serialization point that extra threads do not relieve.

go deeper

for a junior

Recall that the free guarantee covers one operation, and that stores reach it in more than one way - by executing operations one at a time, or by locking the entry while a worker thread operates on it.

for a middle

Explain both mechanisms in your own words and name what is identical (the per-operation promise) versus what differs (who waits behind a long operation, whether extra cores help, the granularity of a lock).

for a senior

Show that you ask which model you are on before predicting behaviour, and reason about a hot entry collapsing both models onto the same serialization point while a thinly spread keyspace does not.

for a principal

Treat the model as an input to failure-domain planning: whether contention shows up as one server queueing or as specific entries stalling changes what you can isolate, what an operator can see, and whether scaling the box helps at all.

## Two designs, one promise Stores in this class converge on the same advertised behaviour - *one operation against one entry is applied as a whole, and no other caller observes it partway* - by two quite different routes. Knowing which route a store took is what lets you predict its behaviour under load; assuming every store took the first is the classic recitation that an interviewer is listening for. ## How run-to-completion delivers it In **one-operation-at-a-time execution**, the server runs one operation to completion before starting the next. Atomicity is then not a mechanism at all, it is an absence: there is no other operation executing that could observe or disturb an in-flight one, so nothing needs to be locked and no lock can be contended. Two things are worth stating precisely, because both are commonly overclaimed: - The serialization is of **operation execution**, not of the whole process. Stores built this way commonly use other threads for network handling, for writing copies out, or for reclaiming memory. "One operation at a time" describes the execution stream, not a thread count. - Because the schedule is the mechanism, **the model scales by clock speed, not by cores**: adding cores gives the store somewhere to put its housekeeping, but the operation stream stays one stream. ## How per-entry locking delivers it In **per-entry locking**, several worker threads pull operations and execute them concurrently. Before touching an entry, a thread takes a lock on it - and here designs vary in an important way: the lock may be on the individual entry, or on a stripe, bucket or partition of the keyspace into which many entries hash. The lock is held for the duration of that one operation and released when it ends. The delivered guarantee is identical from the caller's side. What is different is that atomicity here has a *cost* - lock acquisition and release on every operation - and a *granularity*: on a striped implementation, two callers touching two unrelated entries can still collide if those entries share a stripe. ## What actually differs | Question | Run-to-completion | Per-entry locking | |---|---|---| | What makes one operation atomic | No other operation executes during it | The entry (or its stripe) is held for its duration | | Who waits behind a long operation | Every caller, whatever entry they wanted | Callers touching that entry or its stripe | | Effect of adding cores | Operation stream stays one stream; cores go to networking and housekeeping | More operations execute at once, until they collide on one entry | | An operation touching several entries on one node | Atomic - nothing can interleave with it | Atomic only where the implementation holds every needed lock together for the operation | | Where contention becomes visible | As queueing in front of the whole server | As waiting on specific hot entries | | Cost of the guarantee itself | None - it is the schedule | Lock acquire and release on every operation | ## What does not differ The list of things both models leave to you is the same, and it is the more important half of the answer: - **Two of your operations are still two operations.** Neither model protects the span between a caller's read and its write; both will execute other callers' operations in that span. - **Neither offers an isolation level, a rollback or a save point.** The unit is the operation, and there is no transaction to open. - **Neither extends across nodes.** Atomicity is a property of one node's keyspace; where the keyspace is split, an operation spanning entries on different nodes is not one unit at all. - **Neither says anything about survival.** An operation applied atomically on a node is not, by that fact, an effect that outlives the node. ## What this means in an interview The weak answer is "the store is single-threaded, so everything is atomic" - it names one design as if it were the class, and it makes two opposite errors at once: it makes head-of-line blocking sound universal, and it makes per-entry contention invisible. The strong answer names both routes, says the guarantee they deliver is the same, and then puts the difference where it actually lives - in *who waits* and *at what granularity contention appears*, not in what a caller is promised. A useful follow-through, if you are asked which you would rather have: they are not ranked. A workload spread thinly over a very large keyspace benefits from concurrent workers; a workload that hammers a handful of entries collapses onto the same serialization either way, and then the only thing that matters is how long each individual operation takes.

  • Does per-entry locking make a store less atomic than a run-to-completion one?
    No. Both deliver the same per-operation guarantee: the operation is applied whole and never observed partway. The difference is granularity and blast radius - what a long operation costs callers who wanted a different entry, and whether two unrelated entries can collide because they share a lock stripe.
  • Does run-to-completion mean the server process has exactly one thread?
    No, and conflating the two is a common error. It means operation execution is one stream. Such stores commonly run other threads for network handling, for writing copies out, or for reclaiming memory, which is why their own documentation about threads can read as contradictory to someone who assumed a single thread.
  • Under per-entry locking, is an operation that touches several entries at once still atomic?
    Only where the implementation holds every lock it needs for the whole operation, and only for entries on that node. Under run-to-completion it is atomic by default, because nothing else can execute. This is exactly the sort of claim that is true of one store and false of another, so it is worth asking rather than assuming.

saying these in an interview costs you the question

  • Says every store in this class runs one operation at a time.
  • Treats per-entry locking as a weaker atomicity guarantee.
  • Equates one-operation-at-a-time execution with a single-threaded process.
  • Expects extra worker threads to relieve contention on one hot entry.
  • Assumes a lock covers exactly one entry, never a stripe of them.
  • Claims either model protects a caller's read-then-write sequence.