HarperFast / HarperFast/harper
randomAccessFields tables cannot recover rows contaminated with resolver-owned values
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Summary
The #2359 recovery path (shipped in #2368) reaches only records that decode as **plain objects**. On a table with `storage.randomAccessFields: true` (or the global `STORAGE_RANDOMACCESSFIELDS`), records decode as structon lazy typed-struct instances whose stored fields live as accessors on a structon-generated child prototype. For a row that an affected release (5.1.0–5.2.6) contaminated with a value under an `@computed`/`@enumerable @relationship` name:
- the child-prototype field accessor **shadows** the table's resolver on `structPrototype`, so reads return the stale stored value — silently, with no crash to announce it;
- neither recovery trigger fires (`toJSON` is structon's, not the table's surfaced projection; the colliding name is never an own key), so re-encodes re-persist the contamination — it never ages out.
## Expected
The resolver is authoritative over any stored value under its name, on every storage mode — the invariant #2368 establishes for plain-decode tables.
## Repro sketch
1. On 5.2.x, a `randomAccessFields: true` cache table with `salePrice: Float @computed(...)`; fill a record whose durable encode captured `salePrice` (any affected-release write).
2. Upgrade to a build with #2368.
3. Read the row: it reports the stale stored `salePrice`, and a subsequent write re-persists it.
`integrationTests/resources/cachedComputedAttribute.test.ts` has the probes (`PoisonedRecord`, `StoredKeys`); a fixture variant with `randomAccessFields: true` would settle it in one run — the mechanism is confirmed from structon's decode path (`struct.js` child-prototype accessors), but no end-to-end run has exercised it yet.
## Notes
Raised repeatedly by the #2368 pre-push review (rounds 1–5, adjudicated in-scope, deferred). A fix likely wants structon cooperation (the decoded instance knows its structure fields; harper knows the resolver-owned names) — either a decode-side skip list handed to structon, or materializing such rows to plain objects when the encoder has `resolvedAttributeNames`.
Contributor guide
Research direction
Start with integrationTests/resources/cachedComputedAttribute.test.ts and the #2368 recovery path, then inspect struct.js child-prototype accessors involved in decoding randomAccessFields rows. Exercise a fixture variant with randomAccessFields: true using the existing PoisonedRecord and StoredKeys probes; done means the resolver wins over the stored value and subsequent writes no longer re-persist the contamination.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- database
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100