The Retry That Forgets

This client library pins each user to one replica so that a user always reads their own writes. Reads started failing during a replica restart, so a retry was added.

const replicas = [r0, r1, r2, r3];

function pinned(userId) {
  return replicas[hash(userId) % replicas.length];
}

async function readProfile(userId) {
  try {
    return await pinned(userId).get(`profile:${userId}`);
  } catch (err) {
    const alt = replicas[Math.floor(Math.random() * replicas.length)];
    return await alt.get(`profile:${userId}`);   // retry
  }
}

Writes still go to the leader and replicate asynchronously. What did the retry cost, and what restores the guarantee?

A

Read-your-writes was never provided here. Only a linearizable read from the leader can give it, so both the pinned path and the retry path are equally broken.

B

Read-your-writes came only from the pinning, so the fallback silently removes it. Have the write return the log position it reached and require the replica serving the read to have applied at least that position.

C

Nothing was lost. The retry only runs when the pinned replica is unreachable, and a replica that is up is by definition caught up with the leader.

D

The guarantee is fine but the retry is unfair. Replacing Math.random() with the next replica in ring order spreads the fallback load evenly and is the whole fix.