fix: query-cap memory misread (MemAvailable + floor) + rootDirectory getter for native mmap fast-path

Two platform-wide production fixes for consumers on bare VMs / mmap-filesystem storage.

BUG A — auto maxQueryLimit collapsed to ~1000 on healthy VMs → 500s on legitimate
queries. getAvailableMemory() read os.freemem() (kernel MemFree, which excludes
reclaimable page cache and reads as tens of MB on a page-cache-heavy mmap box).
Now reads /proc/meminfo MemAvailable (the `free -h` figure), falling back to
os.freemem() only off-Linux; auto-detected caps (container/free branches) are floored
at 10k so a misread can't collapse them. Explicit maxQueryLimit/reservedQueryMemory
are honored as-is.

BUG B — native mmap vector fast-path never engaged (per-entity reads → 57s cold start
on a 283MB brain). FileSystemStorage now exposes a public `rootDirectory` getter; the
native vector provider feature-detects it to enable its memory-mapped graph path.
brainy stored it as the protected `rootDir`, so the gate silently failed. Self-heals
after the first post-upgrade flush writes the mmap file.

Regression tests: auto-cap floor (container/free/explicit-override) + the rootDirectory
getter. Build clean; full suite 1483 green.
This commit is contained in:
David Snelling 2026-06-16 08:46:39 -07:00
parent 4f8159c572
commit 3f8e0971a2
5 changed files with 127 additions and 6 deletions

View file

@ -399,4 +399,32 @@ describe('Memory Limits - Container Detection & Smart Calculation', () => {
await brain.close()
})
})
// Regression: a memory MISREAD (tiny detected limit, or os.freemem reading a
// sliver on a page-cache-heavy mmap box) must NOT collapse the auto cap to
// ~1000 and 500 legitimate queries. Auto-detected caps are floored at 10k;
// explicit operator settings still pass through unfloored.
describe('Auto-cap floor (BUG: querycap collapse on healthy VM)', () => {
it('floors the container-derived cap so a tiny/misread limit cannot collapse it', () => {
// 50 MB → queryMemory 12.5 MB → floor(12.5/25)·1000 = 0 pre-fix.
process.env.MEMORY_LIMIT = String(50 * 1024 * 1024)
const config = ValidationConfig.getInstance()
expect(config.limitBasis).toBe('containerMemory')
expect(config.maxLimit).toBe(10000) // floored, not 0/1000
})
it('floors the free-memory-derived cap as well', () => {
// No container env → freeMemory branch. On any real box this is well
// above the floor; the guarantee is it can never drop below it.
const config = ValidationConfig.getInstance()
expect(config.limitBasis).toBe('freeMemory')
expect(config.maxLimit).toBeGreaterThanOrEqual(10000)
})
it('does NOT floor an explicit (small) operator override', () => {
const config = ValidationConfig.getInstance({ maxQueryLimit: 500 })
expect(config.limitBasis).toBe('override')
expect(config.maxLimit).toBe(500) // explicit intent respected, not floored
})
})
})