Initial commit: Brainy - Multi-Dimensional AI Database

Open source vector database with HNSW indexing, graph relationships,
and metadata facets. Features CLI with professional augmentation registry
integration for discovering extensions and capabilities.
This commit is contained in:
David Snelling 2025-08-18 17:35:06 -07:00
commit f8c45f2d8d
448 changed files with 103294 additions and 0 deletions

520
dist/storage/enhancedCacheManager.js vendored Normal file
View file

@ -0,0 +1,520 @@
/**
* Enhanced Multi-Level Cache Manager with Predictive Prefetching
* Optimized for HNSW search patterns and large-scale vector operations
*/
// Prefetch prediction strategies
var PrefetchStrategy;
(function (PrefetchStrategy) {
PrefetchStrategy["GRAPH_CONNECTIVITY"] = "connectivity";
PrefetchStrategy["VECTOR_SIMILARITY"] = "similarity";
PrefetchStrategy["ACCESS_PATTERN"] = "pattern";
PrefetchStrategy["HYBRID"] = "hybrid";
})(PrefetchStrategy || (PrefetchStrategy = {}));
/**
* Enhanced cache manager with intelligent prefetching for HNSW operations
* Provides multi-level caching optimized for vector search workloads
*/
export class EnhancedCacheManager {
constructor(config = {}) {
this.hotCache = new Map();
this.warmCache = new Map();
this.prefetchQueue = new Set();
this.accessPatterns = new Map(); // Track access times
this.vectorIndex = new Map(); // For similarity calculations
this.prefetchInProgress = false;
// Statistics and monitoring
this.stats = {
hotCacheHits: 0,
hotCacheMisses: 0,
warmCacheHits: 0,
warmCacheMisses: 0,
prefetchHits: 0,
prefetchMisses: 0,
totalPrefetched: 0,
predictionAccuracy: 0,
backgroundOptimizations: 0
};
this.config = {
hotCacheMaxSize: 1000,
hotCacheEvictionThreshold: 0.8,
warmCacheMaxSize: 10000,
warmCacheTTL: 300000, // 5 minutes
prefetchEnabled: true,
prefetchStrategy: PrefetchStrategy.HYBRID,
prefetchBatchSize: 50,
predictionLookahead: 3,
similarityThreshold: 0.8,
maxSimilarityDistance: 2.0,
backgroundOptimization: true,
statisticsCollection: true,
...config
};
// Start background optimization if enabled
if (this.config.backgroundOptimization) {
this.startBackgroundOptimization();
}
}
/**
* Set storage adapters for warm/cold storage operations
*/
setStorageAdapters(storageAdapter, batchOperations) {
this.storageAdapter = storageAdapter;
this.batchOperations = batchOperations;
}
/**
* Get item with intelligent prefetching
*/
async get(id) {
const startTime = Date.now();
// Update access pattern
this.recordAccess(id, startTime);
// Check hot cache first
let entry = this.hotCache.get(id);
if (entry && !this.isExpired(entry)) {
entry.lastAccessed = startTime;
entry.accessCount++;
this.stats.hotCacheHits++;
// Trigger predictive prefetch
if (this.config.prefetchEnabled) {
this.schedulePrefetch(id, entry.data);
}
return entry.data;
}
this.stats.hotCacheMisses++;
// Check warm cache
entry = this.warmCache.get(id);
if (entry && !this.isExpired(entry)) {
entry.lastAccessed = startTime;
entry.accessCount++;
this.stats.warmCacheHits++;
// Promote to hot cache if frequently accessed
if (entry.accessCount > 3) {
this.promoteToHotCache(id, entry);
}
return entry.data;
}
this.stats.warmCacheMisses++;
// Load from storage
const item = await this.loadFromStorage(id);
if (item) {
// Cache the item
await this.set(id, item);
// Trigger predictive prefetch
if (this.config.prefetchEnabled) {
this.schedulePrefetch(id, item);
}
}
return item;
}
/**
* Get multiple items efficiently with batch operations
*/
async getMany(ids) {
const result = new Map();
const uncachedIds = [];
// Check caches first
for (const id of ids) {
const cached = await this.get(id);
if (cached) {
result.set(id, cached);
}
else {
uncachedIds.push(id);
}
}
// Batch load uncached items
if (uncachedIds.length > 0 && this.batchOperations) {
const batchResult = await this.batchOperations.batchGetNodes(uncachedIds);
// Cache loaded items
for (const [id, item] of batchResult.items) {
await this.set(id, item);
result.set(id, item);
}
}
return result;
}
/**
* Set item in cache with metadata
*/
async set(id, item) {
const now = Date.now();
const entry = {
data: item,
lastAccessed: now,
accessCount: 1,
expiresAt: now + this.config.warmCacheTTL,
connectedNodes: this.extractConnectedNodes(item),
predictionScore: 0
};
// Store vector for similarity calculations
if ('vector' in item && item.vector) {
this.vectorIndex.set(id, item.vector);
entry.vectorSimilarity = 0;
}
// Add to warm cache initially
this.warmCache.set(id, entry);
// Clean up if needed
if (this.warmCache.size > this.config.warmCacheMaxSize) {
this.evictFromWarmCache();
}
// Update statistics
this.stats.warmCacheHits++; // Count as a potential future hit
}
/**
* Intelligent prefetch based on access patterns and graph structure
*/
async schedulePrefetch(currentId, currentItem) {
if (this.prefetchInProgress || !this.config.prefetchEnabled) {
return;
}
// Use different strategies based on configuration
let candidateIds = [];
switch (this.config.prefetchStrategy) {
case PrefetchStrategy.GRAPH_CONNECTIVITY:
candidateIds = this.predictByConnectivity(currentId, currentItem);
break;
case PrefetchStrategy.VECTOR_SIMILARITY:
candidateIds = await this.predictBySimilarity(currentId, currentItem);
break;
case PrefetchStrategy.ACCESS_PATTERN:
candidateIds = this.predictByAccessPattern(currentId);
break;
case PrefetchStrategy.HYBRID:
candidateIds = await this.hybridPrediction(currentId, currentItem);
break;
}
// Filter out already cached items
const uncachedIds = candidateIds.filter(id => !this.hotCache.has(id) && !this.warmCache.has(id)).slice(0, this.config.prefetchBatchSize);
if (uncachedIds.length > 0) {
this.executePrefetch(uncachedIds);
}
}
/**
* Predict next nodes based on graph connectivity
*/
predictByConnectivity(currentId, currentItem) {
const candidates = [];
if ('connections' in currentItem && currentItem.connections) {
const connections = currentItem.connections;
// Add immediate neighbors with higher priority for lower levels
for (const [level, nodeIds] of connections.entries()) {
const priority = Math.max(1, 5 - level); // Higher priority for level 0
for (const nodeId of nodeIds) {
// Add based on priority
for (let i = 0; i < priority; i++) {
candidates.push(nodeId);
}
}
}
}
// Shuffle and deduplicate
const shuffled = candidates.sort(() => Math.random() - 0.5);
return [...new Set(shuffled)];
}
/**
* Predict next nodes based on vector similarity
*/
async predictBySimilarity(currentId, currentItem) {
if (!('vector' in currentItem) || !currentItem.vector) {
return [];
}
const currentVector = currentItem.vector;
const similarities = [];
// Calculate similarities with vectors in cache
for (const [id, vector] of this.vectorIndex.entries()) {
if (id === currentId)
continue;
const similarity = this.cosineSimilarity(currentVector, vector);
if (similarity > this.config.similarityThreshold) {
similarities.push([id, similarity]);
}
}
// Sort by similarity and return top candidates
similarities.sort((a, b) => b[1] - a[1]);
return similarities.slice(0, this.config.prefetchBatchSize).map(([id]) => id);
}
/**
* Predict based on historical access patterns
*/
predictByAccessPattern(currentId) {
const currentPattern = this.accessPatterns.get(currentId);
if (!currentPattern || currentPattern.length < 2) {
return [];
}
// Find similar access patterns
const candidates = [];
for (const [id, pattern] of this.accessPatterns.entries()) {
if (id === currentId || pattern.length < 2)
continue;
const similarity = this.patternSimilarity(currentPattern, pattern);
if (similarity > 0.5) {
candidates.push([id, similarity]);
}
}
candidates.sort((a, b) => b[1] - a[1]);
return candidates.slice(0, this.config.prefetchBatchSize).map(([id]) => id);
}
/**
* Hybrid prediction combining multiple strategies
*/
async hybridPrediction(currentId, currentItem) {
const connectivityCandidates = this.predictByConnectivity(currentId, currentItem);
const similarityCandidates = await this.predictBySimilarity(currentId, currentItem);
const patternCandidates = this.predictByAccessPattern(currentId);
// Weighted combination
const candidateScores = new Map();
// Connectivity gets highest weight (40%)
connectivityCandidates.forEach((id, index) => {
const score = (connectivityCandidates.length - index) / connectivityCandidates.length * 0.4;
candidateScores.set(id, (candidateScores.get(id) || 0) + score);
});
// Similarity gets medium weight (35%)
similarityCandidates.forEach((id, index) => {
const score = (similarityCandidates.length - index) / similarityCandidates.length * 0.35;
candidateScores.set(id, (candidateScores.get(id) || 0) + score);
});
// Pattern gets lower weight (25%)
patternCandidates.forEach((id, index) => {
const score = (patternCandidates.length - index) / patternCandidates.length * 0.25;
candidateScores.set(id, (candidateScores.get(id) || 0) + score);
});
// Sort by combined score
const sortedCandidates = Array.from(candidateScores.entries())
.sort((a, b) => b[1] - a[1])
.map(([id]) => id);
return sortedCandidates.slice(0, this.config.prefetchBatchSize);
}
/**
* Execute prefetch operation in background
*/
async executePrefetch(ids) {
if (this.prefetchInProgress || !this.batchOperations) {
return;
}
this.prefetchInProgress = true;
try {
const batchResult = await this.batchOperations.batchGetNodes(ids);
// Cache prefetched items
for (const [id, item] of batchResult.items) {
const entry = {
data: item,
lastAccessed: Date.now(),
accessCount: 0, // Prefetched items start with 0 access count
expiresAt: Date.now() + this.config.warmCacheTTL,
connectedNodes: this.extractConnectedNodes(item),
predictionScore: 1 // Mark as prefetched
};
this.warmCache.set(id, entry);
}
this.stats.totalPrefetched += batchResult.items.size;
}
catch (error) {
console.warn('Prefetch operation failed:', error);
}
finally {
this.prefetchInProgress = false;
}
}
/**
* Load item from storage adapter
*/
async loadFromStorage(id) {
if (!this.storageAdapter) {
return null;
}
try {
return await this.storageAdapter.get(id);
}
catch (error) {
console.warn(`Failed to load ${id} from storage:`, error);
return null;
}
}
/**
* Promote frequently accessed item to hot cache
*/
promoteToHotCache(id, entry) {
// Remove from warm cache
this.warmCache.delete(id);
// Add to hot cache
this.hotCache.set(id, entry);
// Evict if necessary
if (this.hotCache.size > this.config.hotCacheMaxSize) {
this.evictFromHotCache();
}
}
/**
* Evict least recently used items from hot cache
*/
evictFromHotCache() {
const threshold = Math.floor(this.config.hotCacheMaxSize * this.config.hotCacheEvictionThreshold);
if (this.hotCache.size <= threshold) {
return;
}
// Sort by last accessed time and access count
const entries = Array.from(this.hotCache.entries())
.sort((a, b) => {
const scoreA = a[1].accessCount * 0.7 + (Date.now() - a[1].lastAccessed) * -0.3;
const scoreB = b[1].accessCount * 0.7 + (Date.now() - b[1].lastAccessed) * -0.3;
return scoreA - scoreB;
});
// Remove least valuable entries
const toRemove = entries.slice(0, this.hotCache.size - threshold);
for (const [id] of toRemove) {
this.hotCache.delete(id);
}
}
/**
* Evict expired items from warm cache
*/
evictFromWarmCache() {
const now = Date.now();
const toRemove = [];
for (const [id, entry] of this.warmCache.entries()) {
if (this.isExpired(entry)) {
toRemove.push(id);
}
}
// Remove expired items
for (const id of toRemove) {
this.warmCache.delete(id);
this.vectorIndex.delete(id);
}
// If still over limit, remove LRU items
if (this.warmCache.size > this.config.warmCacheMaxSize) {
const entries = Array.from(this.warmCache.entries())
.sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);
const excess = this.warmCache.size - this.config.warmCacheMaxSize;
for (let i = 0; i < excess; i++) {
const [id] = entries[i];
this.warmCache.delete(id);
this.vectorIndex.delete(id);
}
}
}
/**
* Record access pattern for prediction
*/
recordAccess(id, timestamp) {
if (!this.config.statisticsCollection) {
return;
}
let pattern = this.accessPatterns.get(id);
if (!pattern) {
pattern = [];
this.accessPatterns.set(id, pattern);
}
pattern.push(timestamp);
// Keep only recent accesses (last 10)
if (pattern.length > 10) {
pattern.shift();
}
}
/**
* Extract connected node IDs from HNSW item
*/
extractConnectedNodes(item) {
const connected = new Set();
if ('connections' in item && item.connections) {
const connections = item.connections;
for (const nodeIds of connections.values()) {
nodeIds.forEach(id => connected.add(id));
}
}
return connected;
}
/**
* Check if cache entry is expired
*/
isExpired(entry) {
return entry.expiresAt !== null && Date.now() > entry.expiresAt;
}
/**
* Calculate cosine similarity between vectors
*/
cosineSimilarity(a, b) {
if (a.length !== b.length)
return 0;
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
return magnitude === 0 ? 0 : dotProduct / magnitude;
}
/**
* Calculate pattern similarity between access patterns
*/
patternSimilarity(pattern1, pattern2) {
const minLength = Math.min(pattern1.length, pattern2.length);
if (minLength < 2)
return 0;
// Calculate intervals between accesses
const intervals1 = pattern1.slice(1).map((t, i) => t - pattern1[i]);
const intervals2 = pattern2.slice(1).map((t, i) => t - pattern2[i]);
// Compare interval patterns
let similarity = 0;
const compareLength = Math.min(intervals1.length, intervals2.length);
for (let i = 0; i < compareLength; i++) {
const diff = Math.abs(intervals1[i] - intervals2[i]);
const maxInterval = Math.max(intervals1[i], intervals2[i]);
similarity += maxInterval === 0 ? 1 : 1 - (diff / maxInterval);
}
return compareLength === 0 ? 0 : similarity / compareLength;
}
/**
* Start background optimization process
*/
startBackgroundOptimization() {
setInterval(() => {
this.runBackgroundOptimization();
}, 60000); // Run every minute
}
/**
* Run background optimization tasks
*/
runBackgroundOptimization() {
// Clean up expired entries
this.evictFromWarmCache();
this.evictFromHotCache();
// Clean up old access patterns
const cutoff = Date.now() - 3600000; // 1 hour
for (const [id, pattern] of this.accessPatterns.entries()) {
const recentAccesses = pattern.filter(t => t > cutoff);
if (recentAccesses.length === 0) {
this.accessPatterns.delete(id);
}
else {
this.accessPatterns.set(id, recentAccesses);
}
}
this.stats.backgroundOptimizations++;
}
/**
* Get cache statistics
*/
getStats() {
return {
...this.stats,
hotCacheSize: this.hotCache.size,
warmCacheSize: this.warmCache.size,
prefetchQueueSize: this.prefetchQueue.size,
accessPatternsTracked: this.accessPatterns.size
};
}
/**
* Clear all caches
*/
clear() {
this.hotCache.clear();
this.warmCache.clear();
this.prefetchQueue.clear();
this.accessPatterns.clear();
this.vectorIndex.clear();
}
}
//# sourceMappingURL=enhancedCacheManager.js.map