HarperFast / HarperFast/harper
Record-based setResidency destroys already-stored local values on ordinary writes (invalidated stub overwrite, no delete entry) - byId variant preserves; per-thread-static scoping makes it thread-dependent
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 205
Description
## Summary
The record-based `setResidency(fn)` variant silently **destroys already-stored local values on ordinary writes** when the residency function's returned list excludes the local hostname: the existing full record is overwritten with a partial/INVALIDATED stub (primary-key field stripped), while the transaction log records an ordinary audited `upsert` carrying the full payload — no delete entry, nothing in any error stream. If the peer holding the resident copy is unavailable (or replication is degraded), the data is locally unrecoverable except via the transaction-log payload.
By contrast, `setResidencyById` cannot destroy: its `record === undefined` path in `recordUpdater` never touches the primary store — it only prevents new local storage (pre-existing rows survive as stale values).
Runnable two-node repro (harper-pro 5.2.2, OSS-core mechanism): https://github.com/tpsdev-ai/flair/pull/1266 — `--lane=residency`, committed transcripts show: (a) ordinary self-resave replaces full values with stubs for keys the residency fn excludes, (b) zero delete transactions + ordinary audited upserts, (c) keys the fn includes are stored fully in the same call, (d) restore from the log payload recovers.
Also relevant: the residency function is a per-thread static — only component-worker writes engage it, so the behavior differs by which thread serves a write (ops-API writes elsewhere never trigger it), which makes the destruction harder to correlate with its cause in production.
## Why this is filed
An application/orchestration layer installing a record-based residency function (reasonable for data-placement goals) turns every ordinary write into a potential local destruction of previously-stored data, with no warning, no delete record, and thread-dependent behavior. Ask: (1) should the record-variant preserve the existing stored value (like the byId variant does) rather than overwriting it with an invalidated stub — or at minimum log loudly on first destruction? (2) Documentation of the per-thread-static scoping and the byId-vs-record behavioral difference. (3) If Fabric's hosted-DB orchestration installs residency functions on customer tables in any configuration, that interaction plus degraded replication would produce silent data loss matching a production incident we are investigating — confirmation either way would be valuable.
Contributor guide
Research direction
Start with the record-based setResidency path in recordUpdater and compare it with setResidencyById; use the two-node residency repro in PR 1266 and inspect the transaction-log output. Done should establish whether existing local values are preserved or destruction is surfaced, and document the per-thread-static scoping and the behavioral difference between the two variants.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 42/100