@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
195 lines • 6.8 kB
JavaScript
/**
* 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