@codai/cbd
Version:
Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server
619 lines • 23 kB
JavaScript
/**
* CBD Universal Storage Engine
* Adaptive storage supporting all database paradigms with optimal performance
*
* Phase 1: Foundation storage engine with adaptive layout and multi-paradigm support
*/
import { EventEmitter } from 'events';
import { UniversalRecordUtils } from './UniversalDataModel.js';
/**
* Universal Storage Engine - core engine supporting all paradigms
*/
export class UniversalStorageEngine extends EventEmitter {
config;
initialized = false;
stats;
// Storage layers
memoryCache;
persistentStorage;
indexManager;
compressionEngine;
// Performance monitoring
performanceTracker;
lastFlushTime;
constructor(config) {
super();
this.config = config;
this.lastFlushTime = Date.now();
// Initialize components
this.memoryCache = new Map();
this.persistentStorage = new PersistentStorageLayer(config);
this.indexManager = new IndexManager(config);
this.compressionEngine = new CompressionEngine(config.compressionAlgorithm);
this.performanceTracker = new PerformanceTracker();
// Initialize stats
this.stats = this.createInitialStats();
// Set up periodic maintenance
this.setupMaintenanceTasks();
}
/**
* Initialize the storage engine
*/
async initialize() {
if (this.initialized)
return;
const startTime = Date.now();
try {
console.log('🚀 Initializing Universal Storage Engine...');
// Initialize storage layers
await this.persistentStorage.initialize();
await this.indexManager.initialize();
await this.compressionEngine.initialize();
// Load existing data into memory cache (selective loading)
await this.loadHotData();
// Verify integrity
await this.verifyStorageIntegrity();
this.initialized = true;
const initTime = Date.now() - startTime;
console.log(`✅ Universal Storage Engine initialized in ${initTime}ms`);
this.emit('initialized', { initTime });
}
catch (error) {
console.error('❌ Failed to initialize Universal Storage Engine:', error);
throw new Error(`Storage engine initialization failed: ${error}`);
}
}
/**
* Store a universal record
*/
async store(record, context) {
await this.ensureInitialized();
const startTime = Date.now();
try {
// Validate record
const validation = UniversalRecordUtils.validate(record);
if (!validation.valid) {
throw new Error(`Invalid record: ${validation.errors.join(', ')}`);
}
// Update metadata
record.metadata.updatedAt = new Date();
record.version += 1;
// Determine optimal storage strategy
const storageStrategy = await this.determineStorageStrategy(record, context);
// Apply compression if enabled
const processedRecord = await this.compressionEngine.compress(record, storageStrategy);
// Store in memory cache
this.memoryCache.set(record.id.id, processedRecord);
// Update indexes
await this.indexManager.updateIndexes(processedRecord, 'insert');
// Persist to storage (async for performance)
this.persistentStorage.store(processedRecord, storageStrategy).catch(error => {
console.error('Persistent storage error:', error);
this.emit('storageError', { recordId: record.id.id, error });
});
// Update statistics
this.updateStatsAfterWrite(record, Date.now() - startTime);
// Emit event
this.emit('recordStored', { recordId: record.id.id, paradigm: record.data.type });
return record.id.id;
}
catch (error) {
this.performanceTracker.recordError('store', Date.now() - startTime);
throw new Error(`Failed to store record: ${error}`);
}
}
/**
* Retrieve a record by ID
*/
async get(recordId, context) {
await this.ensureInitialized();
const startTime = Date.now();
try {
// Check memory cache first
let record = this.memoryCache.get(recordId);
let cacheHit = !!record;
if (!record) {
// Load from persistent storage
record = await this.persistentStorage.get(recordId) || undefined;
if (record) {
// Decompress if needed
record = await this.compressionEngine.decompress(record);
// Add to cache (with eviction policy)
this.addToCache(record);
}
}
if (record) {
// Update access metadata
record.metadata.accessCount++;
record.metadata.lastAccessed = new Date();
// Apply security filtering if needed
record = await this.applySecurityFiltering(record, context);
}
// Update statistics
this.updateStatsAfterRead(cacheHit, Date.now() - startTime);
return record || null;
}
catch (error) {
this.performanceTracker.recordError('get', Date.now() - startTime);
throw new Error(`Failed to get record ${recordId}: ${error}`);
}
}
/**
* Query records using multi-paradigm query
*/
async query(query, context) {
await this.ensureInitialized();
const startTime = Date.now();
try {
// Parse and optimize query
const optimizedQuery = await this.optimizeQuery(query, context);
// Determine execution strategy
const executionPlan = await this.createExecutionPlan(optimizedQuery, context);
// Execute query using appropriate indexes
const rawResults = await this.executeQuery(executionPlan, context);
// Apply post-processing (filtering, sorting, aggregation)
const processedResults = await this.postProcessResults(rawResults, optimizedQuery, context);
// Prepare final result
const result = {
data: processedResults,
metadata: {
executionTime: Date.now() - startTime,
recordsScanned: executionPlan.recordsScanned,
recordsReturned: processedResults.length,
indexesUsed: executionPlan.indexesUsed,
queryPlan: this.config.nodeId ? executionPlan : undefined,
cacheHit: executionPlan.cacheUtilized
}
};
// Update statistics
this.updateStatsAfterQuery(result);
return result;
}
catch (error) {
this.performanceTracker.recordError('query', Date.now() - startTime);
throw new Error(`Query execution failed: ${error}`);
}
}
/**
* Update a record
*/
async update(recordId, updates, context) {
await this.ensureInitialized();
const startTime = Date.now();
try {
// Get existing record
const existingRecord = await this.get(recordId, context);
if (!existingRecord) {
return false;
}
// Apply updates
const updatedRecord = {
...existingRecord,
...updates,
id: { ...existingRecord.id, updatedAt: new Date() },
version: existingRecord.version + 1,
metadata: {
...existingRecord.metadata,
...updates.metadata,
updatedAt: new Date(),
updatedBy: context?.user || 'system'
}
};
// Validate updated record
const validation = UniversalRecordUtils.validate(updatedRecord);
if (!validation.valid) {
throw new Error(`Invalid update: ${validation.errors.join(', ')}`);
}
// Update in cache and storage
await this.store(updatedRecord, context);
// Update indexes
await this.indexManager.updateIndexes(updatedRecord, 'update', existingRecord);
this.emit('recordUpdated', { recordId, version: updatedRecord.version });
return true;
}
catch (error) {
this.performanceTracker.recordError('update', Date.now() - startTime);
throw new Error(`Failed to update record ${recordId}: ${error}`);
}
}
/**
* Delete a record
*/
async delete(recordId, context) {
await this.ensureInitialized();
const startTime = Date.now();
try {
// Get record to validate existence and permissions
const record = await this.get(recordId, context);
if (!record) {
return false;
}
// Check delete permissions
if (!await this.hasDeletePermission(record, context)) {
throw new Error('Insufficient permissions to delete record');
}
// Remove from cache
this.memoryCache.delete(recordId);
// Remove from indexes
await this.indexManager.updateIndexes(record, 'delete');
// Remove from persistent storage
await this.persistentStorage.delete(recordId);
// Update statistics
this.updateStatsAfterDelete(record);
this.emit('recordDeleted', { recordId, paradigm: record.data.type });
return true;
}
catch (error) {
this.performanceTracker.recordError('delete', Date.now() - startTime);
throw new Error(`Failed to delete record ${recordId}: ${error}`);
}
}
/**
* Get storage statistics
*/
async getStats() {
await this.ensureInitialized();
// Update real-time stats
this.stats.memoryUsageBytes = this.calculateMemoryUsage();
this.stats.cacheHitRate = this.performanceTracker.getCacheHitRate();
this.stats.averageReadLatency = this.performanceTracker.getAverageLatency('read');
this.stats.averageWriteLatency = this.performanceTracker.getAverageLatency('write');
this.stats.errorRate = this.performanceTracker.getErrorRate();
this.stats.uptime = (Date.now() - this.performanceTracker.startTime) / 1000;
this.stats.lastHealthCheck = new Date();
return { ...this.stats };
}
/**
* Optimize storage performance
*/
async optimize() {
await this.ensureInitialized();
console.log('🔧 Starting storage optimization...');
try {
// Optimize indexes
await this.indexManager.optimize();
// Compact storage
await this.persistentStorage.compact();
// Optimize memory cache
await this.optimizeMemoryCache();
// Update statistics
await this.recalculateStats();
console.log('✅ Storage optimization completed');
this.emit('optimizationCompleted');
}
catch (error) {
console.error('❌ Storage optimization failed:', error);
this.emit('optimizationFailed', error);
}
}
/**
* Shutdown the storage engine gracefully
*/
async shutdown() {
if (!this.initialized)
return;
console.log('🛑 Shutting down Universal Storage Engine...');
try {
// Flush pending writes
await this.flush();
// Shutdown components
await this.persistentStorage.shutdown();
await this.indexManager.shutdown();
await this.compressionEngine.shutdown();
// Clear cache
this.memoryCache.clear();
this.initialized = false;
console.log('✅ Universal Storage Engine shut down gracefully');
}
catch (error) {
console.error('❌ Error during shutdown:', error);
throw error;
}
}
// Private helper methods
async ensureInitialized() {
if (!this.initialized) {
await this.initialize();
}
}
createInitialStats() {
return {
totalRecords: 0,
relationalRecords: 0,
documentRecords: 0,
graphNodes: 0,
graphEdges: 0,
vectorRecords: 0,
timeSeriesRecords: 0,
keyValueRecords: 0,
totalSizeBytes: 0,
compressedSizeBytes: 0,
compressionRatio: 1.0,
averageReadLatency: 0,
averageWriteLatency: 0,
cacheHitRate: 0,
indexEfficiency: 1.0,
memoryUsageBytes: 0,
diskUsageBytes: 0,
cpuUsagePercent: 0,
uptime: 0,
lastHealthCheck: new Date(),
errorRate: 0
};
}
setupMaintenanceTasks() {
// Periodic flush
setInterval(async () => {
try {
await this.flush();
}
catch (error) {
console.error('Periodic flush failed:', error);
}
}, this.config.flushInterval * 1000);
// Stats recalculation
setInterval(async () => {
try {
await this.recalculateStats();
}
catch (error) {
console.error('Stats recalculation failed:', error);
}
}, 60000); // Every minute
// Cache cleanup
setInterval(() => {
this.cleanupCache();
}, 300000); // Every 5 minutes
}
async loadHotData() {
// Implementation for loading frequently accessed data into cache
console.log('🔥 Loading hot data into memory cache...');
// This would be implemented based on access patterns and metadata
}
async verifyStorageIntegrity() {
// Implementation for storage integrity verification
console.log('🔍 Verifying storage integrity...');
// This would check for corrupted data, missing indexes, etc.
}
async determineStorageStrategy(_record, _context) {
// Determine optimal storage strategy based on data type and access patterns
return {
layout: this.config.defaultLayout,
compression: this.config.compressionEnabled,
indexHints: []
// partitioning is optional, so omit it if not needed
};
}
updateStatsAfterWrite(record, latency) {
this.stats.totalRecords++;
this.stats.averageWriteLatency =
(this.stats.averageWriteLatency + latency) / 2;
// Update paradigm-specific counts
switch (record.data.type) {
case 'relational':
this.stats.relationalRecords++;
break;
case 'document':
this.stats.documentRecords++;
break;
case 'vector':
this.stats.vectorRecords++;
break;
case 'timeseries':
this.stats.timeSeriesRecords++;
break;
case 'keyvalue':
this.stats.keyValueRecords++;
break;
case 'graph':
if (record.data.nodeType) {
this.stats.graphNodes++;
}
else {
this.stats.graphEdges++;
}
break;
}
}
updateStatsAfterRead(cacheHit, latency) {
this.stats.averageReadLatency =
(this.stats.averageReadLatency + latency) / 2;
if (cacheHit) {
this.performanceTracker.recordCacheHit();
}
else {
this.performanceTracker.recordCacheMiss();
}
}
updateStatsAfterQuery(result) {
// Update query-related statistics
this.performanceTracker.recordQuery(result.metadata.executionTime);
}
updateStatsAfterDelete(record) {
this.stats.totalRecords--;
// Update paradigm-specific counts
switch (record.data.type) {
case 'relational':
this.stats.relationalRecords--;
break;
case 'document':
this.stats.documentRecords--;
break;
case 'vector':
this.stats.vectorRecords--;
break;
case 'timeseries':
this.stats.timeSeriesRecords--;
break;
case 'keyvalue':
this.stats.keyValueRecords--;
break;
case 'graph':
if (record.data.nodeType) {
this.stats.graphNodes--;
}
else {
this.stats.graphEdges--;
}
break;
}
}
calculateMemoryUsage() {
// Calculate actual memory usage
let totalSize = 0;
for (const record of this.memoryCache.values()) {
totalSize += this.estimateRecordSize(record);
}
return totalSize;
}
estimateRecordSize(record) {
// Rough estimation of record size in bytes
return JSON.stringify(record).length * 2; // UTF-16 approximation
}
addToCache(record) {
// Add to cache with eviction policy
if (this.memoryCache.size >= this.config.cacheSize) {
this.evictLeastRecentlyUsed();
}
this.memoryCache.set(record.id.id, record);
}
evictLeastRecentlyUsed() {
let oldestTime = Date.now();
let oldestKey = '';
for (const [key, record] of this.memoryCache.entries()) {
const accessTime = record.metadata.lastAccessed.getTime();
if (accessTime < oldestTime) {
oldestTime = accessTime;
oldestKey = key;
}
}
if (oldestKey) {
this.memoryCache.delete(oldestKey);
}
}
async optimizeMemoryCache() {
// Optimize memory cache by removing cold data
const now = Date.now();
const evictionThreshold = 3600000; // 1 hour
for (const [key, record] of this.memoryCache.entries()) {
const lastAccess = record.metadata.lastAccessed.getTime();
if (now - lastAccess > evictionThreshold) {
this.memoryCache.delete(key);
}
}
}
cleanupCache() {
// Regular cache cleanup
this.optimizeMemoryCache().catch(error => {
console.error('Cache cleanup failed:', error);
});
}
async flush() {
// Flush pending writes to persistent storage
console.log('💾 Flushing pending writes...');
await this.persistentStorage.flush();
this.lastFlushTime = Date.now();
}
async applySecurityFiltering(record, context) {
// Apply security filtering based on context
if (!context?.user) {
return record;
}
// Simple security filtering - can be enhanced based on requirements
return record;
}
async optimizeQuery(query, _context) {
// Optimize query for better performance
// This is a placeholder - implement actual query optimization logic
return query;
}
async createExecutionPlan(query, _context) {
// Create execution plan for the query
// This is a placeholder - implement actual execution plan logic
return { query, strategy: 'sequential' };
}
async executeQuery(_executionPlan, _context) {
// Execute the query based on the execution plan
// This is a placeholder - implement actual query execution logic
return [];
}
async postProcessResults(results, _query, _context) {
// Post-process results (sorting, filtering, aggregation)
// This is a placeholder - implement actual post-processing logic
return results;
}
async hasDeletePermission(_record, _context) {
// Check if user has permission to delete this record
// Simple permission check - can be enhanced based on requirements
return true;
}
/**
* Get time since last flush in milliseconds
*/
getTimeSinceLastFlush() {
return Date.now() - this.lastFlushTime;
}
async recalculateStats() {
// Recalculate comprehensive statistics
const newStats = await this.persistentStorage.calculateStats();
this.stats = { ...this.stats, ...newStats };
}
}
// Placeholder classes - these would be fully implemented in separate files
class PersistentStorageLayer {
constructor(_config) { }
async initialize() { }
async store(_record, _strategy) { }
async get(_recordId) { return null; }
async delete(_recordId) { }
async flush() { }
async compact() { }
async shutdown() { }
async calculateStats() { return {}; }
}
class IndexManager {
constructor(_config) { }
async initialize() { }
async updateIndexes(_record, _operation, _oldRecord) { }
async optimize() { }
async shutdown() { }
}
class CompressionEngine {
constructor(_algorithm) { }
async initialize() { }
async compress(record, _strategy) { return record; }
async decompress(record) { return record; }
async shutdown() { }
}
class PerformanceTracker {
startTime = Date.now();
cacheHits = 0;
cacheMisses = 0;
errors = new Map();
latencies = new Map();
recordCacheHit() { this.cacheHits++; }
recordCacheMiss() { this.cacheMisses++; }
recordError(operation, _latency) {
this.errors.set(operation, (this.errors.get(operation) || 0) + 1);
}
recordQuery(latency) {
const queries = this.latencies.get('query') || [];
queries.push(latency);
this.latencies.set('query', queries);
}
getCacheHitRate() {
const total = this.cacheHits + this.cacheMisses;
return total > 0 ? this.cacheHits / total : 0;
}
getAverageLatency(operation) {
const latencies = this.latencies.get(operation) || [];
return latencies.length > 0 ?
latencies.reduce((sum, lat) => sum + lat, 0) / latencies.length : 0;
}
getErrorRate() {
const totalErrors = Array.from(this.errors.values()).reduce((sum, count) => sum + count, 0);
const totalOperations = this.cacheHits + this.cacheMisses + totalErrors;
return totalOperations > 0 ? totalErrors / totalOperations : 0;
}
}
//# sourceMappingURL=UniversalStorageEngine.js.map