fix(8.0): VFS path-cache instance-scoping + verb totalCount page-cap
Two independent restart/multi-instance correctness bugs.
VFS path cache (A.3): PathResolver keyed the PROCESS-GLOBAL path cache on
`vfs:path:<path>` with no instance scope. Multiple Brainys per process (a
supported pattern) over DIFFERENT storage collided — instance A's `/x → id_A`
satisfied instance B's `stat('/x')` against unrelated storage → stale id →
"Entity not found" (surfaced by metadata-only-comprehensive's VFS stat() once
another VFS test had seeded the cache). Scope every `vfs:path:` key by a
monotonic per-process instance token (the VFS root id is a fixed sentinel, so it
can't disambiguate; the global cache is itself process-scoped so the token
suffices). Scoped the invalidate/prefix-delete paths too, so a branch-switch /
clear / fork on one brain no longer wipes another brain's cache. Cross-instance
sharing was only an optimization and was the collision itself; each instance
keeps its own local pathCache.
Verb totalCount (verb mirror of b2005ff): getVerbsWithPagination returned
`collectedVerbs.length` (the peeked page size, bounded by offset+limit+1) as
totalCount, so getVerbs({limit:1}).totalCount read 1/2 for any non-empty brain
on the unfiltered path. Now returns the authoritative O(1) totalVerbCount
(isNew-gated, visibility-filtered, rehydrated on init); filtered scans keep the
collected length. Regression: tests/unit/storage/getVerbs-totalCount.test.ts
(warm + cold reopen, mirrors the noun test).
metadata-only-comprehensive + vfs-api-wiring integration green; 1471 unit green.
This commit is contained in:
parent
eccf42000b
commit
3a3aa43b3a
3 changed files with 149 additions and 6 deletions
|
|
@ -1789,9 +1789,22 @@ export abstract class BaseStorage extends BaseStorageAdapter {
|
|||
const paginatedVerbs = collectedVerbs.slice(offset, offset + limit)
|
||||
const hasMore = collectedVerbs.length > targetCount
|
||||
|
||||
// totalCount must be the TRUE dataset total, not this peeked page. The verb
|
||||
// type-scan early-terminates at `peekCount = offset + limit + 1`, so
|
||||
// `collectedVerbs.length` only ever reaches the page size + 1 — returning it
|
||||
// made `getVerbs({ pagination: { limit: 1 } }).totalCount` read 1 (or 2) for
|
||||
// any non-empty brain. This is the verb mirror of the noun fix (b2005ff): for
|
||||
// the unfiltered scan the authoritative total is the O(1) `totalVerbCount`
|
||||
// counter (isNew-gated, visibility-filtered, rehydrated on init); `Math.max`
|
||||
// guards a stale counter from under-reporting. A filtered scan has no cheap
|
||||
// exact total, so it keeps the collected length (a lower bound).
|
||||
const totalCount = filter
|
||||
? collectedVerbs.length
|
||||
: Math.max(this.totalVerbCount, collectedVerbs.length)
|
||||
|
||||
return {
|
||||
items: paginatedVerbs,
|
||||
totalCount: collectedVerbs.length, // Accurate count of collected results
|
||||
totalCount,
|
||||
hasMore,
|
||||
nextCursor: hasMore && paginatedVerbs.length > 0
|
||||
? paginatedVerbs[paginatedVerbs.length - 1].id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue