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

Open
#2,257 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.