verso-db 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +28 -13
- package/dist/HNSWIndex.d.ts +40 -1
- package/dist/HNSWIndex.d.ts.map +1 -1
- package/dist/HNSWIndex.js +172 -20
- package/dist/HNSWIndex.js.map +1 -1
- package/dist/SearchWorker.d.ts +36 -8
- package/dist/SearchWorker.d.ts.map +1 -1
- package/dist/SearchWorker.js +207 -51
- package/dist/SearchWorker.js.map +1 -1
- package/dist/WorkerPool.d.ts +36 -8
- package/dist/WorkerPool.d.ts.map +1 -1
- package/dist/WorkerPool.js +191 -17
- package/dist/WorkerPool.js.map +1 -1
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [0.3.0](https://github.com/briansunter/verso/compare/v0.2.0...v0.3.0) (2026-03-16)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* improve WorkerPool throughput, memory, and reliability ([43642e0](https://github.com/briansunter/verso/commit/43642e0aadd26ec45c208a37fd4d748986c67e3e))
|
|
7
|
+
|
|
1
8
|
# [0.2.0](https://github.com/briansunter/verso/compare/v0.1.5...v0.2.0) (2026-03-15)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -36,23 +36,39 @@ npm install verso-db
|
|
|
36
36
|
## Quick Start
|
|
37
37
|
|
|
38
38
|
```typescript
|
|
39
|
-
import { VectorDB
|
|
39
|
+
import { VectorDB } from 'verso-db';
|
|
40
40
|
|
|
41
|
-
// Create database
|
|
42
41
|
const db = new VectorDB({ storagePath: './my_vectors' });
|
|
42
|
+
const collection = await db.createCollection('docs', { dimension: 3 });
|
|
43
|
+
|
|
44
|
+
await collection.add({
|
|
45
|
+
ids: ['a', 'b', 'c'],
|
|
46
|
+
vectors: [new Float32Array([1, 0, 0]), new Float32Array([0, 1, 0]), new Float32Array([0, 0, 1])]
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const results = await collection.query({ queryVector: new Float32Array([1, 0.1, 0]), k: 2 });
|
|
50
|
+
console.log(results.ids); // ['a', 'b']
|
|
51
|
+
|
|
52
|
+
await db.close();
|
|
53
|
+
```
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
Only `dimension` is required — defaults are `metric: 'cosine'`, `M: 16`, `efConstruction: 200`. Use [presets](#parameter-presets) to tune for your use case.
|
|
56
|
+
|
|
57
|
+
### With All Options
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { VectorDB, getRecommendedPreset } from 'verso-db';
|
|
61
|
+
|
|
62
|
+
const db = new VectorDB({ storagePath: './my_vectors' });
|
|
45
63
|
const preset = getRecommendedPreset(768);
|
|
46
64
|
|
|
47
|
-
// Create collection
|
|
48
65
|
const collection = await db.createCollection('documents', {
|
|
49
66
|
dimension: 768,
|
|
50
|
-
metric: 'cosine',
|
|
51
|
-
M: preset.M,
|
|
52
|
-
efConstruction: preset.efConstruction
|
|
67
|
+
metric: 'cosine', // 'cosine' | 'euclidean' | 'dot_product'
|
|
68
|
+
M: preset.M, // max connections per node
|
|
69
|
+
efConstruction: preset.efConstruction // build-time search depth
|
|
53
70
|
});
|
|
54
71
|
|
|
55
|
-
// Add vectors
|
|
56
72
|
await collection.add({
|
|
57
73
|
ids: ['doc1', 'doc2', 'doc3'],
|
|
58
74
|
vectors: [
|
|
@@ -67,18 +83,17 @@ await collection.add({
|
|
|
67
83
|
]
|
|
68
84
|
});
|
|
69
85
|
|
|
70
|
-
// Query
|
|
71
86
|
const results = await collection.query({
|
|
72
87
|
queryVector: new Float32Array(768).fill(0.15),
|
|
73
88
|
k: 10,
|
|
74
|
-
efSearch: preset.efSearch
|
|
89
|
+
efSearch: preset.efSearch, // query-time search depth
|
|
90
|
+
filter: { category: 'tech' } // MongoDB-style metadata filter
|
|
75
91
|
});
|
|
76
92
|
|
|
77
|
-
console.log(results.ids); // ['doc1', '
|
|
78
|
-
console.log(results.distances); // [0.01, 0.
|
|
93
|
+
console.log(results.ids); // ['doc1', 'doc3']
|
|
94
|
+
console.log(results.distances); // [0.01, 0.12]
|
|
79
95
|
console.log(results.metadata); // [{ title: 'Document 1', ... }, ...]
|
|
80
96
|
|
|
81
|
-
// Optional: release resources explicitly
|
|
82
97
|
await db.close();
|
|
83
98
|
```
|
|
84
99
|
|
package/dist/HNSWIndex.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export declare class HNSWIndex {
|
|
|
22
22
|
private static allocateInt8;
|
|
23
23
|
/** Whether flatVectors/flatInt8Vectors use SharedArrayBuffer */
|
|
24
24
|
private useSharedMemory;
|
|
25
|
+
private sharedGraphIndex;
|
|
26
|
+
private sharedGraphNeighborData;
|
|
27
|
+
private sharedGraphMaxLayerSlots;
|
|
28
|
+
private sharedGraphWriteOffset;
|
|
29
|
+
private sharedMetadata;
|
|
25
30
|
private M;
|
|
26
31
|
private M0;
|
|
27
32
|
private efConstruction;
|
|
@@ -344,7 +349,6 @@ export declare class HNSWIndex {
|
|
|
344
349
|
maxLevel: number;
|
|
345
350
|
M: number;
|
|
346
351
|
M0: number;
|
|
347
|
-
graphData: ArrayBuffer;
|
|
348
352
|
nodeLevels: Uint8Array;
|
|
349
353
|
quantizationEnabled: boolean;
|
|
350
354
|
quantizationParams: {
|
|
@@ -353,9 +357,44 @@ export declare class HNSWIndex {
|
|
|
353
357
|
scale: Float32Array;
|
|
354
358
|
offset: Float32Array;
|
|
355
359
|
} | null;
|
|
360
|
+
graphData?: ArrayBuffer;
|
|
361
|
+
graphNeighborData?: Uint32Array;
|
|
362
|
+
graphIndex?: Uint32Array;
|
|
363
|
+
maxLayerSlots?: number;
|
|
364
|
+
sharedMetadata?: Uint32Array;
|
|
356
365
|
} | null;
|
|
366
|
+
/**
|
|
367
|
+
* Serialize graph into SAB-backed flat typed arrays for zero-copy worker sharing.
|
|
368
|
+
* Layout:
|
|
369
|
+
* graphIndex[(nodeId * maxLayerSlots + layer) * 2] = offset into graphNeighborData
|
|
370
|
+
* graphIndex[(nodeId * maxLayerSlots + layer) * 2 + 1] = neighbor count
|
|
371
|
+
* graphNeighborData[offset..offset+count] = neighbor IDs
|
|
372
|
+
*
|
|
373
|
+
* Pre-allocates extra capacity for growth during parallel build.
|
|
374
|
+
*/
|
|
375
|
+
private serializeGraphToSharedBuffers;
|
|
376
|
+
/**
|
|
377
|
+
* Update shared graph SABs for specific nodes.
|
|
378
|
+
* Called during parallel build after each batch to sync graph changes.
|
|
379
|
+
* Workers see updates immediately via shared memory — no postMessage needed.
|
|
380
|
+
*/
|
|
381
|
+
updateSharedGraphNodes(nodeIds: Iterable<number>): void;
|
|
382
|
+
/**
|
|
383
|
+
* Update shared metadata SAB with current index state.
|
|
384
|
+
* Workers read these values during search.
|
|
385
|
+
*/
|
|
386
|
+
updateSharedMetadata(): void;
|
|
387
|
+
/**
|
|
388
|
+
* Check if shared graph SABs are active (for parallel build optimization).
|
|
389
|
+
*/
|
|
390
|
+
hasSharedGraph(): boolean;
|
|
391
|
+
/**
|
|
392
|
+
* Clear shared graph references (called when pool is destroyed/re-initialized).
|
|
393
|
+
*/
|
|
394
|
+
clearSharedGraph(): void;
|
|
357
395
|
/**
|
|
358
396
|
* Serialize graph structure (neighbor lists) into a compact ArrayBuffer.
|
|
397
|
+
* Legacy format for non-SAB fallback.
|
|
359
398
|
* Format per node: [numLayers:uint8] [numNeighbors:uint16, neighborId:uint32...] per layer
|
|
360
399
|
*/
|
|
361
400
|
private serializeGraphStructure;
|
package/dist/HNSWIndex.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HNSWIndex.d.ts","sourceRoot":"","sources":["../src/HNSWIndex.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAEpE,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;CACvB;AAED,qBAAa,SAAS;IAEpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAc;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAqC;IACxE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAc;IAC5D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAc;IAEpD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAO9B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAO3B,gEAAgE;IAChE,OAAO,CAAC,eAAe,CAAkB;
|
|
1
|
+
{"version":3,"file":"HNSWIndex.d.ts","sourceRoot":"","sources":["../src/HNSWIndex.ts"],"names":[],"mappings":"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"}
|
package/dist/HNSWIndex.js
CHANGED
|
@@ -32,6 +32,12 @@ export class HNSWIndex {
|
|
|
32
32
|
}
|
|
33
33
|
/** Whether flatVectors/flatInt8Vectors use SharedArrayBuffer */
|
|
34
34
|
useSharedMemory = false;
|
|
35
|
+
// Shared graph SAB state for zero-copy worker communication
|
|
36
|
+
sharedGraphIndex = null;
|
|
37
|
+
sharedGraphNeighborData = null;
|
|
38
|
+
sharedGraphMaxLayerSlots = 0;
|
|
39
|
+
sharedGraphWriteOffset = 0;
|
|
40
|
+
sharedMetadata = null;
|
|
35
41
|
M; // Max number of connections per node per level
|
|
36
42
|
M0; // Max number of connections for level 0 (typically M * 2)
|
|
37
43
|
efConstruction; // Size of candidate list during construction
|
|
@@ -125,6 +131,9 @@ export class HNSWIndex {
|
|
|
125
131
|
this.visitedArraySize = 10000;
|
|
126
132
|
this.visitedArray = new Uint16Array(this.visitedArraySize);
|
|
127
133
|
this.visitedGeneration = 1;
|
|
134
|
+
// Pre-allocate batch distance buffers sized for max neighbors at layer 0
|
|
135
|
+
this.batchNeighborIds = new Uint32Array(this.M0 + 1);
|
|
136
|
+
this.batchDistances = new Float64Array(this.M0 + 1);
|
|
128
137
|
// Pre-allocate searchLayer heaps - sized for typical ef values
|
|
129
138
|
// Will be resized if needed for larger ef
|
|
130
139
|
this.heapCapacity = Math.max(efConstruction * 2, 500);
|
|
@@ -223,9 +232,10 @@ export class HNSWIndex {
|
|
|
223
232
|
this.nextAutoId = id + 1;
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
|
-
// OPTIMIZATION:
|
|
227
|
-
|
|
228
|
-
|
|
235
|
+
// OPTIMIZATION: Pre-allocated typed arrays for batch distance calculation
|
|
236
|
+
// Sized to M0+1 (max neighbors at layer 0) to eliminate dynamic resizing
|
|
237
|
+
batchNeighborIds;
|
|
238
|
+
batchDistances;
|
|
229
239
|
/**
|
|
230
240
|
* OPTIMIZATION: Batch distance calculation for better cache locality
|
|
231
241
|
* Computes distances from query to multiple neighbors at once
|
|
@@ -667,10 +677,6 @@ export class HNSWIndex {
|
|
|
667
677
|
}
|
|
668
678
|
// Calculate all distances at once (better cache utilization)
|
|
669
679
|
if (batchCount > 0) {
|
|
670
|
-
// Ensure batch arrays are large enough
|
|
671
|
-
if (batchDists.length < batchCount) {
|
|
672
|
-
this.batchDistances.length = batchCount;
|
|
673
|
-
}
|
|
674
680
|
this.calculateDistancesBatch(query, batchIds, batchDists, batchCount);
|
|
675
681
|
// Process batch results
|
|
676
682
|
for (let i = 0; i < batchCount; i++) {
|
|
@@ -1400,17 +1406,27 @@ export class HNSWIndex {
|
|
|
1400
1406
|
}
|
|
1401
1407
|
batchCount++;
|
|
1402
1408
|
// Send incremental graph update: ALL modified nodes (new + existing with new connections)
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1409
|
+
if (this.hasSharedGraph()) {
|
|
1410
|
+
// Shared graph: write directly to SABs, workers see updates via shared memory
|
|
1411
|
+
this.updateSharedGraphNodes(modifiedNodeIds);
|
|
1412
|
+
this.updateSharedMetadata();
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
// Legacy: send graph data via postMessage
|
|
1416
|
+
const graphUpdate = [];
|
|
1417
|
+
for (const nid of modifiedNodeIds) {
|
|
1418
|
+
const node = this.nodes[nid];
|
|
1419
|
+
if (node) {
|
|
1420
|
+
graphUpdate.push({ id: nid, neighbors: node.neighbors });
|
|
1421
|
+
}
|
|
1408
1422
|
}
|
|
1423
|
+
pool.broadcastGraphUpdate(graphUpdate, this.entryPointId, this.maxLevel);
|
|
1409
1424
|
}
|
|
1410
|
-
pool.broadcastGraphUpdate(graphUpdate, this.entryPointId, this.maxLevel);
|
|
1411
1425
|
// Full re-sync periodically for accumulated neighbor changes
|
|
1412
|
-
//
|
|
1426
|
+
// With shared graph, this defragments dead space in neighbor data.
|
|
1427
|
+
// Without shared graph, this fixes accumulated drift from incremental updates.
|
|
1413
1428
|
if (batchCount % resyncInterval === 0 && start + batchSize < normalizedPoints.length) {
|
|
1429
|
+
this.clearSharedGraph();
|
|
1414
1430
|
pool.destroy();
|
|
1415
1431
|
await pool.init(this);
|
|
1416
1432
|
}
|
|
@@ -1572,7 +1588,6 @@ export class HNSWIndex {
|
|
|
1572
1588
|
getSharedSearchData() {
|
|
1573
1589
|
if (this.nodeCount === 0 || this.entryPointId === -1)
|
|
1574
1590
|
return null;
|
|
1575
|
-
const graphData = this.serializeGraphStructure();
|
|
1576
1591
|
const nodeLevels = new Uint8Array(this.nodeCount);
|
|
1577
1592
|
for (let i = 0; i < this.nodeCount; i++) {
|
|
1578
1593
|
const node = this.nodes[i];
|
|
@@ -1606,7 +1621,7 @@ export class HNSWIndex {
|
|
|
1606
1621
|
quantizationParams = this.scalarQuantizer.getParams();
|
|
1607
1622
|
}
|
|
1608
1623
|
}
|
|
1609
|
-
|
|
1624
|
+
const baseData = {
|
|
1610
1625
|
flatVectors,
|
|
1611
1626
|
flatInt8Vectors,
|
|
1612
1627
|
dimension: this.dimension,
|
|
@@ -1616,14 +1631,154 @@ export class HNSWIndex {
|
|
|
1616
1631
|
maxLevel: this.maxLevel,
|
|
1617
1632
|
M: this.M,
|
|
1618
1633
|
M0: this.M0,
|
|
1619
|
-
graphData,
|
|
1620
1634
|
nodeLevels,
|
|
1621
1635
|
quantizationEnabled: this.quantizationEnabled,
|
|
1622
1636
|
quantizationParams,
|
|
1623
1637
|
};
|
|
1638
|
+
// Use shared graph SABs when SharedArrayBuffer is available
|
|
1639
|
+
if (this.useSharedMemory && typeof SharedArrayBuffer !== 'undefined') {
|
|
1640
|
+
const sharedGraph = this.serializeGraphToSharedBuffers();
|
|
1641
|
+
return {
|
|
1642
|
+
...baseData,
|
|
1643
|
+
graphNeighborData: sharedGraph.graphNeighborData,
|
|
1644
|
+
graphIndex: sharedGraph.graphIndex,
|
|
1645
|
+
maxLayerSlots: sharedGraph.maxLayerSlots,
|
|
1646
|
+
sharedMetadata: sharedGraph.sharedMetadata,
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
// Fallback: legacy serialized graph
|
|
1650
|
+
return {
|
|
1651
|
+
...baseData,
|
|
1652
|
+
graphData: this.serializeGraphStructure(),
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Serialize graph into SAB-backed flat typed arrays for zero-copy worker sharing.
|
|
1657
|
+
* Layout:
|
|
1658
|
+
* graphIndex[(nodeId * maxLayerSlots + layer) * 2] = offset into graphNeighborData
|
|
1659
|
+
* graphIndex[(nodeId * maxLayerSlots + layer) * 2 + 1] = neighbor count
|
|
1660
|
+
* graphNeighborData[offset..offset+count] = neighbor IDs
|
|
1661
|
+
*
|
|
1662
|
+
* Pre-allocates extra capacity for growth during parallel build.
|
|
1663
|
+
*/
|
|
1664
|
+
serializeGraphToSharedBuffers() {
|
|
1665
|
+
const maxLayerSlots = Math.max(this.maxLevel + 4, 8);
|
|
1666
|
+
// Count total neighbors for sizing
|
|
1667
|
+
let totalNeighbors = 0;
|
|
1668
|
+
for (let i = 0; i < this.nodeCount; i++) {
|
|
1669
|
+
const node = this.nodes[i];
|
|
1670
|
+
if (!node)
|
|
1671
|
+
continue;
|
|
1672
|
+
for (const neighbors of node.neighbors) {
|
|
1673
|
+
totalNeighbors += neighbors?.length ?? 0;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
// Pre-allocate with capacity for ALL nodes (not just current ones).
|
|
1677
|
+
// During parallel build, serializeGraphToSharedBuffers is called after the seed phase
|
|
1678
|
+
// (e.g. 500 nodes), but the full index may grow to flatVectorsCapacity nodes.
|
|
1679
|
+
// Each node has up to M0 layer-0 neighbors + M upper-layer neighbors.
|
|
1680
|
+
// The append-only write pattern creates dead space when nodes are rewritten,
|
|
1681
|
+
// so we use 3x the estimated maximum to accommodate waste.
|
|
1682
|
+
const nodeCapacity = Math.max(this.nodeCount, this.flatVectorsCapacity);
|
|
1683
|
+
const estimatedMaxNeighbors = nodeCapacity * (this.M0 + this.M);
|
|
1684
|
+
const neighborCapacity = Math.max(Math.ceil(totalNeighbors * 3), totalNeighbors + 10000, estimatedMaxNeighbors * 3);
|
|
1685
|
+
const indexSize = nodeCapacity * maxLayerSlots * 2;
|
|
1686
|
+
const graphIndex = new Uint32Array(new SharedArrayBuffer(indexSize * 4));
|
|
1687
|
+
const graphNeighborData = new Uint32Array(new SharedArrayBuffer(neighborCapacity * 4));
|
|
1688
|
+
// Shared metadata: [nodeCount, entryPointId, maxLevel]
|
|
1689
|
+
const sharedMetadata = new Uint32Array(new SharedArrayBuffer(3 * 4));
|
|
1690
|
+
sharedMetadata[0] = this.nodeCount;
|
|
1691
|
+
sharedMetadata[1] = this.entryPointId;
|
|
1692
|
+
sharedMetadata[2] = this.maxLevel;
|
|
1693
|
+
// Serialize current graph
|
|
1694
|
+
let writeOffset = 0;
|
|
1695
|
+
for (let nodeId = 0; nodeId < this.nodeCount; nodeId++) {
|
|
1696
|
+
const node = this.nodes[nodeId];
|
|
1697
|
+
if (!node)
|
|
1698
|
+
continue;
|
|
1699
|
+
for (let l = 0; l < node.neighbors.length && l < maxLayerSlots; l++) {
|
|
1700
|
+
const neighbors = node.neighbors[l] ?? [];
|
|
1701
|
+
const base = (nodeId * maxLayerSlots + l) * 2;
|
|
1702
|
+
graphIndex[base] = writeOffset;
|
|
1703
|
+
graphIndex[base + 1] = neighbors.length;
|
|
1704
|
+
for (let n = 0; n < neighbors.length; n++) {
|
|
1705
|
+
graphNeighborData[writeOffset + n] = neighbors[n];
|
|
1706
|
+
}
|
|
1707
|
+
writeOffset += neighbors.length;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
// Store references for incremental updates during parallel build
|
|
1711
|
+
this.sharedGraphIndex = graphIndex;
|
|
1712
|
+
this.sharedGraphNeighborData = graphNeighborData;
|
|
1713
|
+
this.sharedGraphMaxLayerSlots = maxLayerSlots;
|
|
1714
|
+
this.sharedGraphWriteOffset = writeOffset;
|
|
1715
|
+
this.sharedMetadata = sharedMetadata;
|
|
1716
|
+
return { graphNeighborData, graphIndex, maxLayerSlots, sharedMetadata };
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Update shared graph SABs for specific nodes.
|
|
1720
|
+
* Called during parallel build after each batch to sync graph changes.
|
|
1721
|
+
* Workers see updates immediately via shared memory — no postMessage needed.
|
|
1722
|
+
*/
|
|
1723
|
+
updateSharedGraphNodes(nodeIds) {
|
|
1724
|
+
if (!this.sharedGraphIndex || !this.sharedGraphNeighborData)
|
|
1725
|
+
return;
|
|
1726
|
+
const maxLayerSlots = this.sharedGraphMaxLayerSlots;
|
|
1727
|
+
let writeOffset = this.sharedGraphWriteOffset;
|
|
1728
|
+
for (const nodeId of nodeIds) {
|
|
1729
|
+
const node = this.nodes[nodeId];
|
|
1730
|
+
if (!node)
|
|
1731
|
+
continue;
|
|
1732
|
+
for (let l = 0; l < node.neighbors.length && l < maxLayerSlots; l++) {
|
|
1733
|
+
const neighbors = node.neighbors[l] ?? [];
|
|
1734
|
+
const base = (nodeId * maxLayerSlots + l) * 2;
|
|
1735
|
+
// If capacity exhausted, clear shared graph to force fallback to
|
|
1736
|
+
// broadcastGraphUpdate. Workers keep their current SAB data (stale but valid).
|
|
1737
|
+
// The periodic resync in addPointsBulkParallel will create fresh SABs.
|
|
1738
|
+
if (writeOffset + neighbors.length > this.sharedGraphNeighborData.length) {
|
|
1739
|
+
this.sharedGraphWriteOffset = writeOffset;
|
|
1740
|
+
this.clearSharedGraph();
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
this.sharedGraphIndex[base] = writeOffset;
|
|
1744
|
+
this.sharedGraphIndex[base + 1] = neighbors.length;
|
|
1745
|
+
for (let n = 0; n < neighbors.length; n++) {
|
|
1746
|
+
this.sharedGraphNeighborData[writeOffset + n] = neighbors[n];
|
|
1747
|
+
}
|
|
1748
|
+
writeOffset += neighbors.length;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
this.sharedGraphWriteOffset = writeOffset;
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Update shared metadata SAB with current index state.
|
|
1755
|
+
* Workers read these values during search.
|
|
1756
|
+
*/
|
|
1757
|
+
updateSharedMetadata() {
|
|
1758
|
+
if (!this.sharedMetadata)
|
|
1759
|
+
return;
|
|
1760
|
+
this.sharedMetadata[0] = this.nodeCount;
|
|
1761
|
+
this.sharedMetadata[1] = this.entryPointId;
|
|
1762
|
+
this.sharedMetadata[2] = this.maxLevel;
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Check if shared graph SABs are active (for parallel build optimization).
|
|
1766
|
+
*/
|
|
1767
|
+
hasSharedGraph() {
|
|
1768
|
+
return this.sharedGraphIndex !== null;
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Clear shared graph references (called when pool is destroyed/re-initialized).
|
|
1772
|
+
*/
|
|
1773
|
+
clearSharedGraph() {
|
|
1774
|
+
this.sharedGraphIndex = null;
|
|
1775
|
+
this.sharedGraphNeighborData = null;
|
|
1776
|
+
this.sharedMetadata = null;
|
|
1777
|
+
this.sharedGraphWriteOffset = 0;
|
|
1624
1778
|
}
|
|
1625
1779
|
/**
|
|
1626
1780
|
* Serialize graph structure (neighbor lists) into a compact ArrayBuffer.
|
|
1781
|
+
* Legacy format for non-SAB fallback.
|
|
1627
1782
|
* Format per node: [numLayers:uint8] [numNeighbors:uint16, neighborId:uint32...] per layer
|
|
1628
1783
|
*/
|
|
1629
1784
|
serializeGraphStructure() {
|
|
@@ -2718,9 +2873,6 @@ export class HNSWIndex {
|
|
|
2718
2873
|
}
|
|
2719
2874
|
}
|
|
2720
2875
|
if (batchCount > 0) {
|
|
2721
|
-
if (batchDists.length < batchCount) {
|
|
2722
|
-
this.batchDistances.length = batchCount;
|
|
2723
|
-
}
|
|
2724
2876
|
this.calculateDistancesBatchInt8(int8Query, batchIds, batchDists, batchCount);
|
|
2725
2877
|
for (let i = 0; i < batchCount; i++) {
|
|
2726
2878
|
const neighborId = batchIds[i];
|