Skip to content

[BACKPORT 2026.1][#33175] DocDB: Let the already-replicated verdict win over an expired write fence (#33653) - #33664

Merged
hari90 merged 1 commit into
yugabyte:2026.1from
hari90:backport-7e3517d7e-2026.1
Sep 1, 2026
Merged

[BACKPORT 2026.1][#33175] DocDB: Let the already-replicated verdict win over an expired write fence (#33653)#33664
hari90 merged 1 commit into
yugabyte:2026.1from
hari90:backport-7e3517d7e-2026.1

Conversation

@hari90

@hari90 hari90 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes an inversion in the write fence (#33645 on this branch), found in review of that backport.

A write carrying fence F replicates, the response is dropped, and the client library resends the
identical ops with the same retryable request id after F has passed. The fence check sat ahead of
RegisterRetryableRequest, so the resend was rejected with WRITE_FENCE_EXPIRED ("the write did
NOT take effect") instead of getting Register's AlreadyPresent verdict -- while the row is
durably present. Exactly the wrong answer for a lease holder, and every further resend repeats it.

The fence is now checked after RegisterRetryableRequest succeeds and still before
NotifyAddedToLeader. Rejection goes through ReplicaState::NotifyReplicationFinishedUnlocked so
the registration is undone -- the leak that motivated the old ordering.

Upgrade/Rollback safety

No wire or storage format change; reorders two admission-time checks on the tablet leader.

Original commit: 7e3517d / #33653

Test plan

yb_thin_client-itest -- full suite on 2026.1, 12/12.

ReplayOfReplicatedWriteWinsOverExpiredFence replicates a fenced write, forces the timed-out
resend path, and holds it until the fence has passed: the caller must see success and exactly one
row. Fails before the fix with WRITE_FENCE_EXPIRED.

…n expired write fence (yugabyte#33653)

## Summary

A write carrying fence F replicates, the response is dropped, and the
client library resends the
identical ops with the same retryable request id after F has passed. The
fence check sat ahead of
`RegisterRetryableRequest`, so the resend was rejected with
`WRITE_FENCE_EXPIRED` ("the write did
NOT take effect") instead of getting `Register`'s `AlreadyPresent`
verdict -- while the row is
durably present. Exactly the wrong answer for a lease holder, and every
further resend repeats it.

The fence is now checked after `RegisterRetryableRequest` succeeds and
still before
`NotifyAddedToLeader`. Rejection goes through
`ReplicaState::NotifyReplicationFinishedUnlocked` so
the registration is undone -- the leak that motivated the old ordering.

## Test Plan

`yb_thin_client-itest` -- full suite, 12/12.

New test `ReplayOfReplicatedWriteWinsOverExpiredFence` replicates a
fenced write, forces the
timed-out resend path, and holds it until the fence has passed: the
caller must see success and
exactly one row. Fails before the fix with `WRITE_FENCE_EXPIRED`.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@hari90
hari90 merged commit d1e4547 into yugabyte:2026.1 Sep 1, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants