skip to content

Your design wants an entry kept alive while it is being read — which side actually pushes the deadline forward, and what does that cost?

level: middleimportance: must knowfreq 60%

answer

  1. is it a setting, or your write?
  2. read does not move it on many stores
  3. one extra operation per access
  4. read-heavy path becomes write-heavy
  5. refresh after the deadline can resurrect

basics

~20 s

Usually the caller, not the store. Many stores do not move a deadline on a read, so an idle deadline is an extra write on every access, turning a read-heavy path into a write-heavy one.

solid answer

~50 s

Establish the store's posture first, because it is not the same everywhere: some stores refresh a deadline on access, some expose one call that reads the value and extends the deadline together, and many do not move a deadline on a read at all. Where the store does not do it, this is not a setting — the caller issues an **extra operation on every access**, and the costs are a round trip, write load on a path that was pure reads, and, where no operation moves a deadline without replacing the value, the whole value going back each time. There is also a race: if the deadline passes between the read and the refresh, an extend that fails on a missing entry is harmless, but a refresh implemented as a write carrying a lifetime recreates the entry from the value just read. Refreshing only when little of the window is left removes most of the write volume.

code

pseudocode · 19 lines
pseudocode
value = read(entry)

if value is missing:
    value = loadFromSourceOfTruth()
    write(entry, value, lifetime: idleWindow)
    return value

left = readRemainingLifetime(entry)

// refresh only near the end of the window, not on every access
if left is a duration and left < idleWindow / 3:
    moved = extendLifetime(entry, lifetime: idleWindow)
    if not moved:
        // the deadline passed between the read and this call: the entry is gone,
        // so reload rather than writing back the value we already read
        value = loadFromSourceOfTruth()
        write(entry, value, lifetime: idleWindow)

return value

go deeper

for a junior

Remember one fact: reading an entry does not automatically extend its deadline on every store. If you need a deadline to move, check whether the store moves it or whether your code has to.

for a middle

Explain the three implementations — refresh on access, a combined read-and-extend call, or a second operation from the caller — and count the operations each one costs per access.

for a senior

Demonstrate the production judgment: the write volume a per-access refresh adds to a read-heavy path, the resurrection race when a refresh lands after the deadline, and the threshold that removes most of the writes.

for a principal

Own the trade-off across stores. Decide whether an idle deadline is worth binding a design to at all when portability means implementing it in the caller, and what the write amplification does to the tier's capacity plan.

## The assumption hidden in the word "sliding" Asking for "a deadline that slides while the entry is in use" sounds like a configuration choice, and on some stores it is one. On many stores in this class it is not: a read is a read, and it does not touch the deadline. That makes the first move in answering this question a question of your own — **does this store refresh on access, or must the caller re-attach the deadline?** A candidate who names that fork has already shown the thing the question is testing; a candidate who says "you turn on the sliding option" has assumed one store's behaviour is the model. ## Three ways the push actually happens 1. **The store refreshes on access.** Where a store offers it, an ordinary read moves the deadline and the caller pays nothing extra. This is the cheapest shape and the least portable one, because a design built on it silently stops being an idle deadline the day it runs on a store without the feature. 2. **One call reads and extends together.** Some stores expose a single operation that returns the value and moves the deadline atomically. One round trip, no window between the two steps, and no race — but again, only where the store has it. 3. **The caller issues a second operation.** The common case: read the value, then either move the deadline or write the value back with a fresh lifetime. Two operations, and a gap between them in which anything can happen. ## What the third shape costs - **An extra operation per access.** On a path whose whole purpose was one fast read, you have doubled the operation count and added a round trip's latency unless the two are pipelined. - **Write load where there was none.** Reads and writes are not equivalent to the tier: writes are what a replicated tier propagates and what a write-log posture records. A read-heavy path that refreshes on every access is a write-heavy path wearing read clothes, and the tier's write volume now scales with read traffic. - **Whole values on the wire.** Where the store has no operation that moves a deadline without replacing the value, each refresh carries the value again. On large entries this dominates everything else. - **A hotter entry is refreshed harder.** The entries most worth keeping are the ones being refreshed most often, so the cost concentrates exactly where the traffic already is. ## The races the extra write buys - **The refresh lands after the deadline passed.** The read succeeded, then the deadline passed, then the refresh arrived. Where the refresh is an extend operation that reports failure on a missing entry, nothing bad happens and the caller can treat the failure as a miss. Where the refresh is a write carrying a lifetime, the entry is **recreated** from the value the caller read — which may already be superseded. This is the failure worth naming out loud. - **Two callers refresh at once.** Both pay for a write and both push the deadline forward; the outcome is benign, but the cost is real and doubles on hot entries. - **A refresh races a replacement.** One caller replaces the value while another refreshes the deadline for the value it read; the new value is now living under a deadline extended on the strength of the old one. ## Cutting the cost without losing the shape The standard remedy is to stop refreshing on every access and refresh only when the deadline is getting close: read the remaining lifetime, and extend only if what is left has fallen below some fraction of the window. Steady traffic then produces one write per window instead of one per access, and the entry still survives while it is in use. Two caveats: reading the remaining lifetime is itself an operation unless the store returns it with the value, and the threshold must leave enough room that a refresh cannot arrive after the deadline has already passed under normal latency. ## What good sounds like State the fork, pick the implementation the store supports, price it in operations per access rather than in words, and name the resurrection race and how you avoid it. "The store does not do this for me, so this is an extra write on every read, and here is what I did about the volume" is a stronger answer than any configuration name.

  • Refreshing only when little of the window is left saves writes — what does it give up?
    Precision. The deadline no longer tracks the last access exactly; an entry can lapse up to the threshold's worth of time earlier than a per-access refresh would have allowed. The threshold also has to exceed normal latency, or a refresh can reach the store after the deadline has already passed.
  • Why is a refresh implemented as a write carrying a lifetime riskier than one implemented as an extend?
    Because of what each does to a missing entry. An extend that fails on a missing entry reports the miss and the caller reloads. A write carrying a lifetime creates the entry, so a refresh arriving just after the deadline passed puts the value the caller read back into the store, possibly after it was superseded.
  • Does moving a deadline on every read hurt a tier that keeps copies of the keyspace?
    Yes, in volume. Copies are kept up to date from writes, so refreshes are now part of the write stream and its size tracks read traffic. The same applies to any posture that records writes to disk; the deadline moves are not free just because the value did not change.

saying these in an interview costs you the question

  • Calls a caller-side refresh loop a store setting and prices it as free
  • Assumes a read always moves the deadline forward
  • Ignores that each refresh is an extra round trip
  • Treats a failed refresh as success and serves a stale entry
  • Refreshes with a full value write on every single access
  • Never considers refreshing only near the end of the window