HarperFast / HarperFast/harper-pro
Replication W9: Exclusive distributed locking (replication integration)
- Dominant language
- JavaScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 80
Description
**Workstream W9 of #430 · exclusive distributed locking — replication integration**
## Summary
Implement `table.lock(primaryKey): Promise` distributed exclusive locking. The core API and semantics are tracked in **harper#483** (`Table.ts` `lock()` is currently a stub: `throw new Error('Not yet implemented')`, line 1735). This workstream tracks the **replication-protocol integration** and the coordination with the core change. Locking is the explicit-serialization escape hatch from wall-clock LWW lost-updates (epic Theme G).
## Design direction (fits the real substrate)
- **`LOCKED` metadata bit** — a free bit in the `RecordEncoder` `uint32` metadata bitmap (alongside `INVALIDATED`/`EVICTED`/`LOCAL_ONLY`). Because it lives in the already-decoded metadata integer, the write/send paths can test "locked / by whom" **without decoding the record value** (the same throughput trick `LOCAL_ONLY` uses).
- **`LOCK`/`UNLOCK` audit types** — a lock transition is a version-changing audit entry that **replicates through the existing pipe for free** (`aftercommit` → subscription → `SUBSCRIPTION_UPDATE`). Peers applying it set their local `LOCKED` bit.
- **Write-gating** — a non-holder write checks the `LOCKED` bit *before* LWW resolution and defers/rejects, converting silent lost-updates into explicit serialization. Unlock (a version change) lets queued writes proceed in version order.
- **Grant set follows residency** — only resident nodes can hold/serve the record, so only they need to grant (far cheaper than whole-cluster).
- **Grant handshake** rides the existing `OPERATION_REQUEST/RESPONSE (136/137)` RPC; durable lock state rides the audit stream.
- **Leases + fencing by lock version** — lease via the existing `expiresAt` machinery; a write must present a lock version ≥ the record's current lock version, so a stale holder's write is rejected by `precedesExistingVersion` regardless of clock skew (mitigates Theme G).
## Phasing
- [ ] **Phase 0 — local-only lock.** Over the existing cross-thread `store.tryLock` + the `LOCAL_ONLY` metadata bit. No replication, no protocol change. Validates the metadata/version-change wiring and the `WritableRecord` API surface.
- [ ] **Phase 1 — single-owner delegation.** Replicated `LOCK`/`UNLOCK`; if another node holds it, request via `OPERATION_REQUEST` from that one node; write-gating on the `LOCKED` bit; leases + fencing. Covers the common "one node owns the hot key" case with point-to-point RPC.
- [ ] **Phase 2 — broadcast / residency-quorum grant.** Shared→exclusive upgrade: fan out grant requests to the residency holder set, `Promise.all`. Deadlock avoidance via global key ordering + Wound-Wait by lock timestamp. Highest-risk piece.
## Hardest correctness problems
- **Clock-skew-safe lease expiry** — conservative lease margins + version fencing (a stale holder's write is fenced even if its clock disagrees).
- **Split-brain during an all-nodes grant** — require durable-commit confirmation of the lock entry; version fencing ensures two exclusive holders can never both pass write-gating.
- **Holder crash** — lease auto-expire sweep emits `UNLOCK` (itself a version change) to unblock waiters.
- **Node-owned vs transaction-owned locks** — an option on `lock()`; transaction-owned auto-unlock at commit, node-owned persist.
## Dependencies
Core metadata/audit wiring (**harper#483**). Independent of W1/W4 — Phase 0/1 can start in parallel with Phase 2 of the epic once the metadata bit + audit type land.
## Effort / risk
**Phase 0 M / low · Phase 1 L / medium · Phase 2 L / high.**
## Acceptance criteria
- `lock()` serializes concurrent multi-node writes to a key with no LWW lost update.
- A crashed lock-holder's lock auto-expires and waiters proceed.
- A stale (lease-expired) holder's write is fenced.
---
🤖 Filed by Claude on behalf of Kris.
Contributor guide
Assessment
This issue has not been assessed yet.