UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

195 lines 6.8 kB
/** * Sync Cache Integration * * Integrates the IncrementalSyncManager with the InvalidationEngine * to ensure cache invalidation happens automatically during sync operations. */ import { getLogger } from '../../../logging/Logger.js'; import EventEmitter from 'events'; export class SyncCacheIntegration extends EventEmitter { logger = getLogger(); invalidationEngine; config; // Batch invalidation tracking pendingInvalidations = new Map(); batchTimer; // Sync tracking activeSyncs = new Set(); syncStats = { totalSyncs: 0, entitiesSynced: 0, cacheInvalidations: 0, lastSyncTime: null, }; constructor(invalidationEngine, config = {}) { super(); this.invalidationEngine = invalidationEngine; this.config = { enabled: true, batchInvalidation: true, batchDelay: 1000, // 1 second warmupAfterSync: false, ...config }; this.logger.info(`SyncCacheIntegration initialized with batch delay: ${this.config.batchDelay}ms`); } /** * Handle sync start event */ async handleSyncStart(projectId) { if (!this.config.enabled) return; this.logger.info(`Sync started for project ${projectId}`); this.activeSyncs.add(projectId); this.syncStats.totalSyncs++; this.emit('sync_start', { projectId }); } /** * Handle sync completion event */ async handleSyncComplete(projectId) { if (!this.config.enabled) return; this.logger.info(`Sync completed for project ${projectId}`); this.activeSyncs.delete(projectId); this.syncStats.lastSyncTime = new Date(); // Flush any pending batch invalidations if (this.config.batchInvalidation) { await this.flushBatchInvalidations(); } this.emit('sync_complete', { projectId }); // Optionally warm up cache with common queries if (this.config.warmupAfterSync) { this.emit('warmup_requested', { projectId }); } } /** * Handle entity sync event */ async handleEntitySync(event) { if (!this.config.enabled) return; this.logger.debug(`Entity synced: ${event.entity} (${event.operation})`); this.syncStats.entitiesSynced++; // Convert to invalidation event const invalidationEvent = { type: event.type === 'entity_deleted' ? 'entity_deleted' : 'entity_changed', entity: event.entity, entityId: event.entityId, projectId: event.projectId, timestamp: Date.now(), changeType: event.operation, metadata: { entityKey: event.entityKey, syncEvent: true } }; if (this.config.batchInvalidation && this.activeSyncs.has(event.projectId)) { // Add to batch this.addToBatch(event.entity, event.projectId); } else { // Invalidate immediately const count = await this.invalidationEngine.handleEntityChange(invalidationEvent); this.syncStats.cacheInvalidations += count; } } /** * Handle multiple entity changes at once */ async handleBulkEntitySync(entities, projectId) { if (!this.config.enabled) return; this.logger.info(`Bulk sync for ${entities.length} entities in project ${projectId}`); if (this.config.batchInvalidation) { // Add all to batch for (const { entity } of entities) { this.addToBatch(entity, projectId); } } else { // Get unique entity types const uniqueEntities = [...new Set(entities.map(e => e.entity))]; const count = await this.invalidationEngine.handleBulkChange(uniqueEntities, projectId); this.syncStats.cacheInvalidations += count; } } /** * Add entity to batch invalidation */ addToBatch(entity, projectId) { const key = projectId; if (!this.pendingInvalidations.has(key)) { this.pendingInvalidations.set(key, new Set()); } this.pendingInvalidations.get(key).add(entity); // Reset batch timer if (this.batchTimer) { clearTimeout(this.batchTimer); } this.batchTimer = setTimeout(() => { this.flushBatchInvalidations().catch(err => { this.logger.error(`Batch invalidation failed: ${err}`); }); }, this.config.batchDelay); } /** * Flush pending batch invalidations */ async flushBatchInvalidations() { if (this.pendingInvalidations.size === 0) return; this.logger.info(`Flushing batch invalidations for ${this.pendingInvalidations.size} projects`); for (const [projectId, entities] of this.pendingInvalidations) { const entityArray = Array.from(entities); const count = await this.invalidationEngine.handleBulkChange(entityArray, projectId); this.syncStats.cacheInvalidations += count; this.logger.info(`Batch invalidated ${count} entries for project ${projectId}`); } this.pendingInvalidations.clear(); if (this.batchTimer) { clearTimeout(this.batchTimer); this.batchTimer = undefined; } } /** * Get sync statistics */ getStats() { return { ...this.syncStats, activeSyncs: this.activeSyncs.size, pendingBatches: this.pendingInvalidations.size, }; } /** * Force invalidation for specific entities */ async invalidateEntities(entities, projectId) { return this.invalidationEngine.handleBulkChange(entities, projectId); } /** * Create a helper method for IncrementalSyncManager to call */ createSyncHandler() { return { onSyncStart: (projectId) => this.handleSyncStart(projectId), onSyncComplete: (projectId) => this.handleSyncComplete(projectId), onEntitySynced: (event) => this.handleEntitySync(event), onBulkSync: (entities, projectId) => this.handleBulkEntitySync(entities, projectId), }; } /** * Shutdown the integration */ async shutdown() { // Flush any pending invalidations await this.flushBatchInvalidations(); if (this.batchTimer) { clearTimeout(this.batchTimer); } this.removeAllListeners(); this.logger.info('SyncCacheIntegration shutdown'); } } //# sourceMappingURL=SyncCacheIntegration.js.map