docs: RELEASES.md entry for 8.2.6 (write/index-spine hardening)
Consumer-facing summary of the Pass-1 durability + integrity release: durable blob-write honesty, single-op history durability (PendingFlushDurabilityError), degraded-index surfacing, full-footprint clear(), count symmetry, and loud index-maintenance / aggregation failures. loadBinaryBlob fault-propagation is held for the native lockstep and deliberately omitted here.
This commit is contained in:
parent
36c10c1e20
commit
a873852e61
1 changed files with 47 additions and 0 deletions
47
RELEASES.md
47
RELEASES.md
|
|
@ -10,6 +10,53 @@ Full auto-generated changelog: `CHANGELOG.md` · Releases: https://github.com/so
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## v8.2.6 — 2026-07-13 (write/index-spine hardening — loud errors, never quiet losses)
|
||||||
|
|
||||||
|
Durability + integrity hardening across the write and index paths. Every fix converts a place that
|
||||||
|
could *silently* lose data, serve a partial result, or acknowledge a write that did not land into a
|
||||||
|
loud, observable failure. No breaking API changes; one new exported error.
|
||||||
|
|
||||||
|
- **A durable blob write that stored nothing is no longer acknowledged.** The atomic blob write
|
||||||
|
(`saveBinaryBlob`, used for vector/graph index segments and the native index files) uses a unique
|
||||||
|
per-writer temp file, so a rename `ENOENT` can only mean *our* temp vanished before the rename —
|
||||||
|
the bytes never landed. It previously returned success on that path; it now retries once with a
|
||||||
|
fresh temp and, if the temp vanishes again, throws instead of acknowledging a write that persisted
|
||||||
|
nothing. A downstream deployment that mmaps these files no longer finds a "successfully written"
|
||||||
|
blob missing on the next open.
|
||||||
|
|
||||||
|
- **Single-op history durability is enforced, not assumed.** The asynchronous group-commit flush
|
||||||
|
that persists single-op generation history previously swallowed a persist failure as a warning
|
||||||
|
while writes kept succeeding and their history piled up, undurable, in memory. It now tolerates a
|
||||||
|
transient blip (retry with capped backoff) and, after repeated failures, **refuses further writes**
|
||||||
|
with the new **`PendingFlushDurabilityError`** rather than promise a durability it cannot deliver.
|
||||||
|
Live canonical data is untouched; the latch self-heals the moment a flush succeeds. Callers needing
|
||||||
|
hard per-write durability should keep using `transact()` (or `flush()` after a single-op).
|
||||||
|
|
||||||
|
- **A degraded derived index is surfaced on reads, not served silently.** When a non-fatal index
|
||||||
|
rebuild fails at open, or a write commits via adopt-forward recovery with an incomplete derived
|
||||||
|
index, `find()` and `get()` now emit one loud warning per degraded window (reads still return —
|
||||||
|
canonical is the source of truth), the state folds into `checkHealth()` /
|
||||||
|
`validateIndexConsistency()`, and `repairIndex()` reconciles and clears it.
|
||||||
|
|
||||||
|
- **`clear()` removes the full derived footprint.** It previously left raw index blobs, the native
|
||||||
|
id-mapper, and column-index manifests on disk, so a cleared brain could re-read stale native state
|
||||||
|
on reopen. It now wipes them together as a set.
|
||||||
|
|
||||||
|
- **Counts stay honest across deletes.** A delete decremented the per-type breakdown but not the
|
||||||
|
scalar total, so the total inflated permanently (and won pagination). Delete now decrements both;
|
||||||
|
the invariant `total === Σ per-type` holds across any interleaving of add / visibility-flip /
|
||||||
|
delete and across a reopen.
|
||||||
|
|
||||||
|
- **Index-maintenance and aggregation failures are loud.** A partial LSM/segment load no longer
|
||||||
|
publishes the manifest's full count as if healthy; an HNSW flush that can't persist a node throws
|
||||||
|
(`HnswFlushError`) instead of returning a lying count and dropping the node; a corrupt or missing
|
||||||
|
manifest-listed column segment throws (`ColumnSegmentLoadError`) instead of silently dropping its
|
||||||
|
entities from every query; and aggregation materialization / state-load failures now warn instead
|
||||||
|
of vanishing into an empty catch.
|
||||||
|
|
||||||
|
New export: **`PendingFlushDurabilityError`** (with `.cause` and `.failedAttempts`). No other public
|
||||||
|
API change. Each fix ships with a dedicated regression test.
|
||||||
|
|
||||||
## v8.2.5 — 2026-07-12 (honest response when a transaction rollback can't complete)
|
## 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
|
Data-integrity fix. When a transaction failed and its rollback then *also* failed to undo a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue