HarperFast / HarperFast/harper
Record locks: delegation messages carry no requestId, so the home cannot tell a duplicate request from a renewal (§5.2)
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
`docs/record-lock-ownership.md` §5.2 states:
> Every message carries `(epoch, homeIncarnation, delegationCounter, requestId)` and is bound to the
> authenticated replication origin. A reply that does not match the requester's current epoch and its
> **outstanding request** is discarded.
`DelegationRequest` and `DelegationReply` in `resources/recordLockCoordinator.ts` carry no
`requestId`. Request/reply correlation is the transport's per-call promise, and the home cannot tell
a **duplicate or delayed** request from a genuine renewal.
### What that costs today
The home's renewal branch (`#grant`) mutates `existing.token` in place for any request from the node
that already holds the grant. So a delayed duplicate mints a token the delegate never receives, and
the home's grant and the delegate's delegation are recorded under different tokens.
That produced a two-holder path — a timed-out request's cleanup released the newer token while the
node still admitted under the older one — which harper#2498 closed by refusing to hand back a grant
while **any** delegation for the key is held. That fix is correct and is the safety answer, but it
leaves an availability residue: the delegate's eventual release names its own token, the home's grant
is recorded under the newer one, so the home ignores the release and holds the key until the
delegation lease expires (up to `DELEGATION_LEASE_MS + skew`, ~365 s today).
### The fix
Carry `requestId` on the request and echo it on the reply, as §5.2 already specifies:
- The home dedupes by `(requester, requestId)`: a duplicate returns the **same** token and the
remaining lease rather than minting a new one, so the delegate's token and the home's never diverge.
- The requester discards a reply whose `requestId` is not one it still has outstanding, instead of
relying on the transport's promise identity.
This is a wire change, so it lands with the harper-pro transport
(`replication/recordLockRpc.ts`, harper-pro#822) rather than in core alone. §11 already plans a
version field on the `LOCK_RELEASE` payload for §7.1's dependency set; this belongs in the same
protocol revision.
### Acceptance
- A delayed duplicate request from the delegate returns the token the delegate already holds, with
the grant's remaining lease, and does not advance the home's token.
- A reply carrying an unknown `requestId` is discarded rather than installed.
- The delegate's release, written under the token it installed, clears the home's grant in the
duplicate-request case — the availability residue above is gone.
Contributor guide
Research direction
Read docs/record-lock-ownership.md §5.2 and resources/recordLockCoordinator.ts, then inspect replication/recordLockRpc.ts and harper-pro#822 for the wire boundary. Trace the renewal, duplicate-request, reply-validation, and release paths. Done means delayed duplicates preserve the existing token, unknown replies are discarded, and the delegate's release clears the home's grant.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nodejs, typescript
- Domain
- distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100