2025-09-11 16:23:32 -07:00
/ * *
* 🧠 Graph Scale Performance Benchmarks
*
* Comprehensive performance validation for large - scale graph operations
* and O ( 1 ) traversal validation . Tests industry - leading performance targets :
*
* - O ( 1 ) neighbor lookup : < 1ms for 10M relationships
* - Memory efficiency : ~ 24 bytes per relationship
* - Index update : < 5ms per relationship amortized
* - Rebuild performance from storage
*
* NO MOCKS , NO STUBS - REAL PRODUCTION CODE AT SCALE
* /
import { describe , it , expect , beforeAll , afterAll , beforeEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { GraphAdjacencyIndex } from '../../src/graph/graphAdjacencyIndex.js'
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
import { EntityIdMapper } from '../../src/utils/entityIdMapper.js'
2025-09-11 16:23:32 -07:00
import { MemoryStorage } from '../../src/storage/adapters/memoryStorage.js'
import { performance } from 'perf_hooks'
// Performance targets and constants
const PERFORMANCE_TARGETS = {
O1_LOOKUP : 1.0 , // <1ms for O(1) neighbor lookup
INDEX_UPDATE : 5.0 , // <5ms amortized per relationship update
MEMORY_PER_REL : 24 , // ~24 bytes per relationship
REBUILD_RATE : 1000 , // 1000 relationships/second rebuild rate
CONCURRENT_LOAD : 100 // 100 concurrent operations
} as const
// Test scales for different environments
const TEST_SCALES = {
CI : {
relationships : 10000 ,
nodes : 5000 ,
concurrentOps : 10
} ,
DEVELOPMENT : {
relationships : 100000 ,
nodes : 50000 ,
concurrentOps : 50
} ,
PRODUCTION : {
relationships : 1000000 ,
nodes : 100000 ,
concurrentOps : 100
}
} as const
// Statistical analysis helpers
class PerformanceStats {
private samples : number [ ] = [ ]
addSample ( value : number ) {
this . samples . push ( value )
}
get mean ( ) : number {
return this . samples . reduce ( ( a , b ) = > a + b , 0 ) / this . samples . length
}
get median ( ) : number {
const sorted = [ . . . this . samples ] . sort ( ( a , b ) = > a - b )
const mid = Math . floor ( sorted . length / 2 )
return sorted . length % 2 === 0
? ( sorted [ mid - 1 ] + sorted [ mid ] ) / 2
: sorted [ mid ]
}
get p95 ( ) : number {
const sorted = [ . . . this . samples ] . sort ( ( a , b ) = > a - b )
const index = Math . floor ( sorted . length * 0.95 )
return sorted [ index ]
}
get p99 ( ) : number {
const sorted = [ . . . this . samples ] . sort ( ( a , b ) = > a - b )
const index = Math . floor ( sorted . length * 0.99 )
return sorted [ index ]
}
get stdDev ( ) : number {
const mean = this . mean
const variance = this . samples . reduce ( ( acc , val ) = > acc + Math . pow ( val - mean , 2 ) , 0 ) / this . samples . length
return Math . sqrt ( variance )
}
get min ( ) : number {
return Math . min ( . . . this . samples )
}
get max ( ) : number {
return Math . max ( . . . this . samples )
}
reset() {
this . samples = [ ]
}
toString ( ) : string {
return ` mean= ${ this . mean . toFixed ( 2 ) } ms, median= ${ this . median . toFixed ( 2 ) } ms, p95= ${ this . p95 . toFixed ( 2 ) } ms, p99= ${ this . p99 . toFixed ( 2 ) } ms `
}
}
// Determine test scale based on environment
function getTestScale() {
if ( process . env . CI ) return TEST_SCALES . CI
if ( process . env . NODE_ENV === 'production' ) return TEST_SCALES . PRODUCTION
return TEST_SCALES . DEVELOPMENT
}
describe ( '🧠 Graph Scale Performance Benchmarks' , ( ) = > {
let brain : Brainy
let graphIndex : GraphAdjacencyIndex
let storage : MemoryStorage
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
let idMapper : EntityIdMapper
2025-09-11 16:23:32 -07:00
const scale = getTestScale ( )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
/** Resolve a UUID to its entity int for the 8.0 BigInt boundary. */
const entityInt = ( uuid : string ) : bigint = > BigInt ( idMapper . getOrAssign ( uuid ) )
/** Map returned entity ints back to UUIDs. */
const intsToUuids = ( ints : bigint [ ] ) : string [ ] = >
ints
. map ( ( i ) = > idMapper . getUuid ( Number ( i ) ) )
. filter ( ( u ) : u is string = > u !== undefined )
2025-09-11 16:23:32 -07:00
// Performance tracking
const lookupStats = new PerformanceStats ( )
const updateStats = new PerformanceStats ( )
const memoryStats = new PerformanceStats ( )
beforeAll ( async ( ) = > {
console . log ( ` \ n🚀 Initializing Graph Scale Performance Tests ` )
console . log ( ` 📊 Scale: ${ scale . relationships . toLocaleString ( ) } relationships, ${ scale . nodes . toLocaleString ( ) } nodes ` )
console . log ( ` 🎯 Targets: O(1) < ${ PERFORMANCE_TARGETS . O1_LOOKUP } ms, Memory ~ ${ PERFORMANCE_TARGETS . MEMORY_PER_REL } bytes/rel \ n ` )
const startTime = Date . now ( )
// Initialize storage and graph index
storage = new MemoryStorage ( )
await storage . init ( )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
idMapper = new EntityIdMapper ( { storage } )
await idMapper . init ( )
2025-09-11 16:23:32 -07:00
graphIndex = new GraphAdjacencyIndex ( storage , {
maxIndexSize : scale.nodes ,
autoOptimize : true
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
} , idMapper )
2025-09-11 16:23:32 -07:00
// Initialize Brainy for unified testing
feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1)
Brainy 8.0 makes subtype required by default on every public write path
(`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`,
import). Per the locked C-1 contract, every entity and relation gets a
non-empty subtype string by the time the storage layer sees it.
OPT-OUT REMAINS FULLY SUPPORTED
The runtime flag is still consumer-controlled. Three opt-out paths
cover migration / legacy fixtures / typed escape:
- `new Brainy({ requireSubtype: false })` — last-resort: turn off the
contract entirely. Recommended only for migration windows or test
fixtures that legitimately can't supply a subtype.
- `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` —
per-type allowlist: strict everywhere except the listed types.
- `brain.requireSubtype(type, options)` — per-type registration with
optional vocabulary. Composes with the brain-wide flag.
Default is now `true`. Opt-out is explicit and documented; nothing
silently degrades.
TEST SWEEP
Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call
site across 120 test files. Three sed patterns covered the shapes:
- `new Brainy({` → `new Brainy({ requireSubtype: false,`
- `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,`
- `new Brainy()` → `new Brainy({ requireSubtype: false })`
tests/helpers/test-factory.ts → createTestConfig() defaults
`requireSubtype: false` so test files using the helper inherit the
opt-out without per-site edits.
The test sites that DO exercise subtype semantics (the
subtype-and-facets suite, the strict-mode-self-test suite, the verb-
subtype-and-enforcement suite, etc.) already pass real subtypes — they
were the 7.30.x acceptance tests for this contract. Those tests
continue to pass unchanged.
CHANGES
src/brainy.ts
- normalizeConfig() — `requireSubtype` default `false` → `true`.
Comment refreshed to document the three opt-out paths.
tests/* (120 files)
- Bulk-edited brain construction sites. No functional test changes; the
opt-out preserves the test author's original intent.
tests/helpers/test-factory.ts
- createTestConfig() base config gains `requireSubtype: false`.
NO-OP for consumers who were already passing subtype on every write.
For consumers who weren't, the upgrade path is one of the three opt-out
forms above. Migration recipe documented in 8.0 release notes (next
commit).
VERIFICATION
- npx tsc --noEmit: clean
- npm test: 1408 / 1409 (same pre-existing race-condition outstanding;
no other regressions from the flip)
2026-06-09 14:58:25 -07:00
brain = new Brainy ( { requireSubtype : false ,
2025-09-11 16:23:32 -07:00
storage : { type : 'memory' } ,
enableGraphIndex : true ,
enableMetadataIndex : true
} )
await brain . init ( )
// Generate test data
console . log ( '📝 Generating test graph data...' )
await generateTestGraph ( scale . nodes , scale . relationships )
const elapsed = Date . now ( ) - startTime
console . log ( ` ✅ Setup complete in ${ ( elapsed / 1000 ) . toFixed ( 1 ) } s \ n ` )
} , 300000 ) // 5 minute timeout
afterAll ( async ( ) = > {
await brain ? . close ( )
await graphIndex ? . close ( )
} )
beforeEach ( ( ) = > {
// Reset stats for each test
lookupStats . reset ( )
updateStats . reset ( )
memoryStats . reset ( )
} )
/ * *
* Generate a realistic test graph with the specified scale
* /
async function generateTestGraph ( nodeCount : number , relationshipCount : number ) {
const batchSize = 1000
// Generate nodes
for ( let i = 0 ; i < nodeCount ; i += batchSize ) {
const batch = [ ]
for ( let j = 0 ; j < batchSize && i + j < nodeCount ; j ++ ) {
const idx = i + j
batch . push ( {
id : ` node- ${ idx } ` ,
data : ` Test entity ${ idx } ` ,
metadata : {
type : idx % 5 === 0 ? 'user' : idx % 3 === 0 ? 'document' : 'concept' ,
category : [ 'tech' , 'science' , 'business' , 'health' , 'education' ] [ idx % 5 ] ,
created : Date.now ( ) - idx * 1000
}
} )
}
await brain . addMany ( batch )
}
// Generate relationships with realistic patterns
const relationshipTypes = [ 'follows' , 'references' , 'related' , 'contains' , 'belongs_to' ]
let relationshipsAdded = 0
while ( relationshipsAdded < relationshipCount ) {
const batch = [ ]
for ( let i = 0 ; i < Math . min ( batchSize , relationshipCount - relationshipsAdded ) ; i ++ ) {
const sourceId = ` node- ${ Math . floor ( Math . random ( ) * nodeCount ) } `
const targetId = ` node- ${ Math . floor ( Math . random ( ) * nodeCount ) } `
const type = relationshipTypes [ Math . floor ( Math . random ( ) * relationshipTypes . length ) ]
if ( sourceId !== targetId ) { // Avoid self-references
batch . push ( {
from : sourceId ,
to : targetId ,
type ,
metadata : {
strength : Math.random ( ) ,
created : Date.now ( ) - Math . random ( ) * 86400000 // Random time within 24h
}
} )
}
}
await brain . relateMany ( batch )
relationshipsAdded += batch . length
if ( relationshipsAdded % 10000 === 0 ) {
console . log ( ` Added ${ relationshipsAdded . toLocaleString ( ) } / ${ relationshipCount . toLocaleString ( ) } relationships... ` )
}
}
}
describe ( '1. GraphAdjacencyIndex Performance Benchmarks' , ( ) = > {
it ( 'should achieve O(1) neighbor lookup validation (<1ms for large graphs)' , async ( ) = > {
console . log ( ` \ n🔍 Testing O(1) neighbor lookups on ${ scale . relationships . toLocaleString ( ) } relationships... ` )
// Warm up the index
await graphIndex . rebuild ( )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
await graphIndex . getNeighbors ( entityInt ( 'node-100' ) ) // Warm up
2025-09-11 16:23:32 -07:00
// Test random lookups
const testIterations = Math . min ( 1000 , scale . nodes / 10 )
const sampleNodes = Array . from ( { length : testIterations } , ( ) = >
` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } `
)
for ( const nodeId of sampleNodes ) {
const startTime = performance . now ( )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
const neighbors = await graphIndex . getNeighbors ( entityInt ( nodeId ) )
2025-09-11 16:23:32 -07:00
const elapsed = performance . now ( ) - startTime
lookupStats . addSample ( elapsed )
// Each lookup should be sub-millisecond
expect ( elapsed ) . toBeLessThan ( PERFORMANCE_TARGETS . O1_LOOKUP )
expect ( Array . isArray ( neighbors ) ) . toBe ( true )
}
console . log ( ` ✅ O(1) Lookup Performance: ${ lookupStats . toString ( ) } ` )
console . log ( ` Target: < ${ PERFORMANCE_TARGETS . O1_LOOKUP } ms per lookup ` )
console . log ( ` Best: ${ lookupStats . min . toFixed ( 3 ) } ms, Worst: ${ lookupStats . max . toFixed ( 3 ) } ms ` )
// Statistical validation
expect ( lookupStats . p95 ) . toBeLessThan ( PERFORMANCE_TARGETS . O1_LOOKUP )
expect ( lookupStats . p99 ) . toBeLessThan ( PERFORMANCE_TARGETS . O1_LOOKUP * 2 ) // Allow some variance for p99
} )
it ( 'should validate memory usage (~24 bytes per relationship)' , async ( ) = > {
const stats = graphIndex . getStats ( )
console . log ( ` \ n💾 Memory Usage Analysis: ` )
console . log ( ` Total relationships: ${ stats . totalRelationships . toLocaleString ( ) } ` )
console . log ( ` Source nodes: ${ stats . sourceNodes . toLocaleString ( ) } ` )
console . log ( ` Target nodes: ${ stats . targetNodes . toLocaleString ( ) } ` )
console . log ( ` Memory usage: ${ ( stats . memoryUsage / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
const bytesPerRelationship = stats . memoryUsage / stats . totalRelationships
console . log ( ` Bytes per relationship: ${ bytesPerRelationship . toFixed ( 1 ) } ` )
// Validate memory efficiency
expect ( bytesPerRelationship ) . toBeLessThan ( PERFORMANCE_TARGETS . MEMORY_PER_REL * 1.5 ) // Allow 50% margin
expect ( bytesPerRelationship ) . toBeGreaterThan ( PERFORMANCE_TARGETS . MEMORY_PER_REL * 0.5 ) // Don't be too efficient (might indicate missing data)
// Memory should scale linearly with relationships
expect ( stats . memoryUsage ) . toBeGreaterThan ( 0 )
} )
it ( 'should validate index update performance (<5ms per relationship amortized)' , async ( ) = > {
console . log ( ` \ n⚡ Testing index update performance... ` )
// Test batch updates
const batchSize = 100
const testBatches = Math . min ( 10 , Math . floor ( scale . nodes / batchSize ) )
for ( let batch = 0 ; batch < testBatches ; batch ++ ) {
const startTime = performance . now ( )
// Add relationships in batch
const relationships = [ ]
for ( let i = 0 ; i < batchSize ; i ++ ) {
const sourceId = ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } `
const targetId = ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } `
relationships . push ( {
from : sourceId ,
to : targetId ,
type : 'test_relationship' ,
metadata : { batch , index : i }
} )
}
await brain . relateMany ( relationships )
const elapsed = performance . now ( ) - startTime
const amortizedTime = elapsed / batchSize
updateStats . addSample ( amortizedTime )
// Each update should be fast
expect ( amortizedTime ) . toBeLessThan ( PERFORMANCE_TARGETS . INDEX_UPDATE )
}
console . log ( ` ✅ Index Update Performance: ${ updateStats . toString ( ) } ` )
console . log ( ` Target: < ${ PERFORMANCE_TARGETS . INDEX_UPDATE } ms amortized per relationship ` )
// Statistical validation
expect ( updateStats . p95 ) . toBeLessThan ( PERFORMANCE_TARGETS . INDEX_UPDATE * 1.5 )
} )
it ( 'should validate rebuild performance from storage' , async ( ) = > {
console . log ( ` \ n🔄 Testing index rebuild performance... ` )
const startTime = performance . now ( )
await graphIndex . rebuild ( )
const rebuildTime = performance . now ( ) - startTime
const rebuildRate = scale . relationships / ( rebuildTime / 1000 ) // relationships per second
console . log ( ` ✅ Rebuild Performance: ` )
console . log ( ` Total time: ${ ( rebuildTime / 1000 ) . toFixed ( 2 ) } s ` )
console . log ( ` Rate: ${ rebuildRate . toFixed ( 0 ) } relationships/second ` )
console . log ( ` Target: > ${ PERFORMANCE_TARGETS . REBUILD_RATE } relationships/second ` )
// Validate rebuild performance
expect ( rebuildRate ) . toBeGreaterThan ( PERFORMANCE_TARGETS . REBUILD_RATE )
// Rebuild should complete within reasonable time
const expectedMaxTime = scale . relationships / PERFORMANCE_TARGETS . REBUILD_RATE * 1000
expect ( rebuildTime ) . toBeLessThan ( expectedMaxTime * 2 ) // Allow 2x margin
// Verify index integrity after rebuild
const stats = graphIndex . getStats ( )
expect ( stats . totalRelationships ) . toBeGreaterThan ( 0 )
expect ( stats . sourceNodes ) . toBeGreaterThan ( 0 )
expect ( stats . targetNodes ) . toBeGreaterThan ( 0 )
} )
} )
describe ( '2. Large-Scale Graph Operations' , ( ) = > {
it ( 'should handle 100K+ relationship graph construction' , async ( ) = > {
console . log ( ` \ n🏗️ Testing large-scale graph construction... ` )
const constructionStart = performance . now ( )
// Add additional relationships to reach target scale
const additionalRelationships = Math . max ( 0 , 100000 - scale . relationships )
if ( additionalRelationships > 0 ) {
const batchSize = 1000
let added = 0
while ( added < additionalRelationships ) {
const batch = [ ]
for ( let i = 0 ; i < Math . min ( batchSize , additionalRelationships - added ) ; i ++ ) {
batch . push ( {
from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` ,
to : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` ,
type : 'bulk_relationship' ,
metadata : { batchId : Math.floor ( added / batchSize ) }
} )
}
await brain . relateMany ( batch )
added += batch . length
}
}
const constructionTime = performance . now ( ) - constructionStart
console . log ( ` ✅ Large-scale construction: ` )
console . log ( ` Time: ${ ( constructionTime / 1000 ) . toFixed ( 2 ) } s ` )
console . log ( ` Rate: ${ ( scale . relationships / ( constructionTime / 1000 ) ) . toFixed ( 0 ) } relationships/s ` )
// Construction should be efficient
expect ( constructionTime ) . toBeLessThan ( 300000 ) // Less than 5 minutes
} )
it ( 'should handle million-node graph traversal' , async ( ) = > {
console . log ( ` \ n🚶 Testing large graph traversal... ` )
// Test traversal from multiple starting points
const startNodes = [ 'node-0' , 'node-100' , 'node-1000' , 'node-10000' ]
const traversalStats = new PerformanceStats ( )
for ( const startNode of startNodes ) {
const startTime = performance . now ( )
// Perform BFS traversal with depth limit
const visited = new Set < string > ( )
const queue : Array < { id : string ; depth : number } > = [ { id : startNode , depth : 0 } ]
let nodesTraversed = 0
const maxDepth = 3
const maxNodes = 1000
while ( queue . length > 0 && nodesTraversed < maxNodes ) {
const { id , depth } = queue . shift ( ) !
if ( visited . has ( id ) || depth > maxDepth ) continue
visited . add ( id )
nodesTraversed ++
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
// Get neighbors (BigInt boundary: ints out, mapped back to UUIDs)
const neighborInts = await graphIndex . getNeighbors ( entityInt ( id ) , { direction : 'out' } )
for ( const neighbor of intsToUuids ( neighborInts ) ) {
2025-09-11 16:23:32 -07:00
if ( ! visited . has ( neighbor ) ) {
queue . push ( { id : neighbor , depth : depth + 1 } )
}
}
}
const traversalTime = performance . now ( ) - startTime
traversalStats . addSample ( traversalTime )
console . log ( ` ${ startNode } : ${ nodesTraversed } nodes in ${ ( traversalTime ) . toFixed ( 2 ) } ms ` )
}
console . log ( ` ✅ Graph traversal performance: ${ traversalStats . toString ( ) } ` )
// Traversal should be fast
expect ( traversalStats . p95 ) . toBeLessThan ( 100 ) // <100ms for traversal
} )
it ( 'should handle complex graph query patterns' , async ( ) = > {
console . log ( ` \ n🔍 Testing complex graph query patterns... ` )
const queryPatterns = [
{ name : 'Single node neighbors' , query : { connected : { from : 'node-100' } } } ,
{ name : 'Bidirectional connections' , query : { connected : { from : 'node-200' , direction : 'both' } } } ,
{ name : 'Multi-hop paths' , query : { connected : { from : 'node-300' , depth : 2 } } } ,
{ name : 'Filtered connections' , query : { connected : { from : 'node-400' } , where : { type : 'follows' } } }
]
const patternStats = new PerformanceStats ( )
for ( const pattern of queryPatterns ) {
const startTime = performance . now ( )
const results = await brain . find ( pattern . query )
const elapsed = performance . now ( ) - startTime
patternStats . addSample ( elapsed )
console . log ( ` ${ pattern . name } : ${ results . length } results in ${ elapsed . toFixed ( 2 ) } ms ` )
// Complex queries should still be fast
expect ( elapsed ) . toBeLessThan ( 500 ) // <500ms for complex queries
expect ( Array . isArray ( results ) ) . toBe ( true )
}
console . log ( ` ✅ Complex query performance: ${ patternStats . toString ( ) } ` )
} )
it ( 'should validate memory efficiency under scale' , async ( ) = > {
console . log ( ` \ n📊 Memory efficiency analysis under scale... ` )
const initialMemory = process . memoryUsage ( )
const initialHeapUsed = initialMemory . heapUsed
// Perform memory-intensive operations
const operations = [ ]
for ( let i = 0 ; i < 100 ; i ++ ) {
operations . push (
brain . find ( { connected : { from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` , depth : 2 } } )
)
}
await Promise . all ( operations )
const finalMemory = process . memoryUsage ( )
const finalHeapUsed = finalMemory . heapUsed
const memoryDelta = finalHeapUsed - initialHeapUsed
console . log ( ` ✅ Memory efficiency: ` )
console . log ( ` Initial heap: ${ ( initialHeapUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Final heap: ${ ( finalHeapUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Delta: ${ ( memoryDelta / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
// Memory usage should be reasonable
expect ( memoryDelta ) . toBeLessThan ( 100 * 1024 * 1024 ) // Less than 100MB increase
// Force garbage collection if available
if ( global . gc ) {
global . gc ( )
const afterGc = process . memoryUsage ( )
console . log ( ` After GC: ${ ( afterGc . heapUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
}
} )
} )
describe ( '3. Unified find() Performance' , ( ) = > {
it ( 'should handle vector+graph+fields combined queries at scale' , async ( ) = > {
console . log ( ` \ n🔗 Testing unified find() with combined queries... ` )
const combinedQueries = [
{
name : 'Vector + Graph' ,
query : {
similar : 'technology artificial intelligence' ,
connected : { from : 'node-1000' , depth : 1 } ,
limit : 20
}
} ,
{
name : 'Vector + Fields' ,
query : {
similar : 'machine learning' ,
where : { category : 'tech' , type : 'document' } ,
limit : 20
}
} ,
{
name : 'Graph + Fields' ,
query : {
connected : { from : 'node-2000' , depth : 2 } ,
where : { created : { $gt : Date.now ( ) - 86400000 } } , // Last 24h
limit : 20
}
} ,
{
name : 'Triple Intelligence' ,
query : {
similar : 'neural networks' ,
connected : { from : 'node-3000' } ,
where : { category : 'science' } ,
limit : 20
}
}
]
const unifiedStats = new PerformanceStats ( )
for ( const testCase of combinedQueries ) {
const startTime = performance . now ( )
const results = await brain . find ( testCase . query )
const elapsed = performance . now ( ) - startTime
unifiedStats . addSample ( elapsed )
console . log ( ` ${ testCase . name } : ${ results . length } results in ${ elapsed . toFixed ( 2 ) } ms ` )
// Unified queries should be efficient
expect ( elapsed ) . toBeLessThan ( 1000 ) // <1s for combined queries
expect ( results . length ) . toBeGreaterThan ( 0 )
expect ( results [ 0 ] . score ) . toBeDefined ( )
}
console . log ( ` ✅ Unified query performance: ${ unifiedStats . toString ( ) } ` )
} )
it ( 'should validate parallel execution performance' , async ( ) = > {
console . log ( ` \ n⚡ Testing parallel query execution... ` )
const parallelQueries = Array . from ( { length : 10 } , ( _ , i ) = > ( {
similar : ` query ${ i } ` ,
connected : { from : ` node- ${ i * 1000 } ` , depth : 1 } ,
where : { category : [ 'tech' , 'science' , 'business' ] [ i % 3 ] } ,
limit : 10
} ) )
const parallelStart = performance . now ( )
const results = await Promise . all ( parallelQueries . map ( query = > brain . find ( query ) ) )
const parallelTime = performance . now ( ) - parallelStart
const sequentialStart = performance . now ( )
for ( const query of parallelQueries ) {
await brain . find ( query )
}
const sequentialTime = performance . now ( ) - sequentialStart
const speedup = sequentialTime / parallelTime
console . log ( ` ✅ Parallel execution: ` )
console . log ( ` Parallel time: ${ parallelTime . toFixed ( 2 ) } ms ` )
console . log ( ` Sequential time: ${ sequentialTime . toFixed ( 2 ) } ms ` )
console . log ( ` Speedup: ${ speedup . toFixed ( 2 ) } x ` )
// Parallel execution should provide speedup
expect ( speedup ) . toBeGreaterThan ( 1.5 ) // At least 1.5x speedup
expect ( results . length ) . toBe ( 10 )
results . forEach ( resultSet = > {
expect ( Array . isArray ( resultSet ) ) . toBe ( true )
expect ( resultSet . length ) . toBeGreaterThan ( 0 )
} )
} )
it ( 'should validate query optimization effectiveness' , async ( ) = > {
console . log ( ` \ n🎯 Testing query optimization effectiveness... ` )
// Test different query patterns to see optimization effectiveness
const optimizationTests = [
{
name : 'ID lookup (fast path)' ,
query : { id : 'node-100' } ,
expectedTime : 1
} ,
{
name : 'Multiple IDs (fast path)' ,
query : { ids : [ 'node-100' , 'node-200' , 'node-300' ] } ,
expectedTime : 5
} ,
{
name : 'Vector search only' ,
query : { similar : 'test query' , limit : 10 } ,
expectedTime : 50
} ,
{
name : 'Metadata filter only' ,
query : { where : { category : 'tech' } , limit : 10 } ,
expectedTime : 20
} ,
{
name : 'Graph traversal only' ,
query : { connected : { from : 'node-1000' } , limit : 10 } ,
expectedTime : 30
}
]
const optimizationStats = new PerformanceStats ( )
for ( const test of optimizationTests ) {
const startTime = performance . now ( )
const results = await brain . find ( test . query )
const elapsed = performance . now ( ) - startTime
optimizationStats . addSample ( elapsed )
console . log ( ` ${ test . name } : ${ elapsed . toFixed ( 2 ) } ms (target: < ${ test . expectedTime } ms) ` )
// Each query should meet its performance target
expect ( elapsed ) . toBeLessThan ( test . expectedTime * 2 ) // Allow 2x margin
expect ( Array . isArray ( results ) ) . toBe ( true )
}
console . log ( ` ✅ Query optimization: ${ optimizationStats . toString ( ) } ` )
} )
it ( 'should validate memory usage during large queries' , async ( ) = > {
console . log ( ` \ n💾 Memory usage during large queries... ` )
const initialMemory = process . memoryUsage ( )
// Execute large queries
const largeQueries = [
brain . find ( { similar : 'comprehensive test' , limit : 100 } ) ,
brain . find ( { where : { category : 'tech' } , limit : 100 } ) ,
brain . find ( { connected : { from : 'node-1000' , depth : 3 } , limit : 100 } ) ,
brain . find ( {
similar : 'large scale' ,
connected : { from : 'node-2000' , depth : 2 } ,
where : { type : 'document' } ,
limit : 100
} )
]
await Promise . all ( largeQueries )
const finalMemory = process . memoryUsage ( )
const memoryIncrease = finalMemory . heapUsed - initialMemory . heapUsed
console . log ( ` ✅ Large query memory usage: ` )
console . log ( ` Memory increase: ${ ( memoryIncrease / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Peak RSS: ${ ( finalMemory . rss / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
// Memory usage should be reasonable for large queries
expect ( memoryIncrease ) . toBeLessThan ( 50 * 1024 * 1024 ) // Less than 50MB increase
} )
} )
describe ( '4. Concurrent Load Testing' , ( ) = > {
it ( 'should handle multiple concurrent graph operations' , async ( ) = > {
console . log ( ` \ n🔄 Testing concurrent graph operations... ` )
const concurrentOps = Math . min ( scale . concurrentOps , PERFORMANCE_TARGETS . CONCURRENT_LOAD )
const operations : Promise < any > [ ] = [ ]
// Mix of different operation types
for ( let i = 0 ; i < concurrentOps ; i ++ ) {
const operationType = i % 4
switch ( operationType ) {
case 0 : // Neighbor lookup
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
operations . push ( graphIndex . getNeighbors ( entityInt ( ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` ) ) )
2025-09-11 16:23:32 -07:00
break
case 1 : // Unified find
operations . push ( brain . find ( {
connected : { from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` } ,
limit : 5
} ) )
break
case 2 : // Relationship addition
operations . push ( brain . relate ( {
from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` ,
to : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` ,
type : 'concurrent_test'
} ) )
break
case 3 : // Complex query
operations . push ( brain . find ( {
similar : ` concurrent query ${ i } ` ,
where : { category : [ 'tech' , 'science' ] [ i % 2 ] } ,
limit : 3
} ) )
break
}
}
const concurrentStart = performance . now ( )
const results = await Promise . all ( operations )
const concurrentTime = performance . now ( ) - concurrentStart
console . log ( ` ✅ Concurrent operations: ` )
console . log ( ` ${ concurrentOps } operations completed in ${ concurrentTime . toFixed ( 2 ) } ms ` )
console . log ( ` Average time per operation: ${ ( concurrentTime / concurrentOps ) . toFixed ( 2 ) } ms ` )
// Concurrent operations should complete efficiently
expect ( concurrentTime ) . toBeLessThan ( 5000 ) // Less than 5 seconds for all operations
expect ( results . length ) . toBe ( concurrentOps )
} )
it ( 'should handle spike testing for sudden traffic increases' , async ( ) = > {
console . log ( ` \ n📈 Testing traffic spike handling... ` )
const spikeLevels = [ 10 , 50 , 100 , 200 ]
const spikeResults : number [ ] = [ ]
for ( const spikeLevel of spikeLevels ) {
const spikeOperations = Array . from ( { length : spikeLevel } , ( ) = >
brain . find ( { connected : { from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` } } )
)
const spikeStart = performance . now ( )
await Promise . all ( spikeOperations )
const spikeTime = performance . now ( ) - spikeStart
spikeResults . push ( spikeTime )
console . log ( ` Spike ${ spikeLevel } : ${ spikeTime . toFixed ( 2 ) } ms ( ${ ( spikeTime / spikeLevel ) . toFixed ( 2 ) } ms/op) ` )
// Even under spike, performance should be reasonable
expect ( spikeTime ) . toBeLessThan ( spikeLevel * 50 ) // <50ms per operation on average
}
// Performance should degrade gracefully under load
const degradation = spikeResults [ spikeResults . length - 1 ] / spikeResults [ 0 ]
console . log ( ` Performance degradation: ${ degradation . toFixed ( 2 ) } x under 20x load increase ` )
// Allow some degradation but not exponential
expect ( degradation ) . toBeLessThan ( 10 ) // Less than 10x slower under 20x load
} )
it ( 'should detect memory leaks under sustained load' , async ( ) = > {
console . log ( ` \ n🕵️ Testing memory leak detection... ` )
const leakTestDuration = 30000 // 30 seconds
const leakTestStart = Date . now ( )
const memorySamples : number [ ] = [ ]
// Run continuous operations for leak detection
while ( Date . now ( ) - leakTestStart < leakTestDuration ) {
const operations = Array . from ( { length : 10 } , ( ) = >
brain . find ( { connected : { from : ` node- ${ Math . floor ( Math . random ( ) * scale . nodes ) } ` } } )
)
await Promise . all ( operations )
// Sample memory usage
const memUsage = process . memoryUsage ( )
memorySamples . push ( memUsage . heapUsed )
// Small delay to prevent overwhelming the system
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
}
const initialMemory = memorySamples [ 0 ]
const finalMemory = memorySamples [ memorySamples . length - 1 ]
const memoryGrowth = finalMemory - initialMemory
const growthRate = memoryGrowth / leakTestDuration * 1000 // bytes per second
console . log ( ` ✅ Memory leak analysis: ` )
console . log ( ` Initial memory: ${ ( initialMemory / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Final memory: ${ ( finalMemory / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Growth: ${ ( memoryGrowth / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Growth rate: ${ ( growthRate / 1024 ) . toFixed ( 2 ) } KB/s ` )
// Memory growth should be minimal (less than 10MB over 30 seconds)
expect ( memoryGrowth ) . toBeLessThan ( 10 * 1024 * 1024 )
// Growth rate should be very low
expect ( growthRate ) . toBeLessThan ( 100 * 1024 ) // Less than 100KB/s growth
} )
it ( 'should validate resource exhaustion handling' , async ( ) = > {
console . log ( ` \ n🚨 Testing resource exhaustion handling... ` )
const exhaustionTests = [
{
name : 'Deep recursion' ,
operation : ( ) = > brain . find ( { connected : { from : 'node-0' , depth : 10 } } )
} ,
{
name : 'Large result sets' ,
operation : ( ) = > brain . find ( { connected : { from : 'node-1000' , depth : 5 } , limit : 10000 } )
} ,
{
name : 'Complex filters' ,
operation : ( ) = > brain . find ( {
where : {
$and : [
{ category : 'tech' } ,
{ type : 'document' } ,
{ created : { $gt : Date.now ( ) - 86400000 } } ,
{ score : { $gt : 0.5 } }
]
} ,
limit : 1000
} )
}
]
for ( const test of exhaustionTests ) {
const startTime = performance . now ( )
try {
const result = await test . operation ( )
const elapsed = performance . now ( ) - startTime
console . log ( ` ${ test . name } : ${ elapsed . toFixed ( 2 ) } ms ( ${ Array . isArray ( result ) ? result . length : 'N/A' } results) ` )
// Operations should complete without throwing
expect ( elapsed ) . toBeLessThan ( 10000 ) // Less than 10 seconds
} catch ( error ) {
console . log ( ` ${ test . name } : Failed with ${ error . message } ` )
// Some operations might legitimately fail under extreme conditions
expect ( error . message ) . toMatch ( /timeout|limit|memory|recursion/i )
}
}
console . log ( ` ✅ Resource exhaustion handling validated ` )
} )
} )
describe ( '5. Real-World Scenarios' , ( ) = > {
it ( 'should handle social network analysis (friends, followers, connections)' , async ( ) = > {
console . log ( ` \ n👥 Testing social network analysis... ` )
// Create a social network scenario
const socialUsers = Array . from ( { length : 1000 } , ( _ , i ) = > ` user- ${ i } ` )
const socialRelationships = [ ]
// Create follower relationships (scale-free network)
for ( let i = 0 ; i < socialUsers . length ; i ++ ) {
const followerCount = Math . floor ( Math . random ( ) * 50 ) + 1 // 1-50 followers
for ( let j = 0 ; j < followerCount ; j ++ ) {
const targetUser = socialUsers [ Math . floor ( Math . random ( ) * socialUsers . length ) ]
if ( targetUser !== socialUsers [ i ] ) {
socialRelationships . push ( {
from : socialUsers [ i ] ,
to : targetUser ,
type : 'follows' ,
metadata : { strength : Math.random ( ) }
} )
}
}
}
await brain . relateMany ( socialRelationships )
// Test social network queries
const socialQueries = [
{
name : 'Find influencers' ,
query : { connected : { from : 'user-0' , direction : 'in' } , limit : 20 }
} ,
{
name : 'Find following' ,
query : { connected : { from : 'user-100' , direction : 'out' } , limit : 20 }
} ,
{
name : 'Mutual connections' ,
query : {
connected : { from : 'user-200' , direction : 'both' } ,
where : { type : 'follows' } ,
limit : 20
}
}
]
const socialStats = new PerformanceStats ( )
for ( const socialQuery of socialQueries ) {
const startTime = performance . now ( )
const results = await brain . find ( socialQuery . query )
const elapsed = performance . now ( ) - startTime
socialStats . addSample ( elapsed )
console . log ( ` ${ socialQuery . name } : ${ results . length } connections in ${ elapsed . toFixed ( 2 ) } ms ` )
}
console . log ( ` ✅ Social network performance: ${ socialStats . toString ( ) } ` )
expect ( socialStats . p95 ) . toBeLessThan ( 100 )
} )
it ( 'should handle knowledge graph traversal (entity relationships)' , async ( ) = > {
console . log ( ` \ n🧠 Testing knowledge graph traversal... ` )
// Create knowledge graph entities
const entities = [
'Machine Learning' , 'Neural Networks' , 'Deep Learning' , 'AI' , 'Computer Vision' ,
'Natural Language Processing' , 'Supervised Learning' , 'Unsupervised Learning' ,
'Reinforcement Learning' , 'Data Science' , 'Statistics' , 'Python' , 'TensorFlow'
]
// Create semantic relationships
const knowledgeRelationships = [
{ from : 'Machine Learning' , to : 'AI' , type : 'subfield_of' } ,
{ from : 'Deep Learning' , to : 'Machine Learning' , type : 'subfield_of' } ,
{ from : 'Neural Networks' , to : 'Deep Learning' , type : 'foundation_of' } ,
{ from : 'Computer Vision' , to : 'AI' , type : 'application_of' } ,
{ from : 'Natural Language Processing' , to : 'AI' , type : 'application_of' } ,
{ from : 'Supervised Learning' , to : 'Machine Learning' , type : 'type_of' } ,
{ from : 'Unsupervised Learning' , to : 'Machine Learning' , type : 'type_of' } ,
{ from : 'Reinforcement Learning' , to : 'Machine Learning' , type : 'type_of' } ,
{ from : 'Data Science' , to : 'Machine Learning' , type : 'uses' } ,
{ from : 'Statistics' , to : 'Data Science' , type : 'foundation_of' } ,
{ from : 'Python' , to : 'Machine Learning' , type : 'tool_for' } ,
{ from : 'TensorFlow' , to : 'Machine Learning' , type : 'tool_for' }
]
// Add entities and relationships
for ( const entity of entities ) {
await brain . add ( {
id : entity ,
data : ` Knowledge about ${ entity } ` ,
metadata : { type : 'concept' , domain : 'AI' }
} )
}
await brain . relateMany ( knowledgeRelationships )
// Test knowledge graph queries
const knowledgeQueries = [
{
name : 'Find related concepts' ,
query : { connected : { from : 'Machine Learning' , depth : 2 } , limit : 15 }
} ,
{
name : 'Find applications' ,
query : {
connected : { from : 'AI' , direction : 'in' } ,
where : { type : 'application_of' } ,
limit : 10
}
} ,
{
name : 'Semantic path finding' ,
query : {
similar : 'artificial intelligence applications' ,
connected : { from : 'AI' , depth : 3 } ,
limit : 20
}
}
]
const knowledgeStats = new PerformanceStats ( )
for ( const kgQuery of knowledgeQueries ) {
const startTime = performance . now ( )
const results = await brain . find ( kgQuery . query )
const elapsed = performance . now ( ) - startTime
knowledgeStats . addSample ( elapsed )
console . log ( ` ${ kgQuery . name } : ${ results . length } concepts in ${ elapsed . toFixed ( 2 ) } ms ` )
}
console . log ( ` ✅ Knowledge graph performance: ${ knowledgeStats . toString ( ) } ` )
expect ( knowledgeStats . p95 ) . toBeLessThan ( 200 )
} )
it ( 'should handle recommendation system queries' , async ( ) = > {
console . log ( ` \ n🎯 Testing recommendation system queries... ` )
// Create recommendation scenario with users, items, and ratings
const users = Array . from ( { length : 500 } , ( _ , i ) = > ` user- ${ i } ` )
const items = Array . from ( { length : 200 } , ( _ , i ) = > ` item- ${ i } ` )
const categories = [ 'electronics' , 'books' , 'clothing' , 'movies' , 'music' ]
// Add users and items
for ( const user of users ) {
await brain . add ( {
id : user ,
data : ` User profile for ${ user } ` ,
metadata : { type : 'user' , category : 'consumer' }
} )
}
for ( const item of items ) {
const category = categories [ Math . floor ( Math . random ( ) * categories . length ) ]
await brain . add ( {
id : item ,
data : ` Product: ${ item } ` ,
metadata : { type : 'product' , category , price : Math.random ( ) * 100 }
} )
}
// Create purchase/rating relationships
const purchaseRelationships = [ ]
for ( let i = 0 ; i < 2000 ; i ++ ) {
const user = users [ Math . floor ( Math . random ( ) * users . length ) ]
const item = items [ Math . floor ( Math . random ( ) * items . length ) ]
const rating = Math . floor ( Math . random ( ) * 5 ) + 1
purchaseRelationships . push ( {
from : user ,
to : item ,
type : 'purchased' ,
metadata : { rating , timestamp : Date.now ( ) - Math . random ( ) * 2592000000 } // Random within 30 days
} )
}
await brain . relateMany ( purchaseRelationships )
// Test recommendation queries
const recommendationQueries = [
{
name : 'User purchase history' ,
query : { connected : { from : 'user-100' , direction : 'out' } , limit : 10 }
} ,
{
name : 'Item popularity' ,
query : { connected : { from : 'item-50' , direction : 'in' } , limit : 15 }
} ,
{
name : 'Similar user recommendations' ,
query : {
connected : { from : 'user-200' , direction : 'out' } ,
where : { rating : { $gte : 4 } } ,
limit : 10
}
} ,
{
name : 'Category-based recommendations' ,
query : {
similar : 'electronics gadgets' ,
connected : { from : 'user-300' , depth : 2 } ,
where : { category : 'electronics' } ,
limit : 15
}
}
]
const recommendationStats = new PerformanceStats ( )
for ( const recQuery of recommendationQueries ) {
const startTime = performance . now ( )
const results = await brain . find ( recQuery . query )
const elapsed = performance . now ( ) - startTime
recommendationStats . addSample ( elapsed )
console . log ( ` ${ recQuery . name } : ${ results . length } recommendations in ${ elapsed . toFixed ( 2 ) } ms ` )
}
console . log ( ` ✅ Recommendation system performance: ${ recommendationStats . toString ( ) } ` )
expect ( recommendationStats . p95 ) . toBeLessThan ( 150 )
} )
it ( 'should handle path finding and network analysis' , async ( ) = > {
console . log ( ` \ n🛣️ Testing path finding and network analysis... ` )
// Create a network topology for path finding
const networkNodes = Array . from ( { length : 100 } , ( _ , i ) = > ` network- ${ i } ` )
const networkConnections = [ ]
// Create a mesh network with some clustering
for ( let i = 0 ; i < networkNodes . length ; i ++ ) {
const connections = Math . floor ( Math . random ( ) * 5 ) + 2 // 2-6 connections per node
for ( let j = 0 ; j < connections ; j ++ ) {
let targetIndex = i + Math . floor ( Math . random ( ) * 10 ) - 5 // Nearby nodes
if ( targetIndex < 0 ) targetIndex = 0
if ( targetIndex >= networkNodes . length ) targetIndex = networkNodes . length - 1
const targetNode = networkNodes [ targetIndex ]
if ( targetNode !== networkNodes [ i ] ) {
networkConnections . push ( {
from : networkNodes [ i ] ,
to : targetNode ,
type : 'connected_to' ,
metadata : {
latency : Math.random ( ) * 100 + 1 , // 1-100ms latency
bandwidth : Math.random ( ) * 1000 + 100 // 100-1100 Mbps
}
} )
}
}
}
// Add network nodes and connections
for ( const node of networkNodes ) {
await brain . add ( {
id : node ,
data : ` Network node ${ node } ` ,
metadata : { type : 'network_node' , capacity : Math.random ( ) * 1000 }
} )
}
await brain . relateMany ( networkConnections )
// Test network analysis queries
const networkQueries = [
{
name : 'Shortest path analysis' ,
query : { connected : { from : 'network-0' , depth : 4 } , limit : 20 }
} ,
{
name : 'Network centrality' ,
query : { connected : { from : 'network-50' , direction : 'both' } , limit : 25 }
} ,
{
name : 'Bottleneck detection' ,
query : {
connected : { from : 'network-25' , depth : 3 } ,
where : { capacity : { $lt : 500 } } ,
limit : 15
}
} ,
{
name : 'Network health analysis' ,
query : {
similar : 'network connectivity' ,
connected : { from : 'network-75' , depth : 2 } ,
where : { latency : { $lt : 50 } } ,
limit : 20
}
}
]
const networkStats = new PerformanceStats ( )
for ( const netQuery of networkQueries ) {
const startTime = performance . now ( )
const results = await brain . find ( netQuery . query )
const elapsed = performance . now ( ) - startTime
networkStats . addSample ( elapsed )
console . log ( ` ${ netQuery . name } : ${ results . length } paths in ${ elapsed . toFixed ( 2 ) } ms ` )
}
console . log ( ` ✅ Network analysis performance: ${ networkStats . toString ( ) } ` )
expect ( networkStats . p95 ) . toBeLessThan ( 120 )
} )
} )
describe ( 'Performance Summary & Validation' , ( ) = > {
it ( 'should provide comprehensive performance report' , async ( ) = > {
console . log ( ` \ n📊 ===== COMPREHENSIVE PERFORMANCE REPORT ===== ` )
const finalStats = graphIndex . getStats ( )
const memoryUsage = process . memoryUsage ( )
console . log ( ` \ n🎯 PERFORMANCE TARGETS VALIDATION: ` )
console . log ( ` ✅ O(1) Lookup: ${ lookupStats . p95 . toFixed ( 3 ) } ms < ${ PERFORMANCE_TARGETS . O1_LOOKUP } ms target ` )
console . log ( ` ✅ Memory/Rel: ${ ( finalStats . memoryUsage / finalStats . totalRelationships ) . toFixed ( 1 ) } bytes < ${ PERFORMANCE_TARGETS . MEMORY_PER_REL } target ` )
console . log ( ` ✅ Update: ${ updateStats . p95 . toFixed ( 2 ) } ms < ${ PERFORMANCE_TARGETS . INDEX_UPDATE } ms target ` )
console . log ( ` ✅ Rebuild: ${ ( finalStats . totalRelationships / ( finalStats . rebuildTime / 1000 ) ) . toFixed ( 0 ) } rel/s > ${ PERFORMANCE_TARGETS . REBUILD_RATE } target ` )
console . log ( ` \ n📈 SCALE METRICS: ` )
console . log ( ` Relationships: ${ finalStats . totalRelationships . toLocaleString ( ) } ` )
console . log ( ` Source Nodes: ${ finalStats . sourceNodes . toLocaleString ( ) } ` )
console . log ( ` Target Nodes: ${ finalStats . targetNodes . toLocaleString ( ) } ` )
console . log ( ` Memory Usage: ${ ( finalStats . memoryUsage / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` Heap Usage: ${ ( memoryUsage . heapUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` )
console . log ( ` \ n⚡ PERFORMANCE STATISTICS: ` )
console . log ( ` Lookup Performance: ${ lookupStats . toString ( ) } ` )
console . log ( ` Update Performance: ${ updateStats . toString ( ) } ` )
console . log ( ` Memory Efficiency: ${ memoryStats . toString ( ) } ` )
console . log ( ` \ n🏆 VALIDATION RESULTS: ` )
// Validate all performance targets
const validations = [
{ name : 'O(1) Neighbor Lookup' , value : lookupStats.p95 , target : PERFORMANCE_TARGETS.O1_LOOKUP , condition : '<' } ,
{ name : 'Memory per Relationship' , value : finalStats.memoryUsage / finalStats . totalRelationships , target : PERFORMANCE_TARGETS.MEMORY_PER_REL , condition : '<' } ,
{ name : 'Index Update Performance' , value : updateStats.p95 , target : PERFORMANCE_TARGETS.INDEX_UPDATE , condition : '<' } ,
{ name : 'Rebuild Rate' , value : finalStats.totalRelationships / ( finalStats . rebuildTime / 1000 ) , target : PERFORMANCE_TARGETS.REBUILD_RATE , condition : '>' }
]
let allPassed = true
for ( const validation of validations ) {
const passed = validation . condition === '<'
? validation . value < validation . target
: validation . value > validation . target
const status = passed ? '✅ PASS' : '❌ FAIL'
console . log ( ` ${ status } ${ validation . name } : ${ validation . value . toFixed ( 2 ) } ${ validation . condition } ${ validation . target } ` )
if ( ! passed ) allPassed = false
}
console . log ( ` \ n🎉 OVERALL RESULT: ${ allPassed ? 'ALL TARGETS MET' : 'SOME TARGETS MISSED' } ` )
console . log ( ` ==================================================== \ n ` )
// Final validation
expect ( allPassed ) . toBe ( true )
} )
} )
} )