feat: stable EntityIdMapper — rebuild() no longer renumbers (2.4.0 #1, the foundation) #1

Merged
dpsifr merged 4 commits from feat/2.4.0-storage into main 2026-05-28 19:37:48 +02:00
dpsifr commented 2026-05-28 18:45:42 +02:00 (Migrated from github.com)

Summary

  • Fix the renumber-on-rebuild bug. metadataIndex.rebuild() previously called idMapper.clear() which reset nextId to 1 and renumbered every UUID by re-insertion order — silently invalidating any consumer that had persisted int-keyed data against the old map. This is the foundation 2.4.0 #2 (vector mmap store), #3 (graph link compression), and #4 (column-store JS↔native interchange) all need.
  • Targeted change: remove the unconditional idMapper.clear() in rebuild(). The rebuild already re-iterates every entity via getOrAssign(uuid), which returns the existing int unchanged for known UUIDs. Stale UUID→int entries for entities no longer in storage persist as harmless memory overhead.
  • clearAllIndexData() — the explicit nuclear recovery path — keeps its idMapper.clear() call (renumbering is intentional there) and now logs a prodLog.warn making it explicit that any persisted int-keyed data is invalidated.
  • Strengthened the EntityIdMapper class JSDoc with the stability contract: append-only getOrAssign, monotonic nextId, remove() leaves permanent holes, rebuild() never renumbers, only clear() does.

Test plan

  • tests/regression/entity-id-mapper-stability.test.ts — 5 tests pinning down the contract:
    1. UUID→int mappings persist across a single rebuild.
    2. Mappings stay byte-for-byte stable across many consecutive rebuilds.
    3. Entities added after rebuild get fresh monotonic ints (no collisions).
    4. Removed entities leave a permanent hole — new entities never recycle the gap, even across a rebuild.
    5. clearAllIndexData() is the explicit nuclear path that DOES renumber.
  • Full suite green: 62 files, 1417 tests passing.

Compat / risk

  • No public API change. EntityIdMapper's API surface is unchanged; only rebuild()'s implementation behavior is.
  • Existing consumers that called rebuild() and then relied on getting fresh int ids assigned for existing UUIDs are now broken — but no such consumer should exist (the renumber was a bug, not a feature).
  • Coordinated release: brainy 7.26.0 + cortex 2.4.0 land together once 2.4.0 #2–#4 are in.
## Summary - **Fix the renumber-on-rebuild bug.** `metadataIndex.rebuild()` previously called `idMapper.clear()` which reset `nextId` to `1` and renumbered every UUID by re-insertion order — silently invalidating any consumer that had persisted int-keyed data against the old map. **This is the foundation 2.4.0 #2 (vector mmap store), #3 (graph link compression), and #4 (column-store JS↔native interchange) all need.** - **Targeted change**: remove the unconditional `idMapper.clear()` in `rebuild()`. The rebuild already re-iterates every entity via `getOrAssign(uuid)`, which returns the existing int unchanged for known UUIDs. Stale UUID→int entries for entities no longer in storage persist as harmless memory overhead. - `clearAllIndexData()` — the explicit nuclear recovery path — keeps its `idMapper.clear()` call (renumbering is intentional there) and now logs a `prodLog.warn` making it explicit that any persisted int-keyed data is invalidated. - Strengthened the `EntityIdMapper` class JSDoc with the stability contract: append-only `getOrAssign`, monotonic `nextId`, `remove()` leaves permanent holes, `rebuild()` never renumbers, only `clear()` does. ## Test plan - [x] `tests/regression/entity-id-mapper-stability.test.ts` — 5 tests pinning down the contract: 1. UUID→int mappings persist across a single rebuild. 2. Mappings stay byte-for-byte stable across many consecutive rebuilds. 3. Entities added after rebuild get fresh monotonic ints (no collisions). 4. Removed entities leave a permanent hole — new entities never recycle the gap, even across a rebuild. 5. `clearAllIndexData()` is the explicit nuclear path that DOES renumber. - [x] **Full suite green**: 62 files, 1417 tests passing. ## Compat / risk - No public API change. `EntityIdMapper`'s API surface is unchanged; only `rebuild()`'s implementation behavior is. - Existing consumers that called `rebuild()` and then relied on getting fresh int ids assigned for existing UUIDs are now broken — but no such consumer should exist (the renumber was a bug, not a feature). - Coordinated release: brainy 7.26.0 + cortex 2.4.0 land together once 2.4.0 #2–#4 are in.
Sign in to join this conversation.
No description provided.