brainy/tests/unit/storage/blob-save-durability.test.ts
David Snelling b6c7039769 fix: restore loadBinaryBlob fault-propagation (native column-store lockstep)
The Pass-1 hardening that makes loadBinaryBlob distinguish genuine absence
(ENOENT -> null) from a real fault (EIO/EACCES/... -> throw) was held out of
8.2.6 because the native accelerator's two column-store read sites still relied
on null-on-error. That accelerator release has now hardened those sites to
handle the throw (a faulted segment read marks the field unavailable and throws
a named error), so the fault-propagating form is restored and ships lockstep.

A present-but-unreadable index blob no longer masquerades as absent (which drove
needless rebuilds / empty reads). Adds the loadBinaryBlob leg to the blob
durability suite (absent -> null; present -> bytes; real fault -> throws).
2026-07-13 12:09:55 -07:00

111 lines
4.3 KiB
TypeScript

/**
* @module tests/unit/storage/blob-save-durability
* @description Finding 13 (cortex sweep): saveBinaryBlob must never acknowledge a
* durable write that stored nothing. With the UNIQUE per-writer temp suffix, a
* rename ENOENT can no longer mean "a concurrent idempotent writer already
* renamed it" — nobody else has this writer's temp — so it means our temp
* vanished before the rename and the bytes did NOT land. The old code returned
* success on that ENOENT; the native provider mmaps these blobs, so a
* phantom-acked blob is the exact silent-loss class the registered-blob work
* chases. The fix retries once with a fresh temp, then fails loud.
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import * as fs from 'node:fs'
import * as os from 'node:os'
import * as path from 'node:path'
import { FileSystemStorage } from '../../../src/storage/adapters/fileSystemStorage.js'
const enoent = (): Error => Object.assign(new Error('temp removed'), { code: 'ENOENT' })
describe('FileSystemStorage.saveBinaryBlob durable-write honesty (finding 13)', () => {
let dir: string
let storage: any
beforeEach(async () => {
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'brainy-blob-save-'))
storage = new FileSystemStorage(dir)
await storage.init()
})
afterEach(() => {
vi.restoreAllMocks()
fs.rmSync(dir, { recursive: true, force: true })
})
it('persists and reads back a blob under normal conditions', async () => {
await storage.saveBinaryBlob('graph-lsm/seg-1', Buffer.from([1, 2, 3, 4]))
expect(await storage.loadBinaryBlob('graph-lsm/seg-1')).toEqual(Buffer.from([1, 2, 3, 4]))
})
it('throws (never acks) when the temp vanishes on every rename attempt', async () => {
vi.spyOn(fs.promises, 'rename').mockRejectedValue(enoent())
await expect(
storage.saveBinaryBlob('k/silent-loss', Buffer.from([9]))
).rejects.toThrow(/did NOT persist/)
// Prove the write truly did not land — and was not acknowledged. (loadBinaryBlob
// uses readFile, not rename, so it reflects the real on-disk state.)
expect(await storage.loadBinaryBlob('k/silent-loss')).toBeNull()
})
it('self-heals: a single transient temp-vanish retries and persists', async () => {
const real = fs.promises.rename.bind(fs.promises)
let calls = 0
vi.spyOn(fs.promises, 'rename').mockImplementation(async (from: any, to: any) => {
calls++
if (calls === 1) throw enoent()
return real(from, to)
})
await expect(
storage.saveBinaryBlob('k/heals', Buffer.from([7, 7]))
).resolves.toBeUndefined()
expect(calls).toBe(2) // first attempt vanished, retry landed
expect(await storage.loadBinaryBlob('k/heals')).toEqual(Buffer.from([7, 7]))
})
it('a non-ENOENT rename fault propagates verbatim (not swallowed)', async () => {
vi.spyOn(fs.promises, 'rename').mockRejectedValue(
Object.assign(new Error('disk fault'), { code: 'EIO' })
)
await expect(
storage.saveBinaryBlob('k/eio', Buffer.from([1]))
).rejects.toMatchObject({ code: 'EIO' })
})
})
describe('FileSystemStorage.loadBinaryBlob fault-propagation (finding 11; cor 3.0.13 lockstep)', () => {
let dir: string
let storage: any
beforeEach(async () => {
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'brainy-blob-load-'))
storage = new FileSystemStorage(dir)
await storage.init()
})
afterEach(() => {
vi.restoreAllMocks()
fs.rmSync(dir, { recursive: true, force: true })
})
it('returns null for a genuinely absent blob (ENOENT)', async () => {
expect(await storage.loadBinaryBlob('never/written')).toBeNull()
})
it('reads back a present blob', async () => {
await storage.saveBinaryBlob('present/blob', Buffer.from([1, 2, 3]))
expect(await storage.loadBinaryBlob('present/blob')).toEqual(Buffer.from([1, 2, 3]))
})
it('propagates a real IO fault instead of masking it as absent', async () => {
await storage.saveBinaryBlob('present/blob', Buffer.from([1, 2, 3]))
vi.spyOn(fs.promises, 'readFile').mockRejectedValue(
Object.assign(new Error('disk fault'), { code: 'EIO' })
)
// A present-but-unreadable blob must NOT read as null (that drove needless
// native rebuilds / empty reads); cor 3.0.13's column-store handles the throw.
await expect(storage.loadBinaryBlob('present/blob')).rejects.toMatchObject({ code: 'EIO' })
})
})