Skip to content

Commit 38f165a

Browse files
fix(memory): V4 bundle wiring, region slice reservation, and BM25 sizing (#508)
Fixes V4 bundle ingestion & sidecar elimination: - Default useBundleMode to true in SpectorMemoryBuilder per ADR-0004 - Reserve 32B/entity for name index in ENTITY_NAMES region so adjCap calculation doesn't overflow slice - Store checkpointRegion in CoActivationRecordMemory constructor so save() writes to CHECKPOINT region - Pass checkpointRegion slice to CheckpointDaemon and CoActivationRecordMemory - Increase BM25 initial region size formula to scale from episodicPartitionCapacity (4MB min) - Check for CHECKPOINT and BM25 regions when validating open runtime bundles - Prefer BM25 region save on close/rebuild, avoiding unneeded standalone bm25.bidx creation Co-authored-by: Bharat Joshi <bharatjoshi@spectrayan.com>
1 parent a83a8a7 commit 38f165a

10 files changed

Lines changed: 48 additions & 22 deletions

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/BiologicalSubsystemsBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ static BiologicalSubsystems build(SpectorMemoryBuilder builder,
7676
if (cortex.useBundleMode() && cortex.runtimeBundle() != null) {
7777
java.lang.foreign.MemorySegment regionSlice = cortex.runtimeBundle().regionSegment(com.spectrayan.spector.memory.kernel.bundle.RegionId.COACTIVATION);
7878
boolean isNew = !com.spectrayan.spector.memory.kernel.MemoryHeader.isValid(regionSlice, 0L);
79+
java.lang.foreign.MemorySegment ckptSlice = cortex.runtimeBundle().regionSegment(com.spectrayan.spector.memory.kernel.bundle.RegionId.CHECKPOINT);
7980
coActivationTracker = CoActivationRecordMemory.fromBundle(
8081
cortex.runtimeBundle().arena(), regionSlice, 10_000, 20_000,
81-
StorageLayout.coactivationTracker(basePath), isNew);
82+
StorageLayout.coactivationTracker(basePath), isNew, ckptSlice);
8283
} else if (isDisk && basePath != null) {
8384
coActivationTracker = CoActivationRecordMemory.load(
8485
StorageLayout.coactivationTracker(basePath), 10_000, 20_000);

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/CognitiveCortexBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ private static List<RegionSizeSpec> getRuntimeBundleSpecs(SpectorMemoryBuilder b
309309
long typeRegistrySize = builder.typeRegistrySize;
310310
long insulaSize = builder.insulaSize;
311311

312-
// BM25 region sizing: header(24) + docIds(~48B/doc) + docLengths(4B/doc) + terms+postings(~80B/doc)
313-
long bm25InitialSize = Math.max(64 * 1024, 24 + 132L * workingCap);
312+
// BM25 region sizing: header(24) + docIds(~48B/doc) + docLengths(4B/doc) + terms+postings(~1400B/doc)
313+
long bm25InitialSize = Math.max(4L * 1024 * 1024, 24 + 1500L * builder.episodicPartitionCapacity);
314314

315315
return List.of(
316316
new RegionSizeSpec(

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/DaemonSupervisorBuilder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,17 @@ static DaemonBundle build(
6161
Path indexSavePath = resolvedPartitionDir != null
6262
? resolvedPartitionDir.resolve(StorageLayout.FILE_INDEX)
6363
: StorageLayout.indexMidxRuntime(basePath);
64+
java.lang.foreign.MemorySegment ckptSlice = cortex.useBundleMode() && cortex.runtimeBundle() != null
65+
? cortex.runtimeBundle().regionSegment(com.spectrayan.spector.memory.kernel.bundle.RegionId.CHECKPOINT)
66+
: null;
6467
checkpointDaemon = new CheckpointDaemon(
6568
cortex.cognitiveRouter(), wal,
6669
StorageLayout.checkpointMeta(basePath),
6770
index, indexSavePath,
6871
graphs.hebbianGraph(), graphs.temporalChain(),
6972
graphs.entityDirectory(), graphs.hyperEntityGraph(), bio.coActivationTracker(),
7073
graphs.temporalKnowledgeGraph(),
71-
resolvedPartitionDir, basePath);
74+
resolvedPartitionDir, basePath, ckptSlice);
7275
daemonSupervisor = new DaemonSupervisor("memory");
7376
daemonSupervisor.schedule(
7477
"checkpoint",

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/DefaultSpectorMemory.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,11 +1423,26 @@ private void doClose() {
14231423
if (persistenceMode == MemoryPersistenceMode.DISK
14241424
&& partitionManager.activePartitionDir() != null
14251425
&& bm25Index != null && bm25Index.totalDocuments() > 0) {
1426-
try {
1427-
bm25Index.partition(0).save(
1428-
StorageLayout.bm25BidxRuntime(persistencePath));
1429-
} catch (Exception e) {
1430-
log.warn("Failed to save BM25 index on close: {}", e.getMessage());
1426+
boolean savedToBundle = false;
1427+
if (runtimeBundle != null) {
1428+
try {
1429+
java.lang.foreign.MemorySegment bm25Region = runtimeBundle.regionSegment(
1430+
com.spectrayan.spector.memory.kernel.bundle.RegionId.BM25);
1431+
if (bm25Region != null) {
1432+
int written = bm25Index.partition(0).saveToRegion(bm25Region);
1433+
savedToBundle = written > 0;
1434+
}
1435+
} catch (Exception e) {
1436+
log.debug("BM25 bundle region save on close failed: {}", e.getMessage());
1437+
}
1438+
}
1439+
if (!savedToBundle) {
1440+
try {
1441+
bm25Index.partition(0).save(
1442+
StorageLayout.bm25BidxRuntime(persistencePath));
1443+
} catch (Exception e) {
1444+
log.warn("Failed to save BM25 index on close: {}", e.getMessage());
1445+
}
14311446
}
14321447
// V4 bundle: save to BM25 region
14331448
if (runtimeBundle != null) {

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/RetrievalIndexBuilder.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,24 @@ static RetrievalIndices build(SpectorMemoryBuilder builder,
114114
if (!allTexts.isEmpty()) {
115115
bm25Index.rebuildPartition(0, allTexts);
116116
log.info("Rebuilt BM25 index with {} documents from memory index", allTexts.size());
117-
// Save to file (V3 compat) and bundle region (V4)
118-
java.nio.file.Path bm25Path = StorageLayout.bm25BidxRuntime(basePath);
119-
bm25Index.partition(0).save(bm25Path);
117+
// Save to bundle region (V4) or file (V3 fallback)
118+
boolean savedToBundle = false;
120119
if (cortex.useBundleMode() && cortex.runtimeBundle() != null) {
121120
try {
122121
java.lang.foreign.MemorySegment bm25Region = cortex.runtimeBundle().regionSegment(
123122
com.spectrayan.spector.memory.kernel.bundle.RegionId.BM25);
124123
if (bm25Region != null) {
125-
bm25Index.partition(0).saveToRegion(bm25Region);
124+
int written = bm25Index.partition(0).saveToRegion(bm25Region);
125+
savedToBundle = written > 0;
126126
}
127127
} catch (Exception e) {
128128
log.debug("BM25 bundle region save failed: {}", e.getMessage());
129129
}
130130
}
131+
if (!savedToBundle) {
132+
java.nio.file.Path bm25Path = StorageLayout.bm25BidxRuntime(basePath);
133+
bm25Index.partition(0).save(bm25Path);
134+
}
131135
}
132136
}
133137
} else {

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/SpectorMemoryBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public final class SpectorMemoryBuilder {
6464

6565
// Core configuration
6666
boolean managedByRegistry = false;
67-
boolean useBundleMode = false; // V4 bundle architecture (ADR-0004)
67+
boolean useBundleMode = true; // V4 bundle architecture (ADR-0004)
6868
int dimensions;
6969
EmbeddingProvider embeddingProvider;
7070
Path persistencePath;

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/graph/EntityDirectory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ private EntityDirectory(Arena arena, MemorySegment entityRegionSlice, MemorySegm
192192
MemoryHeader.write(adjacencyRegionSlice, 0L, LAYOUT.schemaVersion(), MemoryShape.GRAPH, 0,
193193
(int) adjacencyRegionSlice.byteSize(), 0, 0, LAYOUT.layoutId(), now, now);
194194

195-
int adjCap = (int) ((adjacencyRegionSlice.byteSize() - MemoryHeader.HEADER_BYTES - 16) / ADJ_ENTRY_BYTES);
195+
long reservedForNames = 32L * entityCapacity;
196+
long availableForAdj = Math.max(0, adjacencyRegionSlice.byteSize() - MemoryHeader.HEADER_BYTES - 16 - reservedForNames);
197+
int adjCap = (int) (availableForAdj / ADJ_ENTRY_BYTES);
196198
adjacencyRegionSlice.set(ValueLayout.JAVA_INT, headerStart + SUB_OFF_ADJ_CAPACITY, adjCap);
197199
adjacencyRegionSlice.set(ValueLayout.JAVA_INT, headerStart + SUB_OFF_ADJ_HWM, 0);
198200
this.adjSegmentCapacity = adjCap;

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/graph/TypeRegistryMemory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ public static TypeRegistryMemory fromBundle(SystemMemoryId systemMemoryId, Arena
213213
public void save(Path filePath) throws IOException {
214214
if (bundleManaged) {
215215
long now = System.currentTimeMillis();
216-
MemoryHeader.write(bundleSlice, 0L, backing.layout().schemaVersion(), MemoryShape.REGISTRY, backing.size(),
217-
(int) bundleSlice.byteSize(), 0, 0, backing.layout().layoutId(), now, now);
216+
MemoryHeader.write(bundleSlice, 0L, backing.layout().schemaVersion(), MemoryShape.REGISTRY, 0,
217+
backing.capacity(), backing.size(), backing.layout().recordStride(), backing.layout().layoutId(), now, now);
218218
backing.flush();
219219
log.info("{} registry saved to bundle: {} types", label, backing.size());
220220
return;

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/hebbian/CoActivationRecordMemory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ private CoActivationRecordMemory(Arena arena, MemorySegment regionSlice,
187187
pairCap, arena, regionSlice,
188188
isNew ? 0 : (int) MemoryHeader.readCount(regionSlice, 0),
189189
true, bundlePath, null, true); // bundleManaged=true
190+
this.checkpointRegion = checkpointRegion;
190191

191192
long totalBytes = 8 + 32L * pairCap + 40L * edgeCap;
192193
MemorySegment segment = segment();

memory/spector-memory/src/main/java/com/spectrayan/spector/memory/index/IndexRecordMemory.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -704,14 +704,14 @@ public void save(Path filePath) {
704704
long totalSlotBytes = (long) entryCount * stride;
705705

706706
long now = System.currentTimeMillis();
707-
MemoryHeader.write(bundleMidxSlice, 0L, INDEX_VERSION_V7, MemoryShape.RECORD, entryCount,
708-
(int) (MemoryHeader.HEADER_BYTES + totalSlotBytes), 0, 0, new IndexEntryLayout().layoutId(), now, now);
707+
MemoryHeader.write(bundleMidxSlice, 0L, INDEX_VERSION_V7, MemoryShape.RECORD, 0,
708+
100_000L, entryCount, stride, new IndexEntryLayout().layoutId(), now, now);
709709
// Persist graphSlotHighWater in the reserved field (offset 60, outside CRC range)
710710
long headerBaseOffset = 0L;
711711
bundleMidxSlice.set(java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED, headerBaseOffset + 60, graphSlotHighWater.get());
712712

713-
MemoryHeader.write(bundleIdplSlice, 0L, 1, MemoryShape.APPEND, entryCount,
714-
(int) (MemoryHeader.HEADER_BYTES + totalPoolBytes), 0, 0, new IdBlobLayout().layoutId(), now, now);
713+
MemoryHeader.write(bundleIdplSlice, 0L, 1, MemoryShape.APPEND, 0,
714+
totalPoolBytes, entryCount, 0, new IdBlobLayout().layoutId(), now, now);
715715

716716
long poolOffset = 0;
717717
int index = 0;
@@ -729,7 +729,7 @@ public void save(Path filePath) {
729729
ByteBuffer slotBuf = ByteBuffer.wrap(slotBytes);
730730
slotBuf.order(java.nio.ByteOrder.nativeOrder());
731731

732-
slotBuf.putLong(poolOffset);
732+
slotBuf.putLong(poolOffset + 4);
733733
slotBuf.putInt(blobBytes.length);
734734
slotBuf.putInt(loc.type().ordinal());
735735
slotBuf.putLong(loc.offset());

0 commit comments

Comments
 (0)