ยง49.7โ€“49.9Idempotent Operations, Client Caching, and the Cache Consistency Problem

Part III OSTEP pp. 629โ€“632 ยท ~7 min read

  • idempotency
  • cache consistency problem
  • update visibility
  • stale cache
  • flush-on-close
  • attribute cache

A stateless server plus self-contained requests set up NFSโ€™s most elegant trick: one uniform way to survive every kind of failure.

49.7 Handling Server Failure with Idempotent Operations

When a client sends a request and no reply comes back, it canโ€™t tell why โ€” the request was lost, the server was down, or the reply was lost. NFSv2 doesnโ€™t try to tell them apart. It just retries: set a timer on send; if it fires with no reply, resend.

Figure 49.6: three ways a client gets no reply โ€” request lost, server down, reply lost. NFS handles all three the same way: time out and retry. Idempotency makes that safe.
Client
Network
Server
Case 1 โ€” the request is lost
Case 2 โ€” the server is down
Case 3 โ€” the reply is lost (the tricky one)
step 1 / 16 ยท time flows downward

Client: send request; set timer

Retrying blindly is only safe because most NFS operations are idempotent โ€” performing them many times has the same effect as performing them once:

โœ“ idempotent: store Xdo it 3ร—:XXX=Xsame result โ€” safe to retryโœ— not idempotent: incrementdo it 3ร—:+1+1+1โ‰ +3retry changes the answer
โ€œStore value Xโ€ is idempotent (do it thrice, same result); โ€œincrementโ€ is not (thrice โ‰  once). READ and LOOKUP are trivially idempotent; WRITE is too, because it carries the exact offset โ€” a retry writes the same bytes to the same place.

Tip: Idempotency Is Powerful

When an operation can be issued more than once with no ill effect, handling failure is easy โ€” just retry. Itโ€™s what lets NFS unify lost-message and server-crash recovery into a single mechanism.

Not everything is naturally idempotent, though. MKDIR isnโ€™t: if the directory is created but the reply is lost, the retried MKDIR finds it already exists and reports failure โ€” even though the operation really did succeed.

Tip: Perfect Is the Enemy of the Good (Voltaireโ€™s Law)

You could redesign mkdir to be idempotent โ€” but why bother? The NFS design covers the important cases cleanly and simply. Accepting that a few corner cases arenโ€™t perfect, and shipping anyway, is a sign of good engineering.

49.8 Improving Performance: Client-Side Caching

Sending every read and write across the (slow) network would be painful, so the NFS client caches file data and metadata in client memory: the first access pays the network cost, later ones are served locally. The cache also does write buffering โ€” an applicationโ€™s write() returns as soon as the data lands in the client cache, and the data is sent to the server later. Great for performance โ€” but multiple client caches create a big problem.

49.9 The Cache Consistency Problem

The cache consistency problem is clearest with three clients and a server:

C1cache: F[v1]STALEC2 (writer)cache: F[v2]newestC3cache: emptywill read serverServer S (disk)F[v1] โ†’ F[v2]v2 only after C2 flushes
Figure 49.7: C1 holds an old F[v1]; C2 (the writer) has the new F[v2]; C3 has nothing yet. The serverโ€™s copy only becomes F[v2] once C2 flushes.

There are two subproblems. Update visibility : while C2โ€™s new version sits buffered in its cache, another client (C3) reading F gets the old F[v1] from the server. And the stale cache : even after C2 flushes F[v2] to the server, C1 still holds F[v1] in its cache and would read it. NFSv2 patches both:

NFSv2's two engineering fixes for the two cache-consistency subproblems.
the subproblemNFS's fix
update visibilityC2 buffers its writes, so another client reading F meanwhile gets the OLD versionflush-on-close (close-to-open): on close(), flush all of the file's dirty pages to the server
stale cacheC1 keeps F[v1] cached after the server already has F[v2]before trusting a cached block, GETATTR the server for the last-modified time; if it's newer than the fetch time, invalidate and refetch
Dotted-underlined cells have explanations โ€” click one.

The flush-on-close rule (a.k.a. close-to-open) handles visibility; a GETATTR last-modified check handles staleness โ€” softened by an attribute cache so clients donโ€™t bombard the server with โ€œhas this changed?โ€ on every access. It works, mostly โ€” but as weโ€™ll see next, these are engineering patches, and the attribute-cache timeout means the implementation ends up defining the semantics.

Check yourself: idempotency and cache consistency

1.A client sends a request and gets no reply. What are the possible causes, and how does NFSv2 respond to all of them?

2.What property makes 'just retry' safe, and what does it mean?

3.Is an NFS WRITE idempotent, and why does that matter for a lost reply?

4.Which operation is NOT naturally idempotent, illustrating that 'life isn't perfect'?

5.Client-side caching creates the cache consistency problem, which has two subproblems. What are they?

6.How does NFSv2 address the two cache-consistency subproblems?

6 questions