docs: RELEASES.md entry for 8.2.5 (honest rollback-failure response)
This commit is contained in:
parent
711d2f046a
commit
a7c7aa5102
1 changed files with 34 additions and 0 deletions
34
RELEASES.md
34
RELEASES.md
|
|
@ -10,6 +10,40 @@ Full auto-generated changelog: `CHANGELOG.md` · Releases: https://github.com/so
|
|||
|
||||
---
|
||||
|
||||
## v8.2.5 — 2026-07-12 (honest response when a transaction rollback can't complete)
|
||||
|
||||
Data-integrity fix. When a transaction failed and its rollback then *also* failed to undo a
|
||||
canonical write (retries exhausted), the old behavior logged, continued, and threw
|
||||
`TransactionRollbackError` — while the record it couldn't undo stayed durable on disk, and the
|
||||
transaction even reported its state as `'rolled_back'`. The caller got an error implying the write
|
||||
was undone; a read-back showed the record. A failed rollback had no truthful response.
|
||||
|
||||
Rollback now tells the truth, with a two-branch contract:
|
||||
|
||||
- **Adopt-forward (safe case).** A single-op write (`add`/`update`/…) whose only damage is a
|
||||
durably-present record — the write the caller asked for — is **adopted**: its generation is
|
||||
committed, the write returns success, and a loud warning records that the derived index may be
|
||||
incomplete for that id until the next rebuild/`repairIndex()`. No error, no double-write; the
|
||||
record is immediately durable and retrievable by `get()`.
|
||||
- **Fail loud + quarantine (unsafe case).** A multi-operation batch, or *any* case where a record
|
||||
was lost (a remove/update whose restore-undo failed), throws the new **`StoreInconsistentError`**
|
||||
naming every unreconciled record and its disposition (`orphan` vs `loss`), and puts the brain into
|
||||
**write-quarantine**: reads keep working, but writes are refused until `repairIndex()` reconciles
|
||||
the derived indexes against canonical storage and lifts the quarantine. The generation counter is
|
||||
not advanced, and a transaction whose rollback failed is now `'inconsistent'`, never the
|
||||
`'rolled_back'` lie.
|
||||
|
||||
The decision is made by *observation*, not guesswork: both commit paths already hold byte-identical
|
||||
before-images, so after a failed rollback the store compares current canonical state to them to
|
||||
classify exactly which records are orphaned or lost.
|
||||
|
||||
New export: `StoreInconsistentError` (with `.records` and `.cause`) and the `UnreconciledRecord` type.
|
||||
No other API change; `repairIndex()` gains the quarantine-lift behavior. Regression
|
||||
(`tests/integration/rollback-trapdoor.test.ts`) injects the exact failure (index add throws →
|
||||
canonical delete-undo fails) and pins adopt-forward (durable, get-able, not quarantined), fail-loud
|
||||
(`StoreInconsistentError` + quarantine + reads work + `repairIndex()` lifts it), and the error's
|
||||
record naming.
|
||||
|
||||
## v8.2.4 — 2026-07-12 (restore can no longer destroy the store it's recovering)
|
||||
|
||||
Recovery-safety fix. `restore()` removed the entire live brain directory and THEN copied the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue