529 lines
16 KiB
TypeScript
529 lines
16 KiB
TypeScript
|
|
/**
|
||
|
|
* Scaled HNSW System - Integration of All Optimization Strategies
|
||
|
|
* Production-ready system for handling millions of vectors with sub-second search
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { Vector, VectorDocument, HNSWConfig } from '../coreTypes.js'
|
||
|
|
import { PartitionedHNSWIndex, PartitionConfig } from './partitionedHNSWIndex.js'
|
||
|
|
import { OptimizedHNSWIndex, OptimizedHNSWConfig } from './optimizedHNSWIndex.js'
|
||
|
|
import { DistributedSearchSystem, SearchStrategy } from './distributedSearch.js'
|
||
|
|
import { EnhancedCacheManager } from '../storage/enhancedCacheManager.js'
|
||
|
|
import { BatchS3Operations } from '../storage/adapters/batchS3Operations.js'
|
||
|
|
import { ReadOnlyOptimizations } from '../storage/readOnlyOptimizations.js'
|
||
|
|
import { euclideanDistance } from '../utils/index.js'
|
||
|
|
|
||
|
|
export interface ScaledHNSWConfig {
|
||
|
|
// Dataset scale expectations
|
||
|
|
expectedDatasetSize: number
|
||
|
|
maxMemoryUsage: number // bytes
|
||
|
|
targetSearchLatency: number // ms
|
||
|
|
|
||
|
|
// Storage configuration
|
||
|
|
s3Config?: {
|
||
|
|
bucketName: string
|
||
|
|
region: string
|
||
|
|
endpoint?: string
|
||
|
|
accessKeyId: string
|
||
|
|
secretAccessKey: string
|
||
|
|
}
|
||
|
|
|
||
|
|
// Optimization strategies
|
||
|
|
enablePartitioning?: boolean
|
||
|
|
enableCompression?: boolean
|
||
|
|
enableDistributedSearch?: boolean
|
||
|
|
enablePredictiveCaching?: boolean
|
||
|
|
|
||
|
|
// Performance tuning
|
||
|
|
partitionConfig?: Partial<PartitionConfig>
|
||
|
|
hnswConfig?: Partial<OptimizedHNSWConfig>
|
||
|
|
readOnlyMode?: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* High-performance HNSW system with all optimizations integrated
|
||
|
|
* Handles datasets from thousands to millions of vectors
|
||
|
|
*/
|
||
|
|
export class ScaledHNSWSystem {
|
||
|
|
private config: ScaledHNSWConfig
|
||
|
|
private partitionedIndex?: PartitionedHNSWIndex
|
||
|
|
private distributedSearch?: DistributedSearchSystem
|
||
|
|
private cacheManager?: EnhancedCacheManager<any>
|
||
|
|
private batchOperations?: BatchS3Operations
|
||
|
|
private readOnlyOptimizations?: ReadOnlyOptimizations
|
||
|
|
|
||
|
|
// Performance monitoring
|
||
|
|
private performanceMetrics = {
|
||
|
|
totalSearches: 0,
|
||
|
|
averageSearchTime: 0,
|
||
|
|
cacheHitRate: 0,
|
||
|
|
compressionRatio: 0,
|
||
|
|
memoryUsage: 0,
|
||
|
|
indexSize: 0
|
||
|
|
}
|
||
|
|
|
||
|
|
constructor(config: ScaledHNSWConfig) {
|
||
|
|
this.config = {
|
||
|
|
expectedDatasetSize: 100000,
|
||
|
|
maxMemoryUsage: 4 * 1024 * 1024 * 1024, // 4GB default
|
||
|
|
targetSearchLatency: 100, // 100ms default
|
||
|
|
enablePartitioning: true,
|
||
|
|
enableCompression: true,
|
||
|
|
enableDistributedSearch: true,
|
||
|
|
enablePredictiveCaching: true,
|
||
|
|
readOnlyMode: false,
|
||
|
|
...config
|
||
|
|
}
|
||
|
|
|
||
|
|
this.initializeOptimizedSystem()
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Initialize the optimized system based on configuration
|
||
|
|
*/
|
||
|
|
private async initializeOptimizedSystem(): Promise<void> {
|
||
|
|
console.log('Initializing Scaled HNSW System...')
|
||
|
|
|
||
|
|
// Determine optimal configuration based on dataset size
|
||
|
|
const optimizedConfig = this.calculateOptimalConfiguration()
|
||
|
|
|
||
|
|
// Initialize partitioned index
|
||
|
|
if (this.config.enablePartitioning) {
|
||
|
|
this.partitionedIndex = new PartitionedHNSWIndex(
|
||
|
|
optimizedConfig.partitionConfig,
|
||
|
|
optimizedConfig.hnswConfig,
|
||
|
|
euclideanDistance
|
||
|
|
)
|
||
|
|
console.log('✓ Partitioned index initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize distributed search system
|
||
|
|
if (this.config.enableDistributedSearch && this.partitionedIndex) {
|
||
|
|
this.distributedSearch = new DistributedSearchSystem({
|
||
|
|
maxConcurrentSearches: optimizedConfig.maxConcurrentSearches,
|
||
|
|
searchTimeout: this.config.targetSearchLatency * 5,
|
||
|
|
adaptivePartitionSelection: true,
|
||
|
|
loadBalancing: true
|
||
|
|
})
|
||
|
|
console.log('✓ Distributed search system initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize batch S3 operations
|
||
|
|
if (this.config.s3Config) {
|
||
|
|
this.batchOperations = new BatchS3Operations(
|
||
|
|
null as any, // Would be initialized with actual S3 client
|
||
|
|
this.config.s3Config.bucketName,
|
||
|
|
{
|
||
|
|
maxConcurrency: 50,
|
||
|
|
useS3Select: this.config.expectedDatasetSize > 100000
|
||
|
|
}
|
||
|
|
)
|
||
|
|
console.log('✓ Batch S3 operations initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize enhanced caching
|
||
|
|
if (this.config.enablePredictiveCaching) {
|
||
|
|
this.cacheManager = new EnhancedCacheManager({
|
||
|
|
hotCacheMaxSize: optimizedConfig.hotCacheSize,
|
||
|
|
warmCacheMaxSize: optimizedConfig.warmCacheSize,
|
||
|
|
prefetchEnabled: true,
|
||
|
|
prefetchStrategy: 'hybrid',
|
||
|
|
prefetchBatchSize: 50
|
||
|
|
})
|
||
|
|
|
||
|
|
if (this.batchOperations) {
|
||
|
|
this.cacheManager.setStorageAdapters(null as any, this.batchOperations)
|
||
|
|
}
|
||
|
|
console.log('✓ Enhanced cache manager initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize read-only optimizations
|
||
|
|
if (this.config.readOnlyMode && this.config.enableCompression) {
|
||
|
|
this.readOnlyOptimizations = new ReadOnlyOptimizations({
|
||
|
|
compression: {
|
||
|
|
vectorCompression: 'quantization',
|
||
|
|
metadataCompression: 'gzip',
|
||
|
|
quantizationType: 'scalar',
|
||
|
|
quantizationBits: 8
|
||
|
|
},
|
||
|
|
segmentSize: optimizedConfig.segmentSize,
|
||
|
|
memoryMapped: true,
|
||
|
|
cacheIndexInMemory: optimizedConfig.cacheIndexInMemory
|
||
|
|
})
|
||
|
|
console.log('✓ Read-only optimizations initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('Scaled HNSW System ready for', this.config.expectedDatasetSize, 'vectors')
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculate optimal configuration based on dataset size and constraints
|
||
|
|
*/
|
||
|
|
private calculateOptimalConfiguration(): {
|
||
|
|
partitionConfig: PartitionConfig
|
||
|
|
hnswConfig: OptimizedHNSWConfig
|
||
|
|
hotCacheSize: number
|
||
|
|
warmCacheSize: number
|
||
|
|
maxConcurrentSearches: number
|
||
|
|
segmentSize: number
|
||
|
|
cacheIndexInMemory: boolean
|
||
|
|
} {
|
||
|
|
const size = this.config.expectedDatasetSize
|
||
|
|
const memoryBudget = this.config.maxMemoryUsage
|
||
|
|
|
||
|
|
let config: any = {}
|
||
|
|
|
||
|
|
if (size <= 10000) {
|
||
|
|
// Small dataset - optimize for speed
|
||
|
|
config = {
|
||
|
|
partitionConfig: {
|
||
|
|
maxNodesPerPartition: 10000,
|
||
|
|
partitionStrategy: 'hash' as const
|
||
|
|
},
|
||
|
|
hnswConfig: {
|
||
|
|
M: 16,
|
||
|
|
efConstruction: 200,
|
||
|
|
efSearch: 50,
|
||
|
|
targetSearchLatency: this.config.targetSearchLatency
|
||
|
|
},
|
||
|
|
hotCacheSize: 1000,
|
||
|
|
warmCacheSize: 5000,
|
||
|
|
maxConcurrentSearches: 4,
|
||
|
|
segmentSize: 5000,
|
||
|
|
cacheIndexInMemory: true
|
||
|
|
}
|
||
|
|
} else if (size <= 100000) {
|
||
|
|
// Medium dataset - balance performance and memory
|
||
|
|
config = {
|
||
|
|
partitionConfig: {
|
||
|
|
maxNodesPerPartition: 25000,
|
||
|
|
partitionStrategy: 'semantic' as const,
|
||
|
|
semanticClusters: 8
|
||
|
|
},
|
||
|
|
hnswConfig: {
|
||
|
|
M: 24,
|
||
|
|
efConstruction: 300,
|
||
|
|
efSearch: 75,
|
||
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
||
|
|
dynamicParameterTuning: true
|
||
|
|
},
|
||
|
|
hotCacheSize: 2000,
|
||
|
|
warmCacheSize: 15000,
|
||
|
|
maxConcurrentSearches: 8,
|
||
|
|
segmentSize: 10000,
|
||
|
|
cacheIndexInMemory: memoryBudget > 2 * 1024 * 1024 * 1024 // 2GB
|
||
|
|
}
|
||
|
|
} else if (size <= 1000000) {
|
||
|
|
// Large dataset - optimize for scale
|
||
|
|
config = {
|
||
|
|
partitionConfig: {
|
||
|
|
maxNodesPerPartition: 50000,
|
||
|
|
partitionStrategy: 'semantic' as const,
|
||
|
|
semanticClusters: 16
|
||
|
|
},
|
||
|
|
hnswConfig: {
|
||
|
|
M: 32,
|
||
|
|
efConstruction: 400,
|
||
|
|
efSearch: 100,
|
||
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
||
|
|
dynamicParameterTuning: true,
|
||
|
|
memoryBudget: memoryBudget
|
||
|
|
},
|
||
|
|
hotCacheSize: 5000,
|
||
|
|
warmCacheSize: 25000,
|
||
|
|
maxConcurrentSearches: 12,
|
||
|
|
segmentSize: 20000,
|
||
|
|
cacheIndexInMemory: memoryBudget > 8 * 1024 * 1024 * 1024 // 8GB
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Very large dataset - maximum optimization
|
||
|
|
config = {
|
||
|
|
partitionConfig: {
|
||
|
|
maxNodesPerPartition: 100000,
|
||
|
|
partitionStrategy: 'hybrid' as const,
|
||
|
|
semanticClusters: 32
|
||
|
|
},
|
||
|
|
hnswConfig: {
|
||
|
|
M: 48,
|
||
|
|
efConstruction: 500,
|
||
|
|
efSearch: 150,
|
||
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
||
|
|
dynamicParameterTuning: true,
|
||
|
|
memoryBudget: memoryBudget,
|
||
|
|
diskCacheEnabled: true
|
||
|
|
},
|
||
|
|
hotCacheSize: 10000,
|
||
|
|
warmCacheSize: 50000,
|
||
|
|
maxConcurrentSearches: 20,
|
||
|
|
segmentSize: 50000,
|
||
|
|
cacheIndexInMemory: false // Too large for memory
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return config
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add vector to the scaled system
|
||
|
|
*/
|
||
|
|
public async addVector(item: VectorDocument): Promise<string> {
|
||
|
|
if (!this.partitionedIndex) {
|
||
|
|
throw new Error('System not properly initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
const startTime = Date.now()
|
||
|
|
const result = await this.partitionedIndex.addItem(item)
|
||
|
|
|
||
|
|
// Update performance metrics
|
||
|
|
this.performanceMetrics.indexSize = this.partitionedIndex.size()
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Bulk insert vectors with optimizations
|
||
|
|
*/
|
||
|
|
public async bulkInsert(items: VectorDocument[]): Promise<string[]> {
|
||
|
|
if (!this.partitionedIndex) {
|
||
|
|
throw new Error('System not properly initialized')
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`Starting optimized bulk insert of ${items.length} vectors`)
|
||
|
|
const startTime = Date.now()
|
||
|
|
|
||
|
|
// Sort items for optimal insertion order
|
||
|
|
const sortedItems = this.optimizeInsertionOrder(items)
|
||
|
|
|
||
|
|
const results: string[] = []
|
||
|
|
const batchSize = this.calculateOptimalBatchSize(items.length)
|
||
|
|
|
||
|
|
// Process in batches
|
||
|
|
for (let i = 0; i < sortedItems.length; i += batchSize) {
|
||
|
|
const batch = sortedItems.slice(i, i + batchSize)
|
||
|
|
|
||
|
|
for (const item of batch) {
|
||
|
|
const id = await this.partitionedIndex.addItem(item)
|
||
|
|
results.push(id)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Progress logging
|
||
|
|
if (i % (batchSize * 10) === 0) {
|
||
|
|
const progress = ((i / sortedItems.length) * 100).toFixed(1)
|
||
|
|
console.log(`Bulk insert progress: ${progress}%`)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const totalTime = Date.now() - startTime
|
||
|
|
console.log(`Bulk insert completed: ${results.length} vectors in ${totalTime}ms`)
|
||
|
|
|
||
|
|
return results
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* High-performance vector search with all optimizations
|
||
|
|
*/
|
||
|
|
public async search(
|
||
|
|
queryVector: Vector,
|
||
|
|
k: number = 10,
|
||
|
|
options: {
|
||
|
|
strategy?: SearchStrategy
|
||
|
|
useCache?: boolean
|
||
|
|
maxPartitions?: number
|
||
|
|
} = {}
|
||
|
|
): Promise<Array<[string, number]>> {
|
||
|
|
const startTime = Date.now()
|
||
|
|
|
||
|
|
try {
|
||
|
|
let results: Array<[string, number]>
|
||
|
|
|
||
|
|
if (this.distributedSearch && this.partitionedIndex) {
|
||
|
|
// Use distributed search for optimal performance
|
||
|
|
results = await this.distributedSearch.distributedSearch(
|
||
|
|
this.partitionedIndex,
|
||
|
|
queryVector,
|
||
|
|
k,
|
||
|
|
options.strategy || SearchStrategy.ADAPTIVE
|
||
|
|
)
|
||
|
|
} else if (this.partitionedIndex) {
|
||
|
|
// Fall back to partitioned search
|
||
|
|
results = await this.partitionedIndex.search(
|
||
|
|
queryVector,
|
||
|
|
k,
|
||
|
|
{ maxPartitions: options.maxPartitions }
|
||
|
|
)
|
||
|
|
} else {
|
||
|
|
throw new Error('No search system available')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update performance metrics
|
||
|
|
const searchTime = Date.now() - startTime
|
||
|
|
this.updateSearchMetrics(searchTime, results.length)
|
||
|
|
|
||
|
|
return results
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Search failed:', error)
|
||
|
|
throw error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get system performance metrics
|
||
|
|
*/
|
||
|
|
public getPerformanceMetrics(): typeof this.performanceMetrics & {
|
||
|
|
partitionStats?: any
|
||
|
|
cacheStats?: any
|
||
|
|
compressionStats?: any
|
||
|
|
distributedSearchStats?: any
|
||
|
|
} {
|
||
|
|
const metrics = { ...this.performanceMetrics }
|
||
|
|
|
||
|
|
// Add subsystem metrics
|
||
|
|
if (this.partitionedIndex) {
|
||
|
|
(metrics as any).partitionStats = this.partitionedIndex.getPartitionStats()
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.cacheManager) {
|
||
|
|
(metrics as any).cacheStats = this.cacheManager.getStats()
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.readOnlyOptimizations) {
|
||
|
|
(metrics as any).compressionStats = this.readOnlyOptimizations.getCompressionStats()
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.distributedSearch) {
|
||
|
|
(metrics as any).distributedSearchStats = this.distributedSearch.getSearchStats()
|
||
|
|
}
|
||
|
|
|
||
|
|
return metrics
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Optimize insertion order for better index quality
|
||
|
|
*/
|
||
|
|
private optimizeInsertionOrder(items: VectorDocument[]): VectorDocument[] {
|
||
|
|
if (items.length < 1000) {
|
||
|
|
return items // Not worth optimizing small batches
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simple clustering-based approach for better HNSW construction
|
||
|
|
// In production, you might use more sophisticated clustering
|
||
|
|
return items.sort(() => Math.random() - 0.5)
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculate optimal batch size based on system resources
|
||
|
|
*/
|
||
|
|
private calculateOptimalBatchSize(totalItems: number): number {
|
||
|
|
const memoryBudget = this.config.maxMemoryUsage
|
||
|
|
const estimatedItemSize = 1000 // Rough estimate per item in bytes
|
||
|
|
|
||
|
|
const maxBatch = Math.floor(memoryBudget * 0.1 / estimatedItemSize)
|
||
|
|
const targetBatch = Math.min(1000, Math.max(100, maxBatch))
|
||
|
|
|
||
|
|
return Math.min(targetBatch, totalItems)
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update search performance metrics
|
||
|
|
*/
|
||
|
|
private updateSearchMetrics(searchTime: number, resultCount: number): void {
|
||
|
|
this.performanceMetrics.totalSearches++
|
||
|
|
this.performanceMetrics.averageSearchTime =
|
||
|
|
(this.performanceMetrics.averageSearchTime + searchTime) / 2
|
||
|
|
|
||
|
|
// Update other metrics
|
||
|
|
if (this.cacheManager) {
|
||
|
|
const cacheStats = this.cacheManager.getStats()
|
||
|
|
const totalOps = cacheStats.hotCacheHits + cacheStats.hotCacheMisses +
|
||
|
|
cacheStats.warmCacheHits + cacheStats.warmCacheMisses
|
||
|
|
|
||
|
|
this.performanceMetrics.cacheHitRate = totalOps > 0 ?
|
||
|
|
(cacheStats.hotCacheHits + cacheStats.warmCacheHits) / totalOps : 0
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.readOnlyOptimizations) {
|
||
|
|
const compressionStats = this.readOnlyOptimizations.getCompressionStats()
|
||
|
|
this.performanceMetrics.compressionRatio = compressionStats.compressionRatio
|
||
|
|
}
|
||
|
|
|
||
|
|
// Estimate memory usage
|
||
|
|
this.performanceMetrics.memoryUsage = this.estimateMemoryUsage()
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Estimate current memory usage
|
||
|
|
*/
|
||
|
|
private estimateMemoryUsage(): number {
|
||
|
|
let totalMemory = 0
|
||
|
|
|
||
|
|
if (this.partitionedIndex) {
|
||
|
|
// Rough estimate: 1KB per vector
|
||
|
|
totalMemory += this.partitionedIndex.size() * 1024
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.cacheManager) {
|
||
|
|
const cacheStats = this.cacheManager.getStats()
|
||
|
|
totalMemory += (cacheStats.hotCacheSize + cacheStats.warmCacheSize) * 1024
|
||
|
|
}
|
||
|
|
|
||
|
|
return totalMemory
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate performance report
|
||
|
|
*/
|
||
|
|
public generatePerformanceReport(): string {
|
||
|
|
const metrics = this.getPerformanceMetrics()
|
||
|
|
|
||
|
|
return `
|
||
|
|
=== Scaled HNSW System Performance Report ===
|
||
|
|
|
||
|
|
Dataset Configuration:
|
||
|
|
- Expected Size: ${this.config.expectedDatasetSize.toLocaleString()} vectors
|
||
|
|
- Current Size: ${metrics.indexSize.toLocaleString()} vectors
|
||
|
|
- Memory Budget: ${(this.config.maxMemoryUsage / 1024 / 1024 / 1024).toFixed(1)}GB
|
||
|
|
- Target Latency: ${this.config.targetSearchLatency}ms
|
||
|
|
|
||
|
|
Performance Metrics:
|
||
|
|
- Total Searches: ${metrics.totalSearches.toLocaleString()}
|
||
|
|
- Average Search Time: ${metrics.averageSearchTime.toFixed(1)}ms
|
||
|
|
- Cache Hit Rate: ${(metrics.cacheHitRate * 100).toFixed(1)}%
|
||
|
|
- Memory Usage: ${(metrics.memoryUsage / 1024 / 1024).toFixed(1)}MB
|
||
|
|
- Compression Ratio: ${metrics.compressionRatio ? (metrics.compressionRatio * 100).toFixed(1) + '%' : 'N/A'}
|
||
|
|
|
||
|
|
System Status: ${this.getSystemStatus()}
|
||
|
|
`.trim()
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get overall system status
|
||
|
|
*/
|
||
|
|
private getSystemStatus(): string {
|
||
|
|
const metrics = this.getPerformanceMetrics()
|
||
|
|
|
||
|
|
if (metrics.averageSearchTime <= this.config.targetSearchLatency) {
|
||
|
|
return '✅ OPTIMAL'
|
||
|
|
} else if (metrics.averageSearchTime <= this.config.targetSearchLatency * 2) {
|
||
|
|
return '⚠️ ACCEPTABLE'
|
||
|
|
} else {
|
||
|
|
return '❌ NEEDS OPTIMIZATION'
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Cleanup system resources
|
||
|
|
*/
|
||
|
|
public cleanup(): void {
|
||
|
|
this.distributedSearch?.cleanup()
|
||
|
|
this.cacheManager?.clear()
|
||
|
|
this.readOnlyOptimizations?.cleanup()
|
||
|
|
this.partitionedIndex?.clear()
|
||
|
|
|
||
|
|
console.log('Scaled HNSW System cleaned up')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Export convenience factory function
|
||
|
|
export function createScaledHNSWSystem(config: ScaledHNSWConfig): ScaledHNSWSystem {
|
||
|
|
return new ScaledHNSWSystem(config)
|
||
|
|
}
|