@topgunbuild/server 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/ServerCoordinator.ts","../src/query/Matcher.ts","../src/query/QueryRegistry.ts","../src/utils/logger.ts","../src/topic/TopicManager.ts","../src/cluster/ClusterManager.ts","../src/cluster/FailureDetector.ts","../src/cluster/PartitionService.ts","../src/cluster/MigrationManager.ts","../src/cluster/LockManager.ts","../src/security/SecurityManager.ts","../src/monitoring/MetricsService.ts","../src/system/SystemManager.ts","../src/utils/BoundedEventQueue.ts","../src/utils/StripedEventExecutor.ts","../src/utils/BackpressureRegulator.ts","../src/utils/CoalescingWriter.ts","../src/memory/BufferPool.ts","../src/memory/ObjectPool.ts","../src/memory/pools/MessagePool.ts","../src/memory/pools/TimestampPool.ts","../src/memory/pools/RecordPool.ts","../src/utils/coalescingPresets.ts","../src/utils/ConnectionRateLimiter.ts","../src/workers/WorkerPool.ts","../src/workers/errors.ts","../src/workers/MerkleWorker.ts","../src/workers/CRDTMergeWorker.ts","../src/workers/SerializationWorker.ts","../src/workers/SharedMemoryManager.ts","../src/tasklet/TaskletScheduler.ts","../src/tasklet/tasklets/IteratorTasklet.ts","../src/ack/WriteAckManager.ts","../src/cluster/ReplicationPipeline.ts","../src/cluster/LagTracker.ts","../src/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts","../src/utils/nativeStats.ts","../src/cluster/ClusterCoordinator.ts"],"sourcesContent":["export * from './ServerCoordinator';\nexport * from './storage';\nexport * from './security/SecurityManager';\nexport * from './utils/logger';\nexport * from './utils/ConnectionRateLimiter';\nexport * from './utils/coalescingPresets';\nexport { CoalescingWriterOptions, CoalescingWriterMetrics } from './utils/CoalescingWriter';\nexport * from './memory';\nexport * from './tasklet';\nexport * from './interceptor/IInterceptor';\nexport { TimestampInterceptor } from './interceptor/TimestampInterceptor';\nexport { RateLimitInterceptor } from './interceptor/RateLimitInterceptor';\n\n// Native module utilities (Phase 3.05)\nexport {\n getNativeStats,\n getNativeModuleStatus,\n logNativeStatus,\n type NativeStats,\n type NativeModuleStatus,\n} from './utils/nativeStats';\n\n// Cluster module (Phase 4)\nexport * from './cluster';\n","import { createServer as createHttpServer, Server as HttpServer } from 'http';\nimport { createServer as createHttpsServer, Server as HttpsServer, ServerOptions as HttpsServerOptions } from 'https';\nimport { readFileSync } from 'fs';\nimport * as net from 'net';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { HLC, LWWMap, ORMap, MerkleTree, serialize, deserialize, PermissionPolicy, Principal, PermissionType, Timestamp, LWWRecord, ORMapRecord, MessageSchema, WriteConcern, WriteConcernValue, ConsistencyLevel, ReplicationConfig, DEFAULT_REPLICATION_CONFIG } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue, ORMapValue, ORMapTombstones } from './storage/IServerStorage';\nimport { IInterceptor, ServerOp, OpContext, ConnectionContext } from './interceptor/IInterceptor';\nimport * as jwt from 'jsonwebtoken';\nimport * as crypto from 'crypto';\nimport { QueryRegistry, Subscription } from './query/QueryRegistry';\n\nconst GC_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\nconst GC_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\nconst CLIENT_HEARTBEAT_TIMEOUT_MS = 20000; // 20 seconds - evict clients that haven't pinged\nconst CLIENT_HEARTBEAT_CHECK_INTERVAL_MS = 5000; // Check for dead clients every 5 seconds\nimport { TopicManager } from './topic/TopicManager';\nimport { ClusterManager } from './cluster/ClusterManager';\nimport { PartitionService } from './cluster/PartitionService';\nimport { LockManager } from './cluster/LockManager';\nimport { executeQuery, Query } from './query/Matcher';\nimport { SecurityManager } from './security/SecurityManager';\nimport { logger } from './utils/logger';\nimport { MetricsService } from './monitoring/MetricsService';\nimport { SystemManager } from './system/SystemManager';\nimport { TLSConfig, ClusterTLSConfig } from './types/TLSConfig';\nimport { StripedEventExecutor } from './utils/StripedEventExecutor';\nimport { BackpressureRegulator } from './utils/BackpressureRegulator';\nimport { CoalescingWriter, CoalescingWriterOptions } from './utils/CoalescingWriter';\nimport { coalescingPresets, CoalescingPreset } from './utils/coalescingPresets';\nimport { ConnectionRateLimiter } from './utils/ConnectionRateLimiter';\nimport { WorkerPool, MerkleWorker, CRDTMergeWorker, SerializationWorker, WorkerPoolConfig } from './workers';\nimport {\n ObjectPool,\n createEventPayloadPool,\n PooledEventPayload,\n} from './memory';\nimport { TaskletScheduler } from './tasklet';\nimport { WriteAckManager } from './ack/WriteAckManager';\nimport { ReplicationPipeline } from './cluster/ReplicationPipeline';\n\ninterface ClientConnection {\n id: string;\n socket: WebSocket;\n writer: CoalescingWriter; // Per-connection write coalescing\n principal?: Principal; // Auth info\n isAuthenticated: boolean;\n subscriptions: Set<string>; // Set of Query IDs\n lastActiveHlc: Timestamp;\n lastPingReceived: number; // Date.now() of last PING received\n}\n\ninterface PendingClusterQuery {\n requestId: string;\n client: ClientConnection;\n queryId: string; // Client's Query ID\n mapName: string;\n query: Query;\n results: { key: string; value: any }[];\n expectedNodes: Set<string>;\n respondedNodes: Set<string>;\n timer: NodeJS.Timeout;\n}\n\nexport interface ServerCoordinatorConfig {\n port: number;\n nodeId: string;\n storage?: IServerStorage;\n jwtSecret?: string;\n host?: string;\n clusterPort?: number;\n peers?: string[];\n securityPolicies?: PermissionPolicy[];\n /** Callback to resolve dynamic peer addresses after ports are known */\n resolvePeers?: () => string[];\n interceptors?: IInterceptor[];\n metricsPort?: number;\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: TLSConfig;\n clusterTls?: ClusterTLSConfig;\n /** Total event queue capacity for bounded queue (default: 10000) */\n eventQueueCapacity?: number;\n /** Number of event queue stripes for parallel processing (default: 4) */\n eventStripeCount?: number;\n /** Enable/disable backpressure (default: true) */\n backpressureEnabled?: boolean;\n /** How often to force sync processing (default: 100 operations) */\n backpressureSyncFrequency?: number;\n /** Maximum pending async operations before blocking (default: 1000) */\n backpressureMaxPending?: number;\n /** Backoff timeout in ms when at capacity (default: 5000) */\n backpressureBackoffMs?: number;\n /** Enable/disable write coalescing (default: true) */\n writeCoalescingEnabled?: boolean;\n /** Coalescing preset: 'conservative', 'balanced', 'highThroughput', 'aggressive' (default: 'highThroughput') */\n writeCoalescingPreset?: CoalescingPreset;\n /** Maximum messages to batch before forcing flush (default: 500 for highThroughput) */\n writeCoalescingMaxBatch?: number;\n /** Maximum delay before flushing in ms (default: 10 for highThroughput) */\n writeCoalescingMaxDelayMs?: number;\n /** Maximum batch size in bytes (default: 262144/256KB for highThroughput) */\n writeCoalescingMaxBytes?: number;\n\n // === Connection Scaling Options ===\n /** WebSocket backlog for pending connections (default: 511) */\n wsBacklog?: number;\n /** Enable WebSocket per-message compression (default: false for CPU savings) */\n wsCompression?: boolean;\n /** Maximum WebSocket payload size in bytes (default: 64MB) */\n wsMaxPayload?: number;\n /** Maximum server connections (default: 10000) */\n maxConnections?: number;\n /** Server timeout in ms (default: 120000 = 2 min) */\n serverTimeout?: number;\n /** Keep-alive timeout in ms (default: 5000) */\n keepAliveTimeout?: number;\n /** Headers timeout in ms (default: 60000) */\n headersTimeout?: number;\n\n // === Rate Limiting Options ===\n /** Enable connection rate limiting (default: true) */\n rateLimitingEnabled?: boolean;\n /** Maximum new connections per second (default: 100) */\n maxConnectionsPerSecond?: number;\n /** Maximum pending connections (default: 1000) */\n maxPendingConnections?: number;\n\n // === Worker Pool Options ===\n /** Enable worker pool for CPU-bound operations (default: false) */\n workerPoolEnabled?: boolean;\n /** Worker pool configuration */\n workerPoolConfig?: Partial<WorkerPoolConfig>;\n\n // === Write Concern Options (Phase 5.01) ===\n /** Default timeout for Write Concern acknowledgments in ms (default: 5000) */\n writeAckTimeout?: number;\n\n // === Replication Options (Phase 4) ===\n /** Enable replication to backup nodes (default: true when cluster has peers) */\n replicationEnabled?: boolean;\n /** Default consistency level for replication (default: EVENTUAL) */\n defaultConsistency?: ConsistencyLevel;\n /** Replication configuration */\n replicationConfig?: Partial<ReplicationConfig>;\n}\n\nexport class ServerCoordinator {\n private httpServer: HttpServer | HttpsServer;\n private metricsServer?: HttpServer;\n private metricsService: MetricsService;\n private wss: WebSocketServer;\n private clients: Map<string, ClientConnection> = new Map();\n\n // Interceptors\n private interceptors: IInterceptor[] = [];\n\n // In-memory storage (partitioned later)\n private maps: Map<string, LWWMap<string, any> | ORMap<string, any>> = new Map();\n private hlc: HLC;\n private storage?: IServerStorage;\n private jwtSecret: string;\n private queryRegistry: QueryRegistry;\n\n private cluster!: ClusterManager;\n private partitionService!: PartitionService;\n private replicationPipeline?: ReplicationPipeline;\n private lockManager!: LockManager;\n private topicManager!: TopicManager;\n private securityManager: SecurityManager;\n private systemManager!: SystemManager;\n\n private pendingClusterQueries: Map<string, PendingClusterQuery> = new Map();\n private gcInterval?: NodeJS.Timeout;\n private heartbeatCheckInterval?: NodeJS.Timeout;\n\n // GC Consensus State\n private gcReports: Map<string, Timestamp> = new Map();\n\n // Track map loading state to avoid returning empty results during async load\n private mapLoadingPromises: Map<string, Promise<void>> = new Map();\n\n // Track pending batch operations for testing purposes\n private pendingBatchOperations: Set<Promise<void>> = new Set();\n\n // Bounded event queue executor for backpressure control\n private eventExecutor: StripedEventExecutor;\n\n // Backpressure regulator for periodic sync processing\n private backpressure: BackpressureRegulator;\n\n // Write coalescing options\n private writeCoalescingEnabled: boolean;\n private writeCoalescingOptions: Partial<CoalescingWriterOptions>;\n\n // Connection rate limiter\n private rateLimiter: ConnectionRateLimiter;\n private rateLimitingEnabled: boolean;\n\n // Worker pool for CPU-bound operations\n private workerPool?: WorkerPool;\n private merkleWorker?: MerkleWorker;\n private crdtMergeWorker?: CRDTMergeWorker;\n private serializationWorker?: SerializationWorker;\n\n // Memory pools for GC pressure reduction\n private eventPayloadPool: ObjectPool<PooledEventPayload>;\n\n // Tasklet scheduler for cooperative multitasking\n private taskletScheduler: TaskletScheduler;\n\n // Write Concern acknowledgment manager (Phase 5.01)\n private writeAckManager: WriteAckManager;\n\n private _actualPort: number = 0;\n private _actualClusterPort: number = 0;\n private _readyPromise: Promise<void>;\n private _readyResolve!: () => void;\n\n constructor(config: ServerCoordinatorConfig) {\n this._readyPromise = new Promise((resolve) => {\n this._readyResolve = resolve;\n });\n\n this.hlc = new HLC(config.nodeId);\n this.storage = config.storage;\n // Handle JWT_SECRET with escaped newlines (e.g., from Docker/Dokploy env vars)\n const rawSecret = config.jwtSecret || process.env.JWT_SECRET || 'topgun-secret-dev';\n this.jwtSecret = rawSecret.replace(/\\\\n/g, '\\n');\n this.queryRegistry = new QueryRegistry();\n this.securityManager = new SecurityManager(config.securityPolicies || []);\n this.interceptors = config.interceptors || [];\n this.metricsService = new MetricsService();\n\n // Initialize bounded event queue executor\n this.eventExecutor = new StripedEventExecutor({\n stripeCount: config.eventStripeCount ?? 4,\n queueCapacity: config.eventQueueCapacity ?? 10000,\n name: `${config.nodeId}-event-executor`,\n onReject: (task) => {\n logger.warn({ nodeId: config.nodeId, key: task.key }, 'Event task rejected due to queue capacity');\n this.metricsService.incEventQueueRejected();\n }\n });\n\n // Initialize backpressure regulator for periodic sync processing\n this.backpressure = new BackpressureRegulator({\n syncFrequency: config.backpressureSyncFrequency ?? 100,\n maxPendingOps: config.backpressureMaxPending ?? 1000,\n backoffTimeoutMs: config.backpressureBackoffMs ?? 5000,\n enabled: config.backpressureEnabled ?? true\n });\n\n // Initialize write coalescing options with preset support\n // Default preset changed from 'conservative' to 'highThroughput' for better performance\n this.writeCoalescingEnabled = config.writeCoalescingEnabled ?? true;\n const preset = coalescingPresets[config.writeCoalescingPreset ?? 'highThroughput'];\n this.writeCoalescingOptions = {\n maxBatchSize: config.writeCoalescingMaxBatch ?? preset.maxBatchSize,\n maxDelayMs: config.writeCoalescingMaxDelayMs ?? preset.maxDelayMs,\n maxBatchBytes: config.writeCoalescingMaxBytes ?? preset.maxBatchBytes,\n };\n\n // Initialize memory pools for GC pressure reduction\n this.eventPayloadPool = createEventPayloadPool({\n maxSize: 4096,\n initialSize: 128,\n });\n\n // Initialize tasklet scheduler for cooperative multitasking\n this.taskletScheduler = new TaskletScheduler({\n defaultTimeBudgetMs: 5,\n maxConcurrent: 20,\n });\n\n // Initialize Write Concern acknowledgment manager (Phase 5.01)\n this.writeAckManager = new WriteAckManager({\n defaultTimeout: config.writeAckTimeout ?? 5000,\n });\n\n // Initialize connection rate limiter\n this.rateLimitingEnabled = config.rateLimitingEnabled ?? true;\n this.rateLimiter = new ConnectionRateLimiter({\n maxConnectionsPerSecond: config.maxConnectionsPerSecond ?? 100,\n maxPendingConnections: config.maxPendingConnections ?? 1000,\n cooldownMs: 1000,\n });\n\n // Initialize worker pool for CPU-bound operations\n if (config.workerPoolEnabled) {\n this.workerPool = new WorkerPool({\n minWorkers: config.workerPoolConfig?.minWorkers ?? 2,\n maxWorkers: config.workerPoolConfig?.maxWorkers,\n taskTimeout: config.workerPoolConfig?.taskTimeout ?? 5000,\n idleTimeout: config.workerPoolConfig?.idleTimeout ?? 30000,\n autoRestart: config.workerPoolConfig?.autoRestart ?? true,\n });\n this.merkleWorker = new MerkleWorker(this.workerPool);\n this.crdtMergeWorker = new CRDTMergeWorker(this.workerPool);\n this.serializationWorker = new SerializationWorker(this.workerPool);\n logger.info({\n minWorkers: config.workerPoolConfig?.minWorkers ?? 2,\n maxWorkers: config.workerPoolConfig?.maxWorkers ?? 'auto'\n }, 'Worker pool initialized for CPU-bound operations');\n }\n\n // HTTP Server Setup first (to get actual port if port=0)\n if (config.tls?.enabled) {\n const tlsOptions = this.buildTLSOptions(config.tls);\n this.httpServer = createHttpsServer(tlsOptions, (_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running (Secure)');\n });\n logger.info('TLS enabled for client connections');\n } else {\n this.httpServer = createHttpServer((_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running');\n });\n\n if (process.env.NODE_ENV === 'production') {\n logger.warn('⚠️ TLS is disabled! Client connections are NOT encrypted.');\n }\n }\n\n const metricsPort = config.metricsPort !== undefined ? config.metricsPort : 9090;\n this.metricsServer = createHttpServer(async (req, res) => {\n if (req.url === '/metrics') {\n try {\n res.setHeader('Content-Type', this.metricsService.getContentType());\n res.end(await this.metricsService.getMetrics());\n } catch (err) {\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n } else {\n res.statusCode = 404;\n res.end();\n }\n });\n this.metricsServer.listen(metricsPort, () => {\n logger.info({ port: metricsPort }, 'Metrics server listening');\n });\n this.metricsServer.on('error', (err) => {\n logger.error({ err, port: metricsPort }, 'Metrics server failed to start');\n });\n\n // Configure WebSocketServer with optimal options for connection scaling\n this.wss = new WebSocketServer({\n server: this.httpServer,\n // Increase backlog for pending connections (default Linux is 128)\n backlog: config.wsBacklog ?? 511,\n // Disable per-message deflate by default (CPU overhead)\n perMessageDeflate: config.wsCompression ?? false,\n // Max payload size (64MB default)\n maxPayload: config.wsMaxPayload ?? 64 * 1024 * 1024,\n // Skip UTF-8 validation for binary messages (performance)\n skipUTF8Validation: true,\n });\n this.wss.on('connection', (ws) => this.handleConnection(ws));\n\n // Configure HTTP server limits for connection scaling\n this.httpServer.maxConnections = config.maxConnections ?? 10000;\n this.httpServer.timeout = config.serverTimeout ?? 120000; // 2 min\n this.httpServer.keepAliveTimeout = config.keepAliveTimeout ?? 5000;\n this.httpServer.headersTimeout = config.headersTimeout ?? 60000;\n\n // Configure socket options for all incoming connections\n this.httpServer.on('connection', (socket: net.Socket) => {\n // Disable Nagle's algorithm for lower latency\n socket.setNoDelay(true);\n // Enable keep-alive with 60s interval\n socket.setKeepAlive(true, 60000);\n });\n\n // Use port 0 to let OS assign a free port\n this.httpServer.listen(config.port, () => {\n const addr = this.httpServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : config.port;\n logger.info({ port: this._actualPort }, 'Server Coordinator listening');\n\n // Now setup cluster with actual/configured cluster port\n const clusterPort = config.clusterPort ?? 0;\n\n // Resolve peers dynamically if callback provided\n const peers = config.resolvePeers ? config.resolvePeers() : (config.peers || []);\n\n this.cluster = new ClusterManager({\n nodeId: config.nodeId,\n host: config.host || 'localhost',\n port: clusterPort,\n peers,\n discovery: config.discovery,\n serviceName: config.serviceName,\n discoveryInterval: config.discoveryInterval,\n tls: config.clusterTls\n });\n this.partitionService = new PartitionService(this.cluster);\n\n // Phase 4: Create ReplicationPipeline (Hazelcast pattern: always create, runtime check)\n // ReplicationPipeline checks cluster size at runtime - no replication for single node\n if (config.replicationEnabled !== false) {\n this.replicationPipeline = new ReplicationPipeline(\n this.cluster,\n this.partitionService,\n {\n ...DEFAULT_REPLICATION_CONFIG,\n defaultConsistency: config.defaultConsistency ?? ConsistencyLevel.EVENTUAL,\n ...config.replicationConfig,\n }\n );\n // Setup operation applier for incoming replications\n this.replicationPipeline.setOperationApplier(this.applyReplicatedOperation.bind(this));\n logger.info({ nodeId: config.nodeId }, 'ReplicationPipeline initialized');\n }\n\n // Phase 4: Listen for partition map changes and broadcast to clients\n this.partitionService.on('rebalanced', (partitionMap, changes) => {\n this.broadcastPartitionMap(partitionMap);\n });\n\n this.lockManager = new LockManager();\n this.lockManager.on('lockGranted', (evt) => this.handleLockGranted(evt));\n\n this.topicManager = new TopicManager({\n cluster: this.cluster,\n sendToClient: (clientId, message) => {\n const client = this.clients.get(clientId);\n if (client && client.socket.readyState === WebSocket.OPEN) {\n client.writer.write(message);\n }\n }\n });\n\n this.systemManager = new SystemManager(\n this.cluster,\n this.metricsService,\n (name) => this.getMap(name) as LWWMap<string, any>\n );\n\n this.setupClusterListeners();\n this.cluster.start().then((actualClusterPort) => {\n this._actualClusterPort = actualClusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started');\n this.systemManager.start();\n this._readyResolve();\n }).catch((err) => {\n // Fallback for ClusterManager that doesn't return port\n this._actualClusterPort = clusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started (sync)');\n this.systemManager.start();\n this._readyResolve();\n });\n });\n\n if (this.storage) {\n this.storage.initialize().then(() => {\n logger.info('Storage adapter initialized');\n }).catch(err => {\n logger.error({ err }, 'Failed to initialize storage');\n });\n }\n\n this.startGarbageCollection();\n this.startHeartbeatCheck();\n }\n\n /** Wait for server to be fully ready (ports assigned) */\n public ready(): Promise<void> {\n return this._readyPromise;\n }\n\n /**\n * Wait for all pending batch operations to complete.\n * Useful for tests that need to verify state after OP_BATCH.\n */\n public async waitForPendingBatches(): Promise<void> {\n if (this.pendingBatchOperations.size === 0) return;\n await Promise.all(this.pendingBatchOperations);\n }\n\n /** Get the actual port the server is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n /** Get the actual cluster port */\n public get clusterPort(): number {\n return this._actualClusterPort;\n }\n\n /** Get event executor metrics for monitoring */\n public getEventExecutorMetrics() {\n return this.eventExecutor.getMetrics();\n }\n\n /** Get total event executor metrics across all stripes */\n public getEventExecutorTotalMetrics() {\n return this.eventExecutor.getTotalMetrics();\n }\n\n /** Get connection rate limiter stats for monitoring */\n public getRateLimiterStats() {\n return this.rateLimiter.getStats();\n }\n\n /** Get worker pool stats for monitoring */\n public getWorkerPoolStats() {\n return this.workerPool?.getStats() ?? null;\n }\n\n /** Check if worker pool is enabled */\n public get workerPoolEnabled(): boolean {\n return !!this.workerPool;\n }\n\n /** Get MerkleWorker for external use (null if worker pool disabled) */\n public getMerkleWorker(): MerkleWorker | null {\n return this.merkleWorker ?? null;\n }\n\n /** Get CRDTMergeWorker for external use (null if worker pool disabled) */\n public getCRDTMergeWorker(): CRDTMergeWorker | null {\n return this.crdtMergeWorker ?? null;\n }\n\n /** Get SerializationWorker for external use (null if worker pool disabled) */\n public getSerializationWorker(): SerializationWorker | null {\n return this.serializationWorker ?? null;\n }\n\n /** Get memory pool stats for monitoring GC pressure reduction */\n public getMemoryPoolStats() {\n return {\n eventPayloadPool: this.eventPayloadPool.getStats(),\n };\n }\n\n /** Get tasklet scheduler stats for monitoring cooperative multitasking */\n public getTaskletSchedulerStats() {\n return this.taskletScheduler.getStats();\n }\n\n /** Get tasklet scheduler for scheduling long-running operations */\n public getTaskletScheduler(): TaskletScheduler {\n return this.taskletScheduler;\n }\n\n public async shutdown() {\n logger.info('Shutting down Server Coordinator...');\n\n // 1. Stop accepting new connections\n this.httpServer.close();\n if (this.metricsServer) {\n this.metricsServer.close();\n }\n this.metricsService.destroy();\n this.wss.close();\n\n // 2. Notify and Close Clients\n logger.info(`Closing ${this.clients.size} client connections...`);\n const shutdownMsg = serialize({ type: 'SHUTDOWN_PENDING', retryAfter: 5000 });\n\n for (const client of this.clients.values()) {\n try {\n if (client.socket.readyState === WebSocket.OPEN) {\n // Send shutdown message directly to socket (bypass batching)\n // This ensures message is sent before socket.close()\n client.socket.send(shutdownMsg);\n if (client.writer) {\n client.writer.close();\n }\n client.socket.close(1001, 'Server Shutdown');\n }\n } catch (e) {\n logger.error({ err: e, clientId: client.id }, 'Error closing client connection');\n }\n }\n this.clients.clear();\n\n // 3. Shutdown event executor (wait for pending tasks)\n logger.info('Shutting down event executor...');\n await this.eventExecutor.shutdown(true);\n\n // 3.5. Shutdown worker pool\n if (this.workerPool) {\n logger.info('Shutting down worker pool...');\n await this.workerPool.shutdown(5000);\n logger.info('Worker pool shutdown complete.');\n }\n\n // 4. Close ReplicationPipeline\n if (this.replicationPipeline) {\n this.replicationPipeline.close();\n }\n\n // 5. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 6. Close Storage\n if (this.storage) {\n logger.info('Closing storage connection...');\n try {\n await this.storage.close();\n logger.info('Storage closed successfully.');\n } catch (err) {\n logger.error({ err }, 'Error closing storage');\n }\n }\n\n // 6. Cleanup\n if (this.gcInterval) {\n clearInterval(this.gcInterval);\n this.gcInterval = undefined;\n }\n\n if (this.heartbeatCheckInterval) {\n clearInterval(this.heartbeatCheckInterval);\n this.heartbeatCheckInterval = undefined;\n }\n\n // Stop LockManager\n if (this.lockManager) {\n this.lockManager.stop();\n }\n\n // Stop SystemManager\n if (this.systemManager) {\n this.systemManager.stop();\n }\n\n // Clear memory pools\n this.eventPayloadPool.clear();\n\n // Shutdown tasklet scheduler\n this.taskletScheduler.shutdown();\n\n // Shutdown Write Concern manager (Phase 5.01)\n this.writeAckManager.shutdown();\n\n logger.info('Server Coordinator shutdown complete.');\n }\n\n private async handleConnection(ws: WebSocket) {\n // Check rate limit before accepting connection\n if (this.rateLimitingEnabled && !this.rateLimiter.shouldAccept()) {\n logger.warn('Connection rate limit exceeded, rejecting');\n this.rateLimiter.onConnectionRejected();\n this.metricsService.incConnectionsRejected();\n ws.close(1013, 'Server overloaded'); // 1013 = Try Again Later\n return;\n }\n\n // Register connection attempt\n if (this.rateLimitingEnabled) {\n this.rateLimiter.onConnectionAttempt();\n }\n this.metricsService.incConnectionsAccepted();\n\n // Client ID is temporary until auth\n const clientId = crypto.randomUUID();\n logger.info({ clientId }, 'Client connected (pending auth)');\n\n // Create CoalescingWriter if enabled, otherwise create a pass-through writer\n const writer = new CoalescingWriter(ws, this.writeCoalescingEnabled ? this.writeCoalescingOptions : {\n maxBatchSize: 1, // Disable batching by flushing immediately\n maxDelayMs: 0,\n maxBatchBytes: 0,\n });\n\n const connection: ClientConnection = {\n id: clientId,\n socket: ws,\n writer,\n isAuthenticated: false,\n subscriptions: new Set(),\n lastActiveHlc: this.hlc.now(), // Initialize with current time\n lastPingReceived: Date.now(), // Initialize heartbeat tracking\n };\n this.clients.set(clientId, connection);\n this.metricsService.setConnectedClients(this.clients.size);\n\n // Run onConnection interceptors\n try {\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onConnection) {\n await interceptor.onConnection(context);\n }\n }\n } catch (err) {\n logger.error({ clientId, err }, 'Interceptor rejected connection');\n ws.close(4000, 'Connection Rejected');\n this.clients.delete(clientId);\n return;\n }\n\n ws.on('message', (message) => {\n try {\n let data: any;\n let buf: Uint8Array;\n\n if (Buffer.isBuffer(message)) {\n buf = message;\n } else if (message instanceof ArrayBuffer) {\n buf = new Uint8Array(message);\n } else if (Array.isArray(message)) {\n buf = Buffer.concat(message);\n } else {\n // Fallback or unexpected type\n buf = Buffer.from(message as any);\n }\n\n try {\n data = deserialize(buf);\n } catch (e) {\n // If msgpack fails, try JSON (legacy support)\n try {\n // Use Buffer.toString() or TextDecoder\n const text = Buffer.isBuffer(buf) ? buf.toString() : new TextDecoder().decode(buf);\n data = JSON.parse(text);\n } catch (jsonErr) {\n // Original error likely relevant\n throw e;\n }\n }\n\n this.handleMessage(connection, data);\n } catch (err) {\n logger.error({ err }, 'Invalid message format');\n ws.close(1002, 'Protocol Error');\n }\n });\n\n ws.on('close', () => {\n logger.info({ clientId }, 'Client disconnected');\n\n // If connection was still pending (not authenticated), mark as failed\n if (this.rateLimitingEnabled && !connection.isAuthenticated) {\n this.rateLimiter.onPendingConnectionFailed();\n }\n\n // Close the CoalescingWriter to flush any pending messages\n connection.writer.close();\n\n // Run onDisconnect interceptors\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onDisconnect) {\n interceptor.onDisconnect(context).catch(err => {\n logger.error({ clientId, err }, 'Error in onDisconnect interceptor');\n });\n }\n }\n\n // Cleanup subscriptions\n for (const subId of connection.subscriptions) {\n this.queryRegistry.unregister(subId);\n }\n\n // Cleanup Locks (Local)\n this.lockManager.handleClientDisconnect(clientId);\n\n // Cleanup Topics (Local)\n this.topicManager.unsubscribeAll(clientId);\n\n // Notify Cluster to Cleanup Locks (Remote)\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_CLIENT_DISCONNECTED', {\n originNodeId: this.cluster.config.nodeId,\n clientId\n });\n }\n }\n\n this.clients.delete(clientId);\n this.metricsService.setConnectedClients(this.clients.size);\n });\n\n // Send Auth Challenge immediately\n ws.send(serialize({ type: 'AUTH_REQUIRED' }));\n }\n\n private async handleMessage(client: ClientConnection, rawMessage: any) {\n // Validation with Zod\n const parseResult = MessageSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n logger.error({ clientId: client.id, error: parseResult.error }, 'Invalid message format from client');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: 'Invalid message format', details: (parseResult.error as any).errors }\n }, true); // urgent\n return;\n }\n const message = parseResult.data;\n\n // Handle PING immediately (even before auth check for authenticated clients)\n if (message.type === 'PING') {\n this.handlePing(client, message.timestamp);\n return;\n }\n\n // Update client's last active HLC\n // Try to extract from payload if present, otherwise assume near current time but logically before next op\n this.updateClientHlc(client, message);\n\n // Handshake / Auth handling\n if (!client.isAuthenticated) {\n if (message.type === 'AUTH') {\n const token = message.token;\n try {\n // Verify JWT - support both HS256 (symmetric) and RS256 (asymmetric/Clerk)\n const isRSAKey = this.jwtSecret.includes('-----BEGIN');\n const verifyOptions: jwt.VerifyOptions = isRSAKey\n ? { algorithms: ['RS256'] }\n : { algorithms: ['HS256'] };\n const decoded = jwt.verify(token, this.jwtSecret, verifyOptions) as any;\n // Ensure roles exist\n if (!decoded.roles) {\n decoded.roles = ['USER']; // Default role\n }\n // Ensure userId exists (map from sub if needed)\n if (!decoded.userId && decoded.sub) {\n decoded.userId = decoded.sub;\n }\n\n client.principal = decoded;\n client.isAuthenticated = true;\n logger.info({ clientId: client.id, user: client.principal!.userId || 'anon' }, 'Client authenticated');\n\n // Mark connection as established (handshake complete)\n if (this.rateLimitingEnabled) {\n this.rateLimiter.onConnectionEstablished();\n }\n\n client.writer.write({ type: 'AUTH_ACK' }, true); // urgent: bypass batching\n return; // Stop processing this message\n } catch (e) {\n logger.error({ clientId: client.id, err: e }, 'Auth failed');\n client.writer.write({ type: 'AUTH_FAIL', error: 'Invalid token' }, true); // urgent\n client.socket.close(4001, 'Unauthorized');\n }\n } else {\n // Reject any other message before auth\n client.socket.close(4001, 'Auth required');\n }\n return;\n }\n\n // Standard Protocol Handling (Authenticated)\n switch (message.type) {\n case 'QUERY_SUB': {\n const { queryId, mapName, query } = message.payload;\n\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName }, 'Access Denied: QUERY_SUB');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${mapName}` }\n }, true);\n return;\n }\n\n logger.info({ clientId: client.id, mapName, query }, 'Client subscribed');\n this.metricsService.incOp('SUBSCRIBE', mapName);\n\n // Identify all relevant nodes\n const allMembers = this.cluster.getMembers();\n const remoteMembers = allMembers.filter(id => !this.cluster.isLocal(id));\n\n const requestId = crypto.randomUUID();\n\n const pending: PendingClusterQuery = {\n requestId,\n client,\n queryId,\n mapName,\n query,\n results: [], // Will populate with local results first\n expectedNodes: new Set(remoteMembers),\n respondedNodes: new Set(),\n timer: setTimeout(() => this.finalizeClusterQuery(requestId, true), 5000) // 5s timeout\n };\n\n this.pendingClusterQueries.set(requestId, pending);\n\n // Execute Locally (async - wait for map to load from storage)\n // [FIX] Using await ensures handleMessage completes only after query execution\n // This is important for:\n // 1. Tests that need to verify results immediately after handleMessage\n // 2. Ensuring storage is loaded before returning results\n try {\n const localResults = await this.executeLocalQuery(mapName, query);\n pending.results.push(...localResults);\n\n // Scatter: Send to other nodes\n if (remoteMembers.length > 0) {\n for (const nodeId of remoteMembers) {\n this.cluster.send(nodeId, 'CLUSTER_QUERY_EXEC', {\n requestId,\n mapName,\n query\n });\n }\n } else {\n // Single node cluster: finalize immediately\n this.finalizeClusterQuery(requestId);\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to execute local query');\n // Finalize with empty results on error\n this.finalizeClusterQuery(requestId);\n }\n break;\n }\n\n case 'QUERY_UNSUB': {\n const { queryId: unsubId } = message.payload;\n this.queryRegistry.unregister(unsubId);\n client.subscriptions.delete(unsubId);\n break;\n }\n\n case 'CLIENT_OP': {\n const op = message.payload;\n\n // Determine action type\n // LWW: op.record.value === null -> REMOVE\n // OR: OR_REMOVE or OR_ADD -> PUT (effectively)\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n this.metricsService.incOp(isRemove ? 'DELETE' : 'PUT', op.mapName);\n\n // Check Permission\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied: Client OP');\n client.writer.write({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: 'Access Denied' }\n });\n return;\n }\n\n logger.info({ clientId: client.id, opType: op.opType, key: op.key, mapName: op.mapName }, 'Received op');\n\n if (this.partitionService.isLocalOwner(op.key)) {\n this.processLocalOp(op, false, client.id).catch(err => {\n logger.error({ clientId: client.id, err }, 'Op failed');\n client.writer.write({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: err.message || 'Internal Error' }\n });\n });\n } else {\n const owner = this.partitionService.getOwner(op.key);\n logger.info({ key: op.key, owner }, 'Forwarding op');\n this.cluster.sendToNode(owner, op);\n }\n break;\n }\n\n case 'OP_BATCH': {\n const ops = message.payload.ops;\n // Extract batch-level Write Concern (Phase 5.01)\n const batchWriteConcern = (message.payload as any).writeConcern as WriteConcernValue | undefined;\n const batchTimeout = (message.payload as any).timeout as number | undefined;\n\n logger.info({ clientId: client.id, count: ops.length, writeConcern: batchWriteConcern }, 'Received batch');\n\n // === OPTIMIZATION 1: Early ACK ===\n // Fast validation pass - check permissions without processing\n const validOps: typeof ops = [];\n let rejectedCount = 0;\n let lastValidId: string | null = null;\n\n // Categorize ops by Write Concern for different ACK handling\n const memoryOps: typeof ops = []; // Ops that need immediate ACK (MEMORY or FIRE_AND_FORGET)\n const deferredOps: typeof ops = []; // Ops that need deferred ACK (APPLIED, REPLICATED, PERSISTED)\n\n for (const op of ops) {\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n rejectedCount++;\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied (Batch)');\n continue;\n }\n\n validOps.push(op);\n if (op.id) {\n lastValidId = op.id;\n }\n\n // Determine effective Write Concern for this operation\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n\n // Categorize by Write Concern level\n if (effectiveWriteConcern === 'FIRE_AND_FORGET' || effectiveWriteConcern === 'MEMORY' || !effectiveWriteConcern) {\n memoryOps.push(op);\n } else {\n deferredOps.push(op);\n }\n }\n\n // Send Early ACK for MEMORY/FIRE_AND_FORGET ops (backwards compatible)\n if (memoryOps.length > 0) {\n const lastMemoryId = memoryOps[memoryOps.length - 1].id;\n if (lastMemoryId) {\n client.writer.write({\n type: 'OP_ACK',\n payload: {\n lastId: lastMemoryId,\n achievedLevel: 'MEMORY'\n }\n });\n }\n }\n\n // Send rejection error if any ops were denied\n if (rejectedCount > 0) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Partial batch failure: ${rejectedCount} ops denied` }\n }, true);\n }\n\n // Register deferred ops with WriteAckManager for tracking\n for (const op of deferredOps) {\n if (op.id) {\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n const effectiveTimeout = op.timeout ?? batchTimeout;\n const wcLevel = this.stringToWriteConcern(effectiveWriteConcern);\n\n // Register and handle the promise\n this.writeAckManager.registerPending(op.id, wcLevel, effectiveTimeout)\n .then((result) => {\n // Send ACK when Write Concern is achieved\n client.writer.write({\n type: 'OP_ACK',\n payload: {\n lastId: op.id!,\n achievedLevel: result.achievedLevel,\n results: [{\n opId: op.id!,\n success: result.success,\n achievedLevel: result.achievedLevel,\n error: result.error\n }]\n }\n });\n })\n .catch((err) => {\n logger.error({ opId: op.id, err }, 'Write concern tracking failed');\n });\n }\n }\n\n // Process valid ops asynchronously (non-blocking)\n if (validOps.length > 0) {\n const batchPromise = new Promise<void>((resolve) => {\n setImmediate(() => {\n this.processBatchAsyncWithWriteConcern(validOps, client.id, batchWriteConcern, batchTimeout)\n .catch(err => {\n logger.error({ clientId: client.id, err }, 'Batch processing failed');\n })\n .finally(() => {\n this.pendingBatchOperations.delete(batchPromise);\n resolve();\n });\n });\n });\n this.pendingBatchOperations.add(batchPromise);\n }\n break;\n }\n\n case 'SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName: message.mapName }, 'Access Denied: SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }, true);\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'Client too old, sending SYNC_RESET_REQUIRED');\n client.writer.write({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n });\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested sync');\n this.metricsService.incOp('GET', message.mapName);\n\n // [FIX] Wait for map to be fully loaded from storage before sending rootHash\n // This prevents sending rootHash=0 for maps that are still loading from PostgreSQL\n try {\n const mapForSync = await this.getMapAsync(message.mapName);\n if (mapForSync instanceof LWWMap) {\n // Use the incremental Merkle Tree from LWWMap\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.writer.write({\n type: 'SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n });\n } else {\n // ORMap sync not implemented via Merkle Tree yet\n logger.warn({ mapName: message.mapName }, 'SYNC_INIT requested for ORMap - Not Implemented');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 501, message: `Merkle Sync not supported for ORMap ${message.mapName}` }\n }, true);\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }, true);\n }\n break;\n }\n\n case 'MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName, path } = message.payload;\n\n // [FIX] Wait for map to be fully loaded before accessing Merkle tree\n try {\n const mapForBucket = await this.getMapAsync(mapName);\n if (mapForBucket instanceof LWWMap) {\n const treeForBucket = mapForBucket.getMerkleTree();\n const buckets = treeForBucket.getBuckets(path);\n const node = treeForBucket.getNode(path);\n if (node && node.entries && node.entries.size > 0) {\n const diffRecords = [];\n for (const key of node.entries.keys()) {\n diffRecords.push({ key, record: mapForBucket.getRecord(key) });\n }\n client.writer.write({\n type: 'SYNC_RESP_LEAF',\n payload: { mapName, path, records: diffRecords }\n });\n } else {\n client.writer.write({\n type: 'SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n });\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'LOCK_REQUEST': {\n const { requestId, name, ttl } = message.payload;\n\n // 1. Access Control\n // Define a convention: lock names are resources.\n // Check if user has 'WRITE' permission on \"locks\" map or specific lock name.\n // Since locks are ephemeral, we might treat them as a special resource \"sys:locks\".\n // Or just check against the lock name itself.\n // Let's use `sys:lock:${name}` pattern or just `${name}`.\n // If we use just name, it might conflict with map names if policies are strict.\n // Assuming for now that lock name represents the resource being protected.\n if (!this.securityManager.checkPermission(client.principal!, name, 'PUT')) {\n client.writer.write({\n // We don't have LOCK_DENIED type in schema yet?\n // Using LOCK_RELEASED with success=false as a hack or ERROR.\n // Ideally ERROR.\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for lock ${name}` }\n }, true);\n return;\n }\n\n if (this.partitionService.isLocalOwner(name)) {\n const result = this.lockManager.acquire(name, client.id, requestId, ttl || 10000);\n if (result.granted) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken: result.fencingToken }\n });\n }\n // If not granted, it is queued. Response sent later via event.\n } else {\n const owner = this.partitionService.getOwner(name);\n // 2. Cluster Reliability Check\n if (!this.cluster.getMembers().includes(owner)) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 503, message: `Lock owner ${owner} is unavailable` }\n }, true);\n return;\n }\n\n this.cluster.send(owner, 'CLUSTER_LOCK_REQ', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n ttl\n });\n }\n break;\n }\n\n case 'LOCK_RELEASE': {\n const { requestId, name, fencingToken } = message.payload;\n\n if (this.partitionService.isLocalOwner(name)) {\n const success = this.lockManager.release(name, client.id, fencingToken);\n client.writer.write({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n });\n } else {\n const owner = this.partitionService.getOwner(name);\n this.cluster.send(owner, 'CLUSTER_LOCK_RELEASE', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n fencingToken\n });\n }\n break;\n }\n\n case 'TOPIC_SUB': {\n const { topic } = message.payload;\n\n // C1: Access Control\n // We treat topics as resources.\n // Policy check: action 'READ' on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'READ')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_SUB');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }, true);\n return;\n }\n\n try {\n this.topicManager.subscribe(client.id, topic);\n } catch (e: any) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }, true);\n }\n break;\n }\n\n case 'TOPIC_UNSUB': {\n const { topic } = message.payload;\n this.topicManager.unsubscribe(client.id, topic);\n break;\n }\n\n case 'TOPIC_PUB': {\n const { topic, data } = message.payload;\n\n // C1: Access Control\n // Policy check: action 'PUT' (publish) on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'PUT')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_PUB');\n // No error sent back? Fire and forget usually implies silent drop or async error.\n // But for security violations, an error is useful during dev.\n // Spec says fire-and-forget delivery, but security rejection should ideally notify.\n // Let's send error.\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }, true);\n return;\n }\n\n try {\n this.topicManager.publish(topic, data, client.id);\n } catch (e: any) {\n // Invalid topic name etc\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }, true);\n }\n break;\n }\n\n // ============ Phase 4: Partition Map Request Handler ============\n\n case 'PARTITION_MAP_REQUEST': {\n // Client is requesting the current partition map\n // This is used for cluster-aware routing\n const clientVersion = message.payload?.currentVersion ?? 0;\n const currentMap = this.partitionService.getPartitionMap();\n\n // Only send if client has stale version or no version\n if (clientVersion < currentMap.version) {\n client.writer.write({\n type: 'PARTITION_MAP',\n payload: currentMap\n });\n logger.debug({\n clientId: client.id,\n clientVersion,\n serverVersion: currentMap.version\n }, 'Sent partition map to client');\n }\n break;\n }\n\n // ============ ORMap Sync Message Handlers ============\n\n case 'ORMAP_SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }, true);\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'ORMap client too old, sending SYNC_RESET_REQUIRED');\n client.writer.write({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n });\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested ORMap sync');\n this.metricsService.incOp('GET', message.mapName);\n\n try {\n const mapForSync = await this.getMapAsync(message.mapName, 'OR');\n if (mapForSync instanceof ORMap) {\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n });\n } else {\n // It's actually an LWWMap, client should use SYNC_INIT\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: `Map ${message.mapName} is not an ORMap` }\n }, true);\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for ORMAP_SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }, true);\n }\n break;\n }\n\n case 'ORMAP_MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName, path } = message.payload;\n\n try {\n const mapForBucket = await this.getMapAsync(mapName, 'OR');\n if (mapForBucket instanceof ORMap) {\n const tree = mapForBucket.getMerkleTree();\n const buckets = tree.getBuckets(path);\n const isLeaf = tree.isLeaf(path);\n\n if (isLeaf) {\n // This is a leaf node - send actual records\n const keys = tree.getKeysInBucket(path);\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n\n for (const key of keys) {\n const recordsMap = mapForBucket.getRecordsMap(key);\n if (recordsMap && recordsMap.size > 0) {\n entries.push({\n key,\n records: Array.from(recordsMap.values()),\n tombstones: mapForBucket.getTombstones()\n });\n }\n }\n\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_LEAF',\n payload: { mapName, path, entries }\n });\n } else {\n // Not a leaf - send bucket hashes\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n });\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for ORMAP_MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'ORMAP_DIFF_REQUEST': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName: diffMapName, keys } = message.payload;\n\n try {\n const mapForDiff = await this.getMapAsync(diffMapName, 'OR');\n if (mapForDiff instanceof ORMap) {\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n const allTombstones = mapForDiff.getTombstones();\n\n for (const key of keys) {\n const recordsMap = mapForDiff.getRecordsMap(key);\n entries.push({\n key,\n records: recordsMap ? Array.from(recordsMap.values()) : [],\n tombstones: allTombstones\n });\n }\n\n client.writer.write({\n type: 'ORMAP_DIFF_RESPONSE',\n payload: { mapName: diffMapName, entries }\n });\n }\n } catch (err) {\n logger.error({ err, mapName: diffMapName }, 'Failed to load map for ORMAP_DIFF_REQUEST');\n }\n break;\n }\n\n case 'ORMAP_PUSH_DIFF': {\n // Check WRITE permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'PUT')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName: pushMapName, entries: pushEntries } = message.payload;\n\n try {\n const mapForPush = await this.getMapAsync(pushMapName, 'OR');\n if (mapForPush instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of pushEntries) {\n const { key, records, tombstones } = entry;\n const result = mapForPush.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName: pushMapName, added: totalAdded, updated: totalUpdated, clientId: client.id }, 'Merged ORMap diff from client');\n\n // Broadcast changes to other clients\n for (const entry of pushEntries) {\n for (const record of entry.records) {\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: {\n mapName: pushMapName,\n eventType: 'OR_ADD',\n key: entry.key,\n orRecord: record\n }\n }, client.id);\n }\n }\n\n // Persist to storage\n if (this.storage) {\n for (const entry of pushEntries) {\n const recordsMap = mapForPush.getRecordsMap(entry.key);\n if (recordsMap && recordsMap.size > 0) {\n await this.storage.store(pushMapName, entry.key, {\n type: 'OR',\n records: Array.from(recordsMap.values())\n });\n }\n }\n }\n }\n }\n } catch (err) {\n logger.error({ err, mapName: pushMapName }, 'Failed to process ORMAP_PUSH_DIFF');\n }\n break;\n }\n\n default:\n logger.warn({ type: message.type }, 'Unknown message type');\n }\n }\n\n private updateClientHlc(client: ClientConnection, message: any) {\n // Try to extract timestamp from message if available\n // This is heuristic based on typical message structure\n let ts: Timestamp | undefined;\n\n if (message.type === 'CLIENT_OP') {\n const op = message.payload;\n if (op.record && op.record.timestamp) {\n ts = op.record.timestamp;\n } else if (op.orRecord && op.orRecord.timestamp) {\n // orRecord usually has entries which have timestamps, or value itself is decorated?\n // Depends on implementation.\n } else if (op.orTag) {\n try {\n ts = HLC.parse(op.orTag);\n } catch (e) { }\n }\n }\n\n if (ts) {\n // Client sent an explicit timestamp, update their HLC\n this.hlc.update(ts); // Also update server clock\n // Client HLC is at least this\n client.lastActiveHlc = ts;\n } else {\n // Just bump to current server time if no explicit TS\n // This assumes client is \"alive\" at this moment.\n client.lastActiveHlc = this.hlc.now();\n }\n }\n\n // ============ Phase 4: Partition Map Broadcast ============\n\n /**\n * Broadcast partition map to all connected and authenticated clients.\n * Called when partition topology changes (node join/leave/failover).\n */\n private broadcastPartitionMap(partitionMap: any): void {\n const message = {\n type: 'PARTITION_MAP',\n payload: partitionMap\n };\n\n let broadcastCount = 0;\n for (const client of this.clients.values()) {\n if (client.isAuthenticated && client.socket.readyState === WebSocket.OPEN) {\n client.writer.write(message);\n broadcastCount++;\n }\n }\n\n logger.info({\n version: partitionMap.version,\n clientCount: broadcastCount\n }, 'Broadcast partition map to clients');\n }\n\n private broadcast(message: any, excludeClientId?: string) {\n const isServerEvent = message.type === 'SERVER_EVENT';\n\n if (isServerEvent) {\n const payload = message.payload;\n const mapName = payload.mapName;\n\n // === SUBSCRIPTION-BASED ROUTING ===\n // Only send to clients that have active subscriptions for this map\n const subscribedClientIds = this.queryRegistry.getSubscribedClientIds(mapName);\n\n // Track metrics\n this.metricsService.incEventsRouted();\n\n if (subscribedClientIds.size === 0) {\n // Early exit - no subscribers for this map!\n this.metricsService.incEventsFilteredBySubscription();\n return;\n }\n\n // Track average subscribers per event\n this.metricsService.recordSubscribersPerEvent(subscribedClientIds.size);\n\n // Send only to subscribed clients with FLS filtering\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n // Shallow clone payload for FLS filtering\n const newPayload = { ...payload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(newPayload.record.value, client.principal, mapName);\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(newPayload.orRecord.value, client.principal, mapName);\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n client.writer.write({ ...message, payload: newPayload });\n }\n } else {\n // Non-event messages (GC_PRUNE, SHUTDOWN_PENDING) still go to all clients\n const msgData = serialize(message);\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1) { // 1 = OPEN\n client.writer.writeRaw(msgData);\n }\n }\n }\n }\n\n /**\n * === OPTIMIZATION 2 & 3: Batched Broadcast with Serialization Caching ===\n * Groups clients by their permission roles and serializes once per group.\n * Also batches multiple events into a single SERVER_BATCH_EVENT message.\n * === OPTIMIZATION 4: Subscription-based Routing ===\n * Only sends events to clients with active subscriptions for affected maps.\n */\n private broadcastBatch(events: any[], excludeClientId?: string): void {\n if (events.length === 0) return;\n\n // === SUBSCRIPTION-BASED ROUTING ===\n // Get unique map names from events\n const affectedMaps = new Set<string>();\n for (const event of events) {\n if (event.mapName) {\n affectedMaps.add(event.mapName);\n }\n }\n\n // Get all subscribed client IDs across all affected maps\n const subscribedClientIds = new Set<string>();\n for (const mapName of affectedMaps) {\n const mapSubscribers = this.queryRegistry.getSubscribedClientIds(mapName);\n for (const clientId of mapSubscribers) {\n subscribedClientIds.add(clientId);\n }\n }\n\n // Track metrics\n this.metricsService.incEventsRouted();\n\n if (subscribedClientIds.size === 0) {\n // Early exit - no subscribers for any of the affected maps!\n this.metricsService.incEventsFilteredBySubscription();\n return;\n }\n\n this.metricsService.recordSubscribersPerEvent(subscribedClientIds.size);\n\n // Group subscribed clients by their role signature for serialization caching\n const clientsByRoleSignature = new Map<string, ClientConnection[]>();\n\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n // Create a role signature for grouping (sorted roles joined)\n const roleSignature = (client.principal.roles || ['USER']).sort().join(',');\n\n if (!clientsByRoleSignature.has(roleSignature)) {\n clientsByRoleSignature.set(roleSignature, []);\n }\n clientsByRoleSignature.get(roleSignature)!.push(client);\n }\n\n // For each role group, filter events once and serialize once\n for (const [, clients] of clientsByRoleSignature) {\n if (clients.length === 0) continue;\n\n // Use first client as representative for filtering (same roles = same permissions)\n const representativeClient = clients[0];\n\n // Filter all events for this role group\n const filteredEvents = events.map(eventPayload => {\n const mapName = eventPayload.mapName;\n const newPayload = { ...eventPayload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(\n newPayload.record.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(\n newPayload.orRecord.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n return newPayload;\n });\n\n // Serialize ONCE for this entire group\n const batchMessage = {\n type: 'SERVER_BATCH_EVENT',\n payload: { events: filteredEvents },\n timestamp: this.hlc.now()\n };\n const serializedBatch = serialize(batchMessage);\n\n // Send to all clients in this role group\n for (const client of clients) {\n try {\n client.writer.writeRaw(serializedBatch);\n } catch (err) {\n logger.error({ clientId: client.id, err }, 'Failed to send batch to client');\n }\n }\n }\n }\n\n /**\n * Helper method to get role signature for a client (for caching key)\n */\n private getClientRoleSignature(client: ClientConnection): string {\n if (!client.principal || !client.principal.roles) {\n return 'USER';\n }\n return client.principal.roles.sort().join(',');\n }\n\n /**\n * === BACKPRESSURE: Synchronous Broadcast ===\n * Same as broadcastBatch but waits for all sends to complete.\n * Used when backpressure forces sync processing to drain the pipeline.\n */\n private async broadcastBatchSync(events: any[], excludeClientId?: string): Promise<void> {\n if (events.length === 0) return;\n\n // Get unique map names from events\n const affectedMaps = new Set<string>();\n for (const event of events) {\n if (event.mapName) {\n affectedMaps.add(event.mapName);\n }\n }\n\n // Get all subscribed client IDs across all affected maps\n const subscribedClientIds = new Set<string>();\n for (const mapName of affectedMaps) {\n const mapSubscribers = this.queryRegistry.getSubscribedClientIds(mapName);\n for (const clientId of mapSubscribers) {\n subscribedClientIds.add(clientId);\n }\n }\n\n if (subscribedClientIds.size === 0) {\n return;\n }\n\n // Group subscribed clients by their role signature\n const clientsByRoleSignature = new Map<string, ClientConnection[]>();\n\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n const roleSignature = (client.principal.roles || ['USER']).sort().join(',');\n\n if (!clientsByRoleSignature.has(roleSignature)) {\n clientsByRoleSignature.set(roleSignature, []);\n }\n clientsByRoleSignature.get(roleSignature)!.push(client);\n }\n\n // Collect all send promises\n const sendPromises: Promise<void>[] = [];\n\n for (const [, clients] of clientsByRoleSignature) {\n if (clients.length === 0) continue;\n\n const representativeClient = clients[0];\n\n // Filter all events for this role group\n const filteredEvents = events.map(eventPayload => {\n const mapName = eventPayload.mapName;\n const newPayload = { ...eventPayload };\n\n if (newPayload.record) {\n const newVal = this.securityManager.filterObject(\n newPayload.record.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) {\n const newVal = this.securityManager.filterObject(\n newPayload.orRecord.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n return newPayload;\n });\n\n const batchMessage = {\n type: 'SERVER_BATCH_EVENT',\n payload: { events: filteredEvents },\n timestamp: this.hlc.now()\n };\n const serializedBatch = serialize(batchMessage);\n\n // Send to all clients and collect promises\n for (const client of clients) {\n sendPromises.push(new Promise<void>((resolve, reject) => {\n try {\n client.socket.send(serializedBatch, (err) => {\n if (err) {\n logger.error({ clientId: client.id, err }, 'Failed to send sync batch to client');\n reject(err);\n } else {\n resolve();\n }\n });\n } catch (err) {\n logger.error({ clientId: client.id, err }, 'Exception sending sync batch to client');\n reject(err);\n }\n }));\n }\n }\n\n // Wait for all sends to complete (ignore individual failures)\n await Promise.allSettled(sendPromises);\n }\n\n private setupClusterListeners() {\n this.cluster.on('memberJoined', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n this.cluster.on('memberLeft', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n\n this.cluster.on('message', (msg) => {\n switch (msg.type) {\n case 'OP_FORWARD':\n logger.info({ senderId: msg.senderId }, 'Received forwarded op');\n if (this.partitionService.isLocalOwner(msg.payload.key)) {\n this.processLocalOp(msg.payload, true, msg.senderId).catch(err => {\n logger.error({ err, senderId: msg.senderId }, 'Forwarded op failed');\n });\n } else {\n logger.warn({ key: msg.payload.key }, 'Received OP_FORWARD but not owner. Dropping.');\n }\n break;\n case 'CLUSTER_EVENT':\n this.handleClusterEvent(msg.payload);\n break;\n\n case 'CLUSTER_QUERY_EXEC': {\n const { requestId, mapName, query } = msg.payload;\n this.executeLocalQuery(mapName, query).then(results => {\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results\n });\n }).catch(err => {\n logger.error({ err, mapName }, 'Failed to execute cluster query');\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results: []\n });\n });\n break;\n }\n\n case 'CLUSTER_QUERY_RESP': {\n const { requestId: reqId, results: remoteResults } = msg.payload;\n const pendingQuery = this.pendingClusterQueries.get(reqId);\n if (pendingQuery) {\n pendingQuery.results.push(...remoteResults);\n pendingQuery.respondedNodes.add(msg.senderId);\n\n if (pendingQuery.respondedNodes.size === pendingQuery.expectedNodes.size) {\n this.finalizeClusterQuery(reqId);\n }\n }\n break;\n }\n\n case 'CLUSTER_GC_REPORT': {\n this.handleGcReport(msg.senderId, msg.payload.minHlc);\n break;\n }\n\n case 'CLUSTER_GC_COMMIT': {\n this.performGarbageCollection(msg.payload.safeTimestamp);\n break;\n }\n\n case 'CLUSTER_LOCK_REQ': {\n const { originNodeId, clientId, requestId, name, ttl } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const result = this.lockManager.acquire(name, compositeId, requestId, ttl || 10000);\n if (result.granted) {\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId,\n requestId,\n name,\n fencingToken: result.fencingToken\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASE': {\n const { originNodeId, clientId, requestId, name, fencingToken } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const success = this.lockManager.release(name, compositeId, fencingToken);\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_RELEASED', {\n clientId, requestId, name, success\n });\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASED': {\n const { clientId, requestId, name, success } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_GRANTED': {\n const { clientId, requestId, name, fencingToken } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n });\n }\n break;\n }\n\n case 'CLUSTER_CLIENT_DISCONNECTED': {\n const { clientId, originNodeId } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n this.lockManager.handleClientDisconnect(compositeId);\n break;\n }\n\n case 'CLUSTER_TOPIC_PUB': {\n const { topic, data, originalSenderId } = msg.payload;\n this.topicManager.publish(topic, data, originalSenderId, true);\n break;\n }\n }\n });\n }\n\n private async executeLocalQuery(mapName: string, query: Query) {\n // Wait for map to be fully loaded from storage before querying\n const map = await this.getMapAsync(mapName);\n const records = new Map<string, any>();\n\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const rec = map.getRecord(key);\n if (rec && rec.value !== null) {\n records.set(key, rec);\n }\n }\n } else if (map instanceof ORMap) {\n // For ORMap, we flatten values. A key matches if ANY of its values match?\n // Or we expose the array of values?\n // For now, we expose { key, value: [v1, v2, ...] }\n // Accessing properties on array might fail depending on query.\n // Assuming user knows what they are querying.\n // Since ORMap doesn't have allKeys, we iterate internal structure?\n // ORMap doesn't expose keys iterator publicly in the class I read?\n // Wait, checking ORMap.ts...\n // It doesn't export keys()! It exports items: Map.\n // But items is private.\n // I need to add keys() to ORMap or use 'any' cast.\n // I will cast to any for now.\n const items = (map as any).items as Map<string, any>;\n for (const key of items.keys()) {\n const values = map.get(key);\n if (values.length > 0) {\n // We wrap in object matching LWWRecord structure roughly?\n // { value: values, timestamp: ... }\n // But timestamp differs per record.\n records.set(key, { value: values });\n }\n }\n }\n\n // Fix: Do not apply offset/limit locally for cluster queries.\n // They will be applied in finalizeClusterQuery after aggregation.\n const localQuery = { ...query };\n delete localQuery.offset;\n delete localQuery.limit;\n\n return executeQuery(records, localQuery);\n }\n\n private finalizeClusterQuery(requestId: string, timeout = false) {\n const pending = this.pendingClusterQueries.get(requestId);\n if (!pending) return;\n\n if (timeout) {\n logger.warn({ requestId, responded: pending.respondedNodes.size, expected: pending.expectedNodes.size }, 'Query timed out. Returning partial results.');\n }\n\n clearTimeout(pending.timer);\n this.pendingClusterQueries.delete(requestId);\n\n const { client, queryId, mapName, query, results } = pending;\n\n // Deduplicate results (if backups responded or multiple nodes have same key)\n const uniqueResults = new Map<string, any>();\n for (const res of results) {\n uniqueResults.set(res.key, res);\n }\n const finalResults = Array.from(uniqueResults.values());\n\n // Re-Apply Sort (Global)\n if (query.sort) {\n finalResults.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n // Handle ORMap array values vs LWW single values?\n // Assuming LWW for sort logic or array comparison.\n const valA = a.value[field];\n const valB = b.value[field];\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n const slicedResults = (query.offset || query.limit)\n ? finalResults.slice(query.offset || 0, (query.offset || 0) + (query.limit || finalResults.length))\n : finalResults;\n\n // Register Subscription\n const resultKeys = new Set(slicedResults.map(r => r.key));\n const sub: Subscription = {\n id: queryId,\n clientId: client.id,\n mapName,\n query,\n socket: client.socket,\n previousResultKeys: resultKeys,\n interestedFields: 'ALL'\n };\n\n this.queryRegistry.register(sub);\n client.subscriptions.add(queryId);\n\n // Apply Field Level Security\n const filteredResults = slicedResults.map(res => {\n const filteredValue = this.securityManager.filterObject(res.value, client.principal!, mapName);\n return { ...res, value: filteredValue };\n });\n\n client.writer.write({\n type: 'QUERY_RESP',\n payload: { queryId, results: filteredResults }\n });\n }\n\n /**\n * Core operation application logic shared between processLocalOp and processLocalOpForBatch.\n * Handles map merge, storage persistence, query evaluation, and event generation.\n *\n * @returns Event payload for broadcasting (or null if operation failed)\n */\n private applyOpToMap(op: any): { eventPayload: any; oldRecord: any } {\n // Determine type hint from op\n const typeHint = (op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE') ? 'OR' : 'LWW';\n const map = this.getMap(op.mapName, typeHint);\n\n // Check compatibility\n if (typeHint === 'OR' && map instanceof LWWMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: LWWMap but received OR op');\n throw new Error('Map type mismatch: LWWMap but received OR op');\n }\n if (typeHint === 'LWW' && map instanceof ORMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: ORMap but received LWW op');\n throw new Error('Map type mismatch: ORMap but received LWW op');\n }\n\n let oldRecord: any;\n let recordToStore: StorageValue<any> | undefined;\n let tombstonesToStore: StorageValue<any> | undefined;\n\n const eventPayload: any = {\n mapName: op.mapName,\n key: op.key,\n };\n\n if (map instanceof LWWMap) {\n oldRecord = map.getRecord(op.key);\n map.merge(op.key, op.record);\n recordToStore = op.record;\n eventPayload.eventType = 'UPDATED';\n eventPayload.record = op.record;\n } else if (map instanceof ORMap) {\n oldRecord = map.getRecords(op.key);\n\n if (op.opType === 'OR_ADD') {\n map.apply(op.key, op.orRecord);\n eventPayload.eventType = 'OR_ADD';\n eventPayload.orRecord = op.orRecord;\n recordToStore = { type: 'OR', records: map.getRecords(op.key) };\n } else if (op.opType === 'OR_REMOVE') {\n map.applyTombstone(op.orTag);\n eventPayload.eventType = 'OR_REMOVE';\n eventPayload.orTag = op.orTag;\n recordToStore = { type: 'OR', records: map.getRecords(op.key) };\n tombstonesToStore = { type: 'OR_TOMBSTONES', tags: map.getTombstones() };\n }\n }\n\n // Live Query Evaluation\n this.queryRegistry.processChange(op.mapName, map, op.key, op.record || op.orRecord, oldRecord);\n\n // Update metrics\n const mapSize = (map instanceof ORMap) ? map.totalRecords : map.size;\n this.metricsService.setMapSize(op.mapName, mapSize);\n\n // Persist to storage (async, don't wait)\n if (this.storage) {\n if (recordToStore) {\n this.storage.store(op.mapName, op.key, recordToStore).catch(err => {\n logger.error({ mapName: op.mapName, key: op.key, err }, 'Failed to persist op');\n });\n }\n if (tombstonesToStore) {\n this.storage.store(op.mapName, '__tombstones__', tombstonesToStore).catch(err => {\n logger.error({ mapName: op.mapName, err }, 'Failed to persist tombstones');\n });\n }\n }\n\n return { eventPayload, oldRecord };\n }\n\n /**\n * Broadcast event to cluster members (excluding self).\n */\n private broadcastToCluster(eventPayload: any): void {\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n /**\n * Apply replicated operation from another node (callback for ReplicationPipeline)\n * This is called when we receive a replicated operation as a backup node\n */\n private async applyReplicatedOperation(\n operation: unknown,\n opId: string,\n sourceNode: string\n ): Promise<boolean> {\n try {\n const op = operation as any;\n logger.debug({ sourceNode, opId, mapName: op.mapName, key: op.key }, 'Applying replicated operation');\n\n // Apply operation to local map (as backup)\n const { eventPayload } = this.applyOpToMap(op);\n\n // Broadcast event to local clients subscribed to this data\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n return true;\n } catch (error) {\n logger.error({ sourceNode, opId, error }, 'Failed to apply replicated operation');\n return false;\n }\n }\n\n /**\n * Build OpContext for interceptors.\n */\n private buildOpContext(clientId: string, fromCluster: boolean): OpContext {\n let context: OpContext = {\n clientId,\n isAuthenticated: false,\n fromCluster,\n originalSenderId: clientId\n };\n\n if (!fromCluster) {\n const client = this.clients.get(clientId);\n if (client) {\n context = {\n clientId: client.id,\n socket: client.socket,\n isAuthenticated: client.isAuthenticated,\n principal: client.principal,\n fromCluster,\n originalSenderId: clientId\n };\n }\n }\n\n return context;\n }\n\n /**\n * Run onBeforeOp interceptors. Returns modified op or null if dropped.\n */\n private async runBeforeInterceptors(op: any, context: OpContext): Promise<any | null> {\n let currentOp: ServerOp | null = op;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.onBeforeOp && currentOp) {\n currentOp = await interceptor.onBeforeOp(currentOp, context);\n if (!currentOp) {\n logger.debug({ interceptor: interceptor.name, opId: op.id }, 'Interceptor silently dropped op');\n return null;\n }\n }\n }\n\n return currentOp;\n }\n\n /**\n * Run onAfterOp interceptors (fire-and-forget).\n */\n private runAfterInterceptors(op: any, context: OpContext): void {\n for (const interceptor of this.interceptors) {\n if (interceptor.onAfterOp) {\n interceptor.onAfterOp(op, context).catch(err => {\n logger.error({ err }, 'Error in onAfterOp');\n });\n }\n }\n }\n\n private handleLockGranted({ clientId, requestId, name, fencingToken }: { clientId: string, requestId: string, name: string, fencingToken: number }) {\n // Check if local client\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n });\n return;\n }\n\n // Check if remote client (composite ID: \"nodeId:realClientId\")\n const parts = clientId.split(':');\n if (parts.length === 2) {\n const [nodeId, realClientId] = parts;\n // Verify nodeId is not self (loopback check, though split should handle it)\n if (nodeId !== this.cluster.config.nodeId) {\n this.cluster.send(nodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId: realClientId,\n requestId,\n name,\n fencingToken\n });\n return;\n }\n }\n\n logger.warn({ clientId, name }, 'Lock granted to unknown client');\n }\n\n private async processLocalOp(op: any, fromCluster: boolean, originalSenderId?: string) {\n // 1. Build context for interceptors\n const context = this.buildOpContext(originalSenderId || 'unknown', fromCluster);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) return; // Silently dropped by interceptor\n op = processedOp;\n } catch (err) {\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op');\n throw err;\n }\n\n // 3. Apply operation to map (shared logic)\n const { eventPayload } = this.applyOpToMap(op);\n\n // 4. Replicate to backup nodes (Hazelcast pattern: after local merge)\n // Note: Only replicate if we are the owner and operation is not from cluster\n // Incoming cluster ops are already replicated by the owner\n if (this.replicationPipeline && !fromCluster) {\n const opId = op.id || `${op.mapName}:${op.key}:${Date.now()}`;\n // Fire-and-forget for EVENTUAL, or await for STRONG/QUORUM\n this.replicationPipeline.replicate(op, opId, op.key).catch(err => {\n logger.warn({ opId, key: op.key, err }, 'Replication failed (non-fatal)');\n });\n }\n\n // 5. Broadcast EVENT to other clients\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 6. Broadcast to cluster (for subscribers on other nodes)\n // Note: This is different from replication - this notifies subscribers\n this.broadcastToCluster(eventPayload);\n\n // 7. Run onAfterOp interceptors\n this.runAfterInterceptors(op, context);\n }\n\n /**\n * === OPTIMIZATION 1: Async Batch Processing with Backpressure ===\n * Processes validated operations asynchronously after ACK has been sent.\n * Uses BackpressureRegulator to periodically force sync processing and\n * prevent unbounded accumulation of async work.\n */\n private async processBatchAsync(ops: any[], clientId: string): Promise<void> {\n // === BACKPRESSURE: Check if we should force sync processing ===\n if (this.backpressure.shouldForceSync()) {\n this.metricsService.incBackpressureSyncForced();\n await this.processBatchSync(ops, clientId);\n return;\n }\n\n // === BACKPRESSURE: Check and wait for capacity ===\n if (!this.backpressure.registerPending()) {\n this.metricsService.incBackpressureWaits();\n try {\n await this.backpressure.waitForCapacity();\n this.backpressure.registerPending();\n } catch (err) {\n this.metricsService.incBackpressureTimeouts();\n logger.warn({ clientId, pendingOps: ops.length }, 'Backpressure timeout - rejecting batch');\n throw new Error('Server overloaded');\n }\n }\n\n // Update pending ops metric\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n\n try {\n // === OPTIMIZATION 3: Batch Broadcast ===\n // Collect all events for a single batched broadcast at the end\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n // Process without immediate broadcast (we'll batch them)\n await this.processLocalOpForBatch(op, clientId, batchedEvents);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in async batch');\n }\n } else {\n // Forward to owner\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n }\n }\n\n // Send batched broadcast if we have events\n if (batchedEvents.length > 0) {\n this.broadcastBatch(batchedEvents, clientId);\n }\n } finally {\n this.backpressure.completePending();\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n }\n }\n\n /**\n * === BACKPRESSURE: Synchronous Batch Processing ===\n * Processes operations synchronously, waiting for broadcast completion.\n * Used when backpressure forces sync to drain the pipeline.\n */\n private async processBatchSync(ops: any[], clientId: string): Promise<void> {\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOpForBatch(op, clientId, batchedEvents);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in sync batch');\n }\n } else {\n // Forward to owner and wait for acknowledgment\n const owner = this.partitionService.getOwner(op.key);\n await this.forwardOpAndWait(op, owner);\n }\n }\n\n // Send batched broadcast SYNCHRONOUSLY - wait for all sends to complete\n if (batchedEvents.length > 0) {\n await this.broadcastBatchSync(batchedEvents, clientId);\n }\n }\n\n /**\n * Forward operation to owner node and wait for completion.\n * Used in sync processing mode.\n */\n private async forwardOpAndWait(op: any, owner: string): Promise<void> {\n return new Promise<void>((resolve) => {\n // Fire and forget for now - cluster forwarding doesn't have ack mechanism\n // In a full implementation, this would wait for cluster ACK\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n // Resolve immediately since cluster doesn't support sync ACK yet\n resolve();\n });\n }\n\n /**\n * Process a single operation for batch processing.\n * Uses shared applyOpToMap but collects events instead of broadcasting immediately.\n */\n private async processLocalOpForBatch(op: any, clientId: string, batchedEvents: any[]): Promise<void> {\n // 1. Build context for interceptors\n const context = this.buildOpContext(clientId, false);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) return; // Silently dropped by interceptor\n op = processedOp;\n } catch (err) {\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op in batch');\n throw err;\n }\n\n // 3. Apply operation to map (shared logic)\n const { eventPayload } = this.applyOpToMap(op);\n\n // 4. Replicate to backup nodes (Hazelcast pattern: after local merge)\n if (this.replicationPipeline) {\n const opId = op.id || `${op.mapName}:${op.key}:${Date.now()}`;\n // Fire-and-forget for batch operations (EVENTUAL by default)\n this.replicationPipeline.replicate(op, opId, op.key).catch(err => {\n logger.warn({ opId, key: op.key, err }, 'Batch replication failed (non-fatal)');\n });\n }\n\n // 5. Collect event for batched broadcast (instead of immediate broadcast)\n batchedEvents.push(eventPayload);\n\n // 6. Broadcast to cluster (for subscribers)\n this.broadcastToCluster(eventPayload);\n\n // 7. Run onAfterOp interceptors\n this.runAfterInterceptors(op, context);\n }\n\n private handleClusterEvent(payload: any) {\n // 1. Replication Logic: Am I a Backup?\n const { mapName, key, eventType } = payload;\n const map = this.getMap(mapName, (eventType === 'OR_ADD' || eventType === 'OR_REMOVE') ? 'OR' : 'LWW');\n const oldRecord = (map instanceof LWWMap) ? map.getRecord(key) : null;\n\n // Only store if we are Owner (shouldn't receive event unless forwarded) or Backup\n if (this.partitionService.isRelated(key)) {\n if (map instanceof LWWMap && payload.record) {\n map.merge(key, payload.record);\n } else if (map instanceof ORMap) {\n if (eventType === 'OR_ADD' && payload.orRecord) {\n map.apply(key, payload.orRecord);\n } else if (eventType === 'OR_REMOVE' && payload.orTag) {\n map.applyTombstone(payload.orTag);\n }\n }\n }\n\n // 2. Notify Query Subscriptions\n this.queryRegistry.processChange(mapName, map, key, payload.record || payload.orRecord, oldRecord);\n\n // 3. Broadcast to local clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: payload,\n timestamp: this.hlc.now()\n });\n }\n\n public getMap(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): LWWMap<string, any> | ORMap<string, any> {\n if (!this.maps.has(name)) {\n let map: LWWMap<string, any> | ORMap<string, any>;\n\n if (typeHint === 'OR') {\n map = new ORMap(this.hlc);\n } else {\n map = new LWWMap(this.hlc);\n }\n\n this.maps.set(name, map);\n\n // Lazy load from storage - track the promise for getMapAsync\n if (this.storage) {\n logger.info({ mapName: name }, 'Loading map from storage...');\n const loadPromise = this.loadMapFromStorage(name, typeHint);\n this.mapLoadingPromises.set(name, loadPromise);\n loadPromise.finally(() => {\n this.mapLoadingPromises.delete(name);\n });\n }\n }\n return this.maps.get(name)!;\n }\n\n /**\n * Returns map after ensuring it's fully loaded from storage.\n * Use this for queries to avoid returning empty results during initial load.\n */\n public async getMapAsync(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): Promise<LWWMap<string, any> | ORMap<string, any>> {\n const mapExisted = this.maps.has(name);\n\n // First ensure map exists (this triggers loading if needed)\n this.getMap(name, typeHint);\n\n // Wait for loading to complete if in progress\n const loadingPromise = this.mapLoadingPromises.get(name);\n\n // [DEBUG] Log state for troubleshooting sync issues\n const map = this.maps.get(name);\n const mapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({\n mapName: name,\n mapExisted,\n hasLoadingPromise: !!loadingPromise,\n currentMapSize: mapSize\n }, '[getMapAsync] State check');\n\n if (loadingPromise) {\n logger.info({ mapName: name }, '[getMapAsync] Waiting for loadMapFromStorage...');\n await loadingPromise;\n const newMapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({ mapName: name, mapSizeAfterLoad: newMapSize }, '[getMapAsync] Load completed');\n }\n\n return this.maps.get(name)!;\n }\n\n private async loadMapFromStorage(name: string, typeHint: 'LWW' | 'OR'): Promise<void> {\n try {\n const keys = await this.storage!.loadAllKeys(name);\n if (keys.length === 0) return;\n\n // Check for ORMap markers in keys\n const hasTombstones = keys.includes('__tombstones__');\n\n const relatedKeys = keys.filter(k => this.partitionService.isRelated(k));\n if (relatedKeys.length === 0) return;\n\n const records = await this.storage!.loadAll(name, relatedKeys);\n let count = 0;\n\n // Check for Type Mismatch and Replace Map if needed\n let isOR = hasTombstones;\n if (!isOR) {\n // Check first record\n for (const [k, v] of records) {\n if (k !== '__tombstones__' && (v as any).type === 'OR') {\n isOR = true;\n break;\n }\n }\n }\n\n // If we created LWW but it's OR, replace it.\n // If we created OR but it's LWW, replace it? (Less likely if hint was OR, but possible if hint was wrong?)\n const currentMap = this.maps.get(name);\n if (!currentMap) return;\n let targetMap = currentMap;\n\n if (isOR && currentMap instanceof LWWMap) {\n logger.info({ mapName: name }, 'Map auto-detected as ORMap. Switching type.');\n targetMap = new ORMap(this.hlc);\n this.maps.set(name, targetMap);\n } else if (!isOR && currentMap instanceof ORMap && typeHint !== 'OR') {\n // Only switch back to LWW if hint wasn't explicit OR\n logger.info({ mapName: name }, 'Map auto-detected as LWWMap. Switching type.');\n targetMap = new LWWMap(this.hlc);\n this.maps.set(name, targetMap);\n }\n\n if (targetMap instanceof ORMap) {\n for (const [key, record] of records) {\n if (key === '__tombstones__') {\n const t = record as ORMapTombstones;\n if (t && t.tags) t.tags.forEach(tag => targetMap.applyTombstone(tag));\n } else {\n const orVal = record as ORMapValue<any>;\n if (orVal && orVal.records) {\n orVal.records.forEach(r => targetMap.apply(key, r));\n count++;\n }\n }\n }\n } else if (targetMap instanceof LWWMap) {\n for (const [key, record] of records) {\n // Expect LWWRecord\n // If record is actually ORMapValue (mismatch), we skip or error?\n // If !isOR, we assume LWWRecord.\n if (!(record as any).type) { // LWWRecord doesn't have type property in my impl\n targetMap.merge(key, record as LWWRecord<any>);\n count++;\n }\n }\n }\n\n if (count > 0) {\n logger.info({ mapName: name, count }, 'Loaded records for map');\n this.queryRegistry.refreshSubscriptions(name, targetMap);\n const mapSize = (targetMap instanceof ORMap) ? targetMap.totalRecords : targetMap.size;\n this.metricsService.setMapSize(name, mapSize);\n }\n } catch (err) {\n logger.error({ mapName: name, err }, 'Failed to load map');\n }\n }\n\n private startGarbageCollection() {\n this.gcInterval = setInterval(() => {\n this.reportLocalHlc();\n }, GC_INTERVAL_MS);\n }\n\n // ============ Heartbeat Methods ============\n\n /**\n * Starts the periodic check for dead clients (those that haven't sent PING).\n */\n private startHeartbeatCheck() {\n this.heartbeatCheckInterval = setInterval(() => {\n this.evictDeadClients();\n }, CLIENT_HEARTBEAT_CHECK_INTERVAL_MS);\n }\n\n /**\n * Handles incoming PING message from client.\n * Responds with PONG immediately.\n */\n private handlePing(client: ClientConnection, clientTimestamp: number): void {\n client.lastPingReceived = Date.now();\n\n const pongMessage = {\n type: 'PONG',\n timestamp: clientTimestamp,\n serverTime: Date.now(),\n };\n\n // PONG is urgent - bypass batching for accurate RTT measurement\n client.writer.write(pongMessage, true);\n }\n\n /**\n * Checks if a client is still alive based on heartbeat.\n */\n public isClientAlive(clientId: string): boolean {\n const client = this.clients.get(clientId);\n if (!client) return false;\n\n const idleTime = Date.now() - client.lastPingReceived;\n return idleTime < CLIENT_HEARTBEAT_TIMEOUT_MS;\n }\n\n /**\n * Returns how long the client has been idle (no PING received).\n */\n public getClientIdleTime(clientId: string): number {\n const client = this.clients.get(clientId);\n if (!client) return Infinity;\n\n return Date.now() - client.lastPingReceived;\n }\n\n /**\n * Evicts clients that haven't sent a PING within the timeout period.\n */\n private evictDeadClients(): void {\n const now = Date.now();\n const deadClients: string[] = [];\n\n for (const [clientId, client] of this.clients) {\n // Only check authenticated clients (unauthenticated ones will timeout via auth mechanism)\n if (client.isAuthenticated) {\n const idleTime = now - client.lastPingReceived;\n if (idleTime > CLIENT_HEARTBEAT_TIMEOUT_MS) {\n deadClients.push(clientId);\n }\n }\n }\n\n for (const clientId of deadClients) {\n const client = this.clients.get(clientId);\n if (client) {\n logger.warn({\n clientId,\n idleTime: now - client.lastPingReceived,\n timeoutMs: CLIENT_HEARTBEAT_TIMEOUT_MS,\n }, 'Evicting dead client (heartbeat timeout)');\n\n // Close the connection\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.close(4002, 'Heartbeat timeout');\n }\n }\n }\n }\n\n private reportLocalHlc() {\n // 1. Calculate Local Min HLC\n let minHlc = this.hlc.now();\n\n for (const client of this.clients.values()) {\n if (HLC.compare(client.lastActiveHlc, minHlc) < 0) {\n minHlc = client.lastActiveHlc;\n }\n }\n\n const members = this.cluster.getMembers().sort();\n const leaderId = members[0];\n const myId = this.cluster.config.nodeId;\n\n if (leaderId === myId) {\n // I am Leader\n this.handleGcReport(myId, minHlc);\n } else {\n // Send to Leader\n this.cluster.send(leaderId, 'CLUSTER_GC_REPORT', { minHlc });\n }\n }\n\n private handleGcReport(nodeId: string, minHlc: Timestamp) {\n this.gcReports.set(nodeId, minHlc);\n\n const members = this.cluster.getMembers();\n\n // Check if we have reports from ALL members\n // (Including self, which is inserted directly)\n const allReported = members.every(m => this.gcReports.has(m));\n\n if (allReported) {\n // Calculate Global Safe Timestamp\n let globalSafe = this.hlc.now(); // Start high\n let initialized = false;\n\n for (const ts of this.gcReports.values()) {\n if (!initialized || HLC.compare(ts, globalSafe) < 0) {\n globalSafe = ts;\n initialized = true;\n }\n }\n\n // Add safety buffer (e.g. GC_AGE)\n // prune(timestamp) removes items OLDER than timestamp.\n // We want to remove items OLDER than (GlobalMin - GC_AGE).\n\n const olderThanMillis = globalSafe.millis - GC_AGE_MS;\n const safeTimestamp: Timestamp = {\n millis: olderThanMillis,\n counter: 0,\n nodeId: globalSafe.nodeId // Doesn't matter much for comparison if millis match, but best effort\n };\n\n logger.info({\n globalMinHlc: globalSafe.millis,\n safeGcTimestamp: olderThanMillis,\n reportsCount: this.gcReports.size\n }, 'GC Consensus Reached. Broadcasting Commit.');\n\n // Broadcast Commit\n const commitMsg = {\n type: 'CLUSTER_GC_COMMIT', // Handled by cluster listener\n payload: { safeTimestamp }\n };\n\n // Send to others\n for (const member of members) {\n if (!this.cluster.isLocal(member)) {\n this.cluster.send(member, 'CLUSTER_GC_COMMIT', { safeTimestamp });\n }\n }\n\n // Execute Locally\n this.performGarbageCollection(safeTimestamp);\n\n // Clear reports for next round?\n // Or keep them and overwrite?\n // Overwriting is better for partial updates, but clearing ensures freshness.\n // Since we run interval based, clearing is safer to ensure active participation next time.\n this.gcReports.clear();\n }\n }\n\n private performGarbageCollection(olderThan: Timestamp) {\n logger.info({ olderThanMillis: olderThan.millis }, 'Performing Garbage Collection');\n const now = Date.now();\n\n for (const [name, map] of this.maps) {\n // 1. Check for active expired records (TTL)\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const record = map.getRecord(key);\n if (record && record.value !== null && record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n logger.info({ mapName: name, key }, 'Record expired (TTL). Converting to tombstone.');\n\n // Create Tombstone at expiration time to handle \"Resurrection\" correctly\n const tombstoneTimestamp: Timestamp = {\n millis: expirationTime,\n counter: 0, // Reset counter for expiration time\n nodeId: this.hlc.getNodeId // Use our ID\n };\n\n const tombstone: LWWRecord<any> = { value: null, timestamp: tombstoneTimestamp };\n\n // Apply locally\n const changed = map.merge(key, tombstone);\n\n if (changed) {\n // Persist and Broadcast\n // Construct an artificial op to reuse pipeline logic or do manual steps\n // Manual steps are safer here as we don't have a client op context\n\n if (this.storage) {\n this.storage.store(name, key, tombstone).catch(err =>\n logger.error({ mapName: name, key, err }, 'Failed to persist expired tombstone')\n );\n }\n\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'UPDATED',\n record: tombstone\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedKeys = map.prune(olderThan);\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned records from LWW map');\n if (this.storage) {\n this.storage.deleteAll(name, removedKeys).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to delete pruned keys from storage');\n });\n }\n }\n } else if (map instanceof ORMap) {\n // ORMap Expiration\n // We need to check all active records in the ORMap\n const items = (map as any).items as Map<string, Map<string, ORMapRecord<any>>>;\n const tombstonesSet = (map as any).tombstones as Set<string>;\n\n const tagsToExpire: { key: string; tag: string }[] = [];\n\n for (const [key, keyMap] of items) {\n for (const [tag, record] of keyMap) {\n if (!tombstonesSet.has(tag)) {\n if (record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n tagsToExpire.push({ key, tag });\n }\n }\n }\n }\n }\n\n for (const { key, tag } of tagsToExpire) {\n logger.info({ mapName: name, key, tag }, 'ORMap Record expired (TTL). Removing.');\n\n // Remove by adding tag to tombstones\n map.applyTombstone(tag);\n\n // Persist change\n if (this.storage) {\n // We need to update the key's record list and tombstones\n // Optimally, we should batch these updates\n const records = map.getRecords(key);\n if (records.length > 0) {\n this.storage.store(name, key, { type: 'OR', records });\n } else {\n this.storage.delete(name, key);\n }\n\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n });\n }\n\n // Broadcast\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'OR_REMOVE',\n orTag: tag\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from OR map');\n // We need to update __tombstones__ in storage\n if (this.storage) {\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n }).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to update tombstones');\n });\n }\n }\n }\n }\n\n // Broadcast to clients\n this.broadcast({\n type: 'GC_PRUNE',\n payload: {\n olderThan\n }\n });\n }\n private buildTLSOptions(config: TLSConfig): HttpsServerOptions {\n const options: HttpsServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.ciphers) {\n options.ciphers = config.ciphers;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n\n // ============ Write Concern Methods (Phase 5.01) ============\n\n /**\n * Get effective Write Concern level for an operation.\n * Per-op writeConcern overrides batch-level.\n */\n private getEffectiveWriteConcern(\n opWriteConcern: WriteConcernValue | undefined,\n batchWriteConcern: WriteConcernValue | undefined\n ): WriteConcernValue | undefined {\n return opWriteConcern ?? batchWriteConcern;\n }\n\n /**\n * Convert string WriteConcern value to enum.\n */\n private stringToWriteConcern(value: WriteConcernValue | undefined): WriteConcern {\n switch (value) {\n case 'FIRE_AND_FORGET':\n return WriteConcern.FIRE_AND_FORGET;\n case 'MEMORY':\n return WriteConcern.MEMORY;\n case 'APPLIED':\n return WriteConcern.APPLIED;\n case 'REPLICATED':\n return WriteConcern.REPLICATED;\n case 'PERSISTED':\n return WriteConcern.PERSISTED;\n default:\n return WriteConcern.MEMORY;\n }\n }\n\n /**\n * Process batch with Write Concern tracking.\n * Notifies WriteAckManager at each stage of processing.\n */\n private async processBatchAsyncWithWriteConcern(\n ops: any[],\n clientId: string,\n batchWriteConcern?: WriteConcernValue,\n batchTimeout?: number\n ): Promise<void> {\n // === BACKPRESSURE: Check if we should force sync processing ===\n if (this.backpressure.shouldForceSync()) {\n this.metricsService.incBackpressureSyncForced();\n await this.processBatchSyncWithWriteConcern(ops, clientId, batchWriteConcern, batchTimeout);\n return;\n }\n\n // === BACKPRESSURE: Check and wait for capacity ===\n if (!this.backpressure.registerPending()) {\n this.metricsService.incBackpressureWaits();\n try {\n await this.backpressure.waitForCapacity();\n this.backpressure.registerPending();\n } catch (err) {\n this.metricsService.incBackpressureTimeouts();\n logger.warn({ clientId, pendingOps: ops.length }, 'Backpressure timeout - rejecting batch');\n // Fail all pending operations\n for (const op of ops) {\n if (op.id) {\n this.writeAckManager.failPending(op.id, 'Server overloaded');\n }\n }\n throw new Error('Server overloaded');\n }\n }\n\n // Update pending ops metric\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n\n try {\n // === OPTIMIZATION 3: Batch Broadcast ===\n // Collect all events for a single batched broadcast at the end\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n // Process operation with Write Concern tracking\n await this.processLocalOpWithWriteConcern(op, clientId, batchedEvents, batchWriteConcern);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in async batch');\n // Fail the pending write\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n }\n } else {\n // Forward to owner\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType,\n writeConcern: op.writeConcern ?? batchWriteConcern,\n }\n });\n // For forwarded ops, we mark REPLICATED immediately since it's sent to cluster\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n\n // Send batched broadcast if we have events\n if (batchedEvents.length > 0) {\n this.broadcastBatch(batchedEvents, clientId);\n // Notify REPLICATED for all ops that were broadcast\n for (const op of ops) {\n if (op.id && this.partitionService.isLocalOwner(op.key)) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n } finally {\n this.backpressure.completePending();\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n }\n }\n\n /**\n * Synchronous batch processing with Write Concern.\n */\n private async processBatchSyncWithWriteConcern(\n ops: any[],\n clientId: string,\n batchWriteConcern?: WriteConcernValue,\n batchTimeout?: number\n ): Promise<void> {\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOpWithWriteConcern(op, clientId, batchedEvents, batchWriteConcern);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in sync batch');\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n }\n } else {\n // Forward to owner and wait for acknowledgment\n const owner = this.partitionService.getOwner(op.key);\n await this.forwardOpAndWait(op, owner);\n // Mark REPLICATED after forwarding\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n\n // Send batched broadcast SYNCHRONOUSLY - wait for all sends to complete\n if (batchedEvents.length > 0) {\n await this.broadcastBatchSync(batchedEvents, clientId);\n // Notify REPLICATED for all local ops\n for (const op of ops) {\n if (op.id && this.partitionService.isLocalOwner(op.key)) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n }\n\n /**\n * Process a single operation with Write Concern level notifications.\n */\n private async processLocalOpWithWriteConcern(\n op: any,\n clientId: string,\n batchedEvents: any[],\n batchWriteConcern?: WriteConcernValue\n ): Promise<void> {\n // 1. Build context for interceptors\n const context = this.buildOpContext(clientId, false);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) {\n // Silently dropped by interceptor - fail the pending write\n if (op.id) {\n this.writeAckManager.failPending(op.id, 'Dropped by interceptor');\n }\n return;\n }\n op = processedOp;\n } catch (err) {\n logger.warn({ opId: op.id, err }, 'Interceptor rejected op');\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n return;\n }\n\n // 3. Apply operation to map\n const { eventPayload } = this.applyOpToMap(op);\n\n // 4. Notify APPLIED level (CRDT merged)\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.APPLIED);\n }\n\n // 5. Collect event for batched broadcast\n if (eventPayload) {\n batchedEvents.push({\n mapName: op.mapName,\n key: op.key,\n ...eventPayload\n });\n }\n\n // 6. Handle PERSISTED Write Concern\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n if (effectiveWriteConcern === 'PERSISTED' && this.storage) {\n try {\n // Wait for storage write to complete\n await this.persistOpSync(op);\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.PERSISTED);\n }\n } catch (err) {\n logger.error({ opId: op.id, err }, 'Persistence failed');\n if (op.id) {\n this.writeAckManager.failPending(op.id, `Persistence failed: ${err}`);\n }\n }\n } else if (this.storage && op.id) {\n // Fire-and-forget persistence for non-PERSISTED writes\n this.persistOpAsync(op).catch(err => {\n logger.error({ opId: op.id, err }, 'Async persistence failed');\n });\n }\n\n // 7. Run onAfterOp interceptors\n try {\n const serverOp: ServerOp = {\n mapName: op.mapName,\n key: op.key,\n opType: op.opType || (op.record?.value === null ? 'REMOVE' : 'PUT'),\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n };\n await this.runAfterInterceptors(serverOp, context);\n } catch (err) {\n logger.warn({ opId: op.id, err }, 'onAfterOp interceptor failed');\n }\n }\n\n /**\n * Persist operation synchronously (blocking).\n * Used for PERSISTED Write Concern.\n */\n private async persistOpSync(op: any): Promise<void> {\n if (!this.storage) return;\n\n const isORMapOp = op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE' || op.orRecord || op.orTag;\n\n if (isORMapOp) {\n const orMap = this.getMap(op.mapName, 'OR') as ORMap<string, any>;\n const records = orMap.getRecords(op.key);\n const tombstones = orMap.getTombstones();\n\n if (records.length > 0) {\n await this.storage.store(op.mapName, op.key, { type: 'OR', records } as ORMapValue<any>);\n } else {\n await this.storage.delete(op.mapName, op.key);\n }\n\n if (tombstones.length > 0) {\n await this.storage.store(op.mapName, '__tombstones__', { type: 'OR_TOMBSTONES', tags: tombstones } as ORMapTombstones);\n }\n } else {\n const lwwMap = this.getMap(op.mapName, 'LWW') as LWWMap<string, any>;\n const record = lwwMap.getRecord(op.key);\n if (record) {\n await this.storage.store(op.mapName, op.key, record);\n }\n }\n }\n\n /**\n * Persist operation asynchronously (fire-and-forget).\n * Used for non-PERSISTED Write Concern levels.\n */\n private async persistOpAsync(op: any): Promise<void> {\n return this.persistOpSync(op);\n }\n}\n","import { LWWRecord, PredicateNode, evaluatePredicate } from '@topgunbuild/core';\n\nexport interface Query {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Checks if a record matches a query.\n * Supports simple exact match for now.\n */\nexport function matchesQuery(record: LWWRecord<any>, query: Query): boolean {\n const data = record.value;\n if (!data) return false; \n\n // Check TTL\n if (record.ttlMs) {\n const now = Date.now();\n if (record.timestamp.millis + record.ttlMs < now) {\n return false; // Expired\n }\n }\n\n // 1. New Predicate API\n if (query.predicate) {\n return evaluatePredicate(query.predicate, data);\n }\n\n // 2. Legacy 'where' clause\n if (!query.where) return true; // Empty query matches everything\n\n for (const [field, expected] of Object.entries(query.where)) {\n const actual = data[field];\n\n // Operator matching (e.g. { age: { $gt: 18 } })\n if (typeof expected === 'object' && expected !== null && !Array.isArray(expected)) {\n for (const [op, opValueRaw] of Object.entries(expected)) {\n const opValue = opValueRaw as any; // Cast for comparison\n switch (op) {\n case '$gt':\n if (!(actual > opValue)) return false;\n break;\n case '$gte':\n if (!(actual >= opValue)) return false;\n break;\n case '$lt':\n if (!(actual < opValue)) return false;\n break;\n case '$lte':\n if (!(actual <= opValue)) return false;\n break;\n case '$ne':\n if (!(actual !== opValue)) return false;\n break;\n // Add more operators as needed ($in, etc.)\n default:\n // Unknown operator, treating as exact match (or should we fail?)\n // For now, ignore unknown operators or treat as mismatch?\n // Let's treat unknown operators as false to be safe.\n return false; \n }\n }\n } else {\n // Simple exact match\n if (actual !== expected) {\n return false;\n }\n }\n }\n \n return true;\n}\n\nexport function executeQuery(records: Map<string, LWWRecord<any>> | LWWRecord<any>[], query: Query): { key: string; value: any }[] {\n // Handle null/undefined query\n if (!query) {\n query = {};\n }\n\n let results: { key: string; record: LWWRecord<any> }[] = [];\n\n // 1. Filter\n if (records instanceof Map) {\n for (const [key, record] of records) {\n if (matchesQuery(record, query)) {\n results.push({ key, record });\n }\n }\n } else {\n // If array, we might not have keys easily unless they are in the record or we iterate\n // For now assume Map input primarily for ServerCoordinator\n // But if input is array of records?\n for (const record of records) {\n // Assuming key is not readily available if just array of records, \n // but usually we pass Map from ServerCoordinator.\n // If we really need key, we need it in the input.\n // Let's stick to Map input for now as that's what ServerCoordinator has.\n // But wait, the signature I defined allows array.\n if (matchesQuery(record, query)) {\n results.push({ key: '?', record }); \n }\n }\n }\n\n // 2. Sort\n if (query.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n const valA = a.record.value[field];\n const valB = b.record.value[field];\n \n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // 3. Limit & Offset\n if (query.offset || query.limit) {\n const offset = query.offset || 0;\n const limit = query.limit || results.length;\n results = results.slice(offset, offset + limit);\n }\n\n return results.map(r => ({ key: r.key, value: r.record.value }));\n}\n","import { Query, matchesQuery, executeQuery } from './Matcher';\nimport { LWWRecord, LWWMap, ORMap, serialize, PredicateNode, ORMapRecord } from '@topgunbuild/core';\nimport { WebSocket } from 'ws';\nimport { logger } from '../utils/logger';\n\nexport interface Subscription {\n id: string; // queryId\n clientId: string;\n mapName: string;\n query: Query;\n socket: WebSocket;\n previousResultKeys: Set<string>;\n interestedFields?: Set<string> | 'ALL';\n _cleanup?: () => void; // For Reverse Index cleanup\n}\n\nclass ReverseQueryIndex {\n // field -> value -> Set<Subscription>\n private equality = new Map<string, Map<any, Set<Subscription>>>();\n // field -> Set<Subscription>\n private interest = new Map<string, Set<Subscription>>();\n // catch-all\n private wildcard = new Set<Subscription>();\n\n public add(sub: Subscription) {\n const query = sub.query;\n let indexed = false;\n const cleanupFns: (() => void)[] = [];\n\n // 1. Where\n if (query.where) {\n for (const [field, value] of Object.entries(query.where)) {\n if (typeof value !== 'object') {\n // Exact match\n this.addEquality(field, value, sub);\n cleanupFns.push(() => this.removeEquality(field, value, sub));\n indexed = true;\n } else {\n // Operator - add to interest\n this.addInterest(field, sub);\n cleanupFns.push(() => this.removeInterest(field, sub));\n indexed = true;\n }\n }\n }\n \n // 2. Predicate\n if (query.predicate) {\n const visit = (node: PredicateNode) => {\n if (node.op === 'eq' && node.attribute && node.value !== undefined) {\n this.addEquality(node.attribute, node.value, sub);\n cleanupFns.push(() => this.removeEquality(node.attribute!, node.value, sub));\n indexed = true;\n } else if (node.attribute) {\n // Any other op on attribute\n this.addInterest(node.attribute, sub);\n cleanupFns.push(() => this.removeInterest(node.attribute!, sub));\n indexed = true;\n }\n \n if (node.children) {\n node.children.forEach(visit);\n }\n };\n visit(query.predicate);\n }\n \n // 3. Sort\n if (query.sort) {\n Object.keys(query.sort).forEach(k => {\n this.addInterest(k, sub);\n cleanupFns.push(() => this.removeInterest(k, sub));\n indexed = true;\n });\n }\n\n if (!indexed) {\n this.wildcard.add(sub);\n cleanupFns.push(() => this.wildcard.delete(sub));\n }\n \n sub._cleanup = () => cleanupFns.forEach(fn => fn());\n }\n\n public remove(sub: Subscription) {\n if (sub._cleanup) {\n sub._cleanup();\n sub._cleanup = undefined;\n }\n }\n\n public getCandidates(changedFields: Set<string> | 'ALL', oldVal: any, newVal: any): Set<Subscription> {\n const candidates = new Set<Subscription>(this.wildcard);\n\n if (changedFields === 'ALL') {\n // Return all possible candidates (inefficient but safe)\n // We collect from all indexes? Or just return all subs?\n // To match \"wildcard\" behavior, we should probably iterate all.\n // But we don't track all subs in index easily.\n // We can iterate this.interest and this.equality.\n for (const set of this.interest.values()) {\n for (const s of set) candidates.add(s);\n }\n for (const map of this.equality.values()) {\n for (const set of map.values()) {\n for (const s of set) candidates.add(s);\n }\n }\n return candidates;\n }\n\n // If no changes detected (shouldn't happen if called correctly), just return wildcard\n if (changedFields.size === 0) return candidates;\n\n for (const field of changedFields) {\n // 1. Interest (General)\n if (this.interest.has(field)) {\n for (const sub of this.interest.get(field)!) {\n candidates.add(sub);\n }\n }\n\n // 2. Equality\n if (this.equality.has(field)) {\n const valMap = this.equality.get(field)!;\n \n // Check New Value queries\n if (newVal && newVal[field] !== undefined && valMap.has(newVal[field])) {\n for (const sub of valMap.get(newVal[field])!) {\n candidates.add(sub);\n }\n }\n \n // Check Old Value queries\n if (oldVal && oldVal[field] !== undefined && valMap.has(oldVal[field])) {\n for (const sub of valMap.get(oldVal[field])!) {\n candidates.add(sub);\n }\n }\n }\n }\n \n return candidates;\n }\n\n private addEquality(field: string, value: any, sub: Subscription) {\n if (!this.equality.has(field)) this.equality.set(field, new Map());\n const valMap = this.equality.get(field)!;\n if (!valMap.has(value)) valMap.set(value, new Set());\n valMap.get(value)!.add(sub);\n }\n\n private removeEquality(field: string, value: any, sub: Subscription) {\n const valMap = this.equality.get(field);\n if (valMap) {\n const set = valMap.get(value);\n if (set) {\n set.delete(sub);\n if (set.size === 0) valMap.delete(value);\n }\n if (valMap.size === 0) this.equality.delete(field);\n }\n }\n\n private addInterest(field: string, sub: Subscription) {\n if (!this.interest.has(field)) this.interest.set(field, new Set());\n this.interest.get(field)!.add(sub);\n }\n\n private removeInterest(field: string, sub: Subscription) {\n const set = this.interest.get(field);\n if (set) {\n set.delete(sub);\n if (set.size === 0) this.interest.delete(field);\n }\n }\n}\n\nexport class QueryRegistry {\n // MapName -> Set of Subscriptions (Legacy/Backup)\n private subscriptions: Map<string, Set<Subscription>> = new Map();\n \n // MapName -> Reverse Index\n private indexes: Map<string, ReverseQueryIndex> = new Map();\n\n public register(sub: Subscription) {\n if (!this.subscriptions.has(sub.mapName)) {\n this.subscriptions.set(sub.mapName, new Set());\n this.indexes.set(sub.mapName, new ReverseQueryIndex());\n }\n \n const interestedFields = this.analyzeQueryFields(sub.query);\n sub.interestedFields = interestedFields;\n\n this.subscriptions.get(sub.mapName)!.add(sub);\n this.indexes.get(sub.mapName)!.add(sub);\n \n logger.info({ clientId: sub.clientId, mapName: sub.mapName, query: sub.query }, 'Client subscribed');\n }\n\n public unregister(queryId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.id === queryId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n return; \n }\n }\n }\n }\n\n public unsubscribeAll(clientId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.clientId === clientId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n }\n }\n }\n }\n\n /**\n * Returns all active subscriptions for a specific map.\n * Used for subscription-based event routing to avoid broadcasting to all clients.\n */\n public getSubscriptionsForMap(mapName: string): Subscription[] {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) {\n return [];\n }\n return Array.from(subs);\n }\n\n /**\n * Returns unique client IDs that have subscriptions for a specific map.\n * Useful for efficient routing when a client has multiple queries on the same map.\n */\n public getSubscribedClientIds(mapName: string): Set<string> {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) {\n return new Set();\n }\n const clientIds = new Set<string>();\n for (const sub of subs) {\n clientIds.add(sub.clientId);\n }\n return clientIds;\n }\n\n /**\n * Refreshes all subscriptions for a given map.\n * Useful when the map is bulk-loaded from storage.\n */\n public refreshSubscriptions(mapName: string, map: LWWMap<string, any> | ORMap<string, any>) {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) return;\n\n const allRecords = this.getMapRecords(map);\n\n for (const sub of subs) {\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n // Send update for all currently matching records\n // We assume value might have changed or it is new\n this.sendUpdate(sub, res.key, res.value, 'UPDATE');\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private getMapRecords(map: LWWMap<string, any> | ORMap<string, any>): Map<string, any> {\n const recordsMap = new Map<string, any>();\n\n // Use duck-typing to support mocks and proxies\n const mapAny = map as any;\n\n // LWWMap-like: has allKeys() and getRecord()\n if (typeof mapAny.allKeys === 'function' && typeof mapAny.getRecord === 'function') {\n for (const key of mapAny.allKeys()) {\n const rec = mapAny.getRecord(key);\n if (rec) {\n recordsMap.set(key, rec);\n }\n }\n }\n // ORMap-like: has items Map and get() returns array\n else if (mapAny.items instanceof Map && typeof mapAny.get === 'function') {\n const items = mapAny.items as Map<string, any>;\n for (const key of items.keys()) {\n const values = mapAny.get(key);\n if (values.length > 0) {\n recordsMap.set(key, { value: values });\n }\n }\n }\n return recordsMap;\n }\n\n /**\n * Processes a record change for all relevant subscriptions.\n * Calculates diffs and sends updates.\n */\n public processChange(\n mapName: string,\n map: LWWMap<string, any> | ORMap<string, any>,\n changeKey: string,\n changeRecord: any, // LWWRecord | ORMapRecord | ORMapRecord[]\n oldRecord?: any // LWWRecord | ORMapRecord[]\n ) {\n const index = this.indexes.get(mapName);\n if (!index) return;\n\n // Extract Values\n const newVal = this.extractValue(changeRecord);\n const oldVal = this.extractValue(oldRecord);\n\n // 0. Calculate Changed Fields\n const changedFields = this.getChangedFields(oldVal, newVal);\n\n if (changedFields !== 'ALL' && changedFields.size === 0 && oldRecord && changeRecord) {\n return;\n }\n \n const candidates = index.getCandidates(changedFields, oldVal, newVal);\n \n if (candidates.size === 0) return;\n\n // Helper to get all records as a Map for executeQuery\n let recordsMap: Map<string, any> | null = null;\n const getRecordsMap = () => {\n if (recordsMap) return recordsMap;\n recordsMap = this.getMapRecords(map);\n return recordsMap;\n };\n\n for (const sub of candidates) {\n const dummyRecord: LWWRecord<any> = { \n value: newVal,\n timestamp: { millis: 0, counter: 0, nodeId: '' } // Dummy timestamp for matchesQuery\n };\n const isMatch = matchesQuery(dummyRecord, sub.query); // Approximate match check\n const wasInResult = sub.previousResultKeys.has(changeKey);\n\n if (!isMatch && !wasInResult) {\n continue;\n }\n\n // Re-evaluate query\n const allRecords = getRecordsMap();\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // Determine changes\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n const key = res.key;\n const isNew = !sub.previousResultKeys.has(key);\n \n if (key === changeKey) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n } else if (isNew) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n }\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private extractValue(record: any): any {\n if (!record) return null;\n if (Array.isArray(record)) {\n // ORMapRecord[]\n return record.map(r => r.value);\n }\n // LWWRecord or ORMapRecord\n return record.value;\n }\n\n private sendUpdate(sub: Subscription, key: string, value: any, type: 'UPDATE' | 'REMOVE') {\n if (sub.socket.readyState === 1) {\n sub.socket.send(serialize({\n type: 'QUERY_UPDATE',\n payload: {\n queryId: sub.id,\n key,\n value,\n type\n }\n }));\n }\n }\n\n private analyzeQueryFields(query: Query): Set<string> | 'ALL' {\n const fields = new Set<string>();\n try {\n if (query.predicate) {\n const extract = (node: PredicateNode) => {\n if (node.attribute) fields.add(node.attribute);\n if (node.children) node.children.forEach(extract);\n };\n extract(query.predicate);\n }\n if (query.where) {\n Object.keys(query.where).forEach(k => fields.add(k));\n }\n if (query.sort) {\n Object.keys(query.sort).forEach(k => fields.add(k));\n }\n } catch (e) {\n return 'ALL';\n }\n return fields.size > 0 ? fields : 'ALL';\n }\n\n private getChangedFields(oldValue: any, newValue: any): Set<string> | 'ALL' {\n // If values are arrays (ORMap), just return ALL for now to force check\n if (Array.isArray(oldValue) || Array.isArray(newValue)) return 'ALL';\n\n if (oldValue === newValue) return new Set();\n if (!oldValue && !newValue) return new Set();\n\n if (!oldValue) return new Set(Object.keys(newValue || {}));\n if (!newValue) return new Set(Object.keys(oldValue || {}));\n \n const changes = new Set<string>();\n const allKeys = new Set([...Object.keys(oldValue), ...Object.keys(newValue)]);\n \n for (const key of allKeys) {\n if (oldValue[key] !== newValue[key]) {\n changes.add(key);\n }\n }\n return changes;\n }\n}\n","import pino from 'pino';\n\nconst logLevel = process.env.LOG_LEVEL || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: process.env.NODE_ENV !== 'production' ? {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,hostname'\n }\n } : undefined,\n formatters: {\n level: (label) => {\n return { level: label };\n }\n }\n});\n\nexport type Logger = typeof logger;\n\n","import { ClusterManager } from '../cluster/ClusterManager';\nimport { logger } from '../utils/logger';\n\nexport interface TopicManagerConfig {\n cluster: ClusterManager;\n /** Callback to send message to a specific client */\n sendToClient: (clientId: string, message: any) => void;\n}\n\nexport class TopicManager {\n private subscribers: Map<string, Set<string>> = new Map(); // topic -> Set<clientId>\n private cluster: ClusterManager;\n private sendToClient: (clientId: string, message: any) => void;\n private readonly MAX_SUBSCRIPTIONS = 100; // M1: Basic limit\n\n constructor(config: TopicManagerConfig) {\n this.cluster = config.cluster;\n this.sendToClient = config.sendToClient;\n }\n\n private validateTopic(topic: string): void {\n // H2: Validation\n if (!topic || topic.length > 256 || !/^[\\w\\-.:/]+$/.test(topic)) {\n throw new Error('Invalid topic name');\n }\n }\n\n /**\n * Subscribe a client to a topic\n */\n public subscribe(clientId: string, topic: string) {\n this.validateTopic(topic);\n\n // Check limit (M1)\n // This is expensive (iterating all topics). Optimized: maintain client->topics map?\n // For now, iterate.\n let count = 0;\n for (const subs of this.subscribers.values()) {\n if (subs.has(clientId)) count++;\n }\n if (count >= this.MAX_SUBSCRIPTIONS) {\n throw new Error('Subscription limit reached');\n }\n\n if (!this.subscribers.has(topic)) {\n this.subscribers.set(topic, new Set());\n }\n this.subscribers.get(topic)!.add(clientId);\n logger.debug({ clientId, topic }, 'Client subscribed to topic');\n }\n\n /**\n * Unsubscribe a client from a topic\n */\n public unsubscribe(clientId: string, topic: string) {\n const subs = this.subscribers.get(topic);\n if (subs) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n logger.debug({ clientId, topic }, 'Client unsubscribed from topic');\n }\n }\n\n /**\n * Clean up all subscriptions for a client (e.g. on disconnect)\n */\n public unsubscribeAll(clientId: string) {\n for (const [topic, subs] of this.subscribers) {\n if (subs.has(clientId)) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n }\n }\n }\n\n /**\n * Publish a message to a topic\n * @param topic Topic name\n * @param data Message data\n * @param senderId Client ID of the publisher (optional)\n * @param fromCluster Whether this message came from another cluster node\n */\n public publish(topic: string, data: any, senderId?: string, fromCluster: boolean = false) {\n this.validateTopic(topic);\n\n // 1. Send to local subscribers\n const subs = this.subscribers.get(topic);\n if (subs) {\n const payload = {\n topic,\n data,\n publisherId: senderId,\n timestamp: Date.now()\n };\n \n const message = {\n type: 'TOPIC_MESSAGE',\n payload\n };\n\n for (const clientId of subs) {\n // Don't echo back to sender if local\n if (clientId !== senderId) {\n this.sendToClient(clientId, message);\n }\n }\n }\n\n // 2. Broadcast to cluster (only if not already from cluster)\n if (!fromCluster) {\n this.cluster.getMembers().forEach(nodeId => {\n if (!this.cluster.isLocal(nodeId)) {\n this.cluster.send(nodeId, 'CLUSTER_TOPIC_PUB', {\n topic,\n data,\n originalSenderId: senderId\n });\n }\n });\n }\n }\n}\n\n","import { WebSocket, WebSocketServer, ClientOptions as WsClientOptions } from 'ws';\nimport { EventEmitter } from 'events';\nimport * as dns from 'dns';\nimport { logger } from '../utils/logger';\nimport { readFileSync } from 'fs';\nimport * as https from 'https';\nimport { ClusterTLSConfig } from '../types/TLSConfig';\nimport { FailureDetector, FailureDetectorConfig, DEFAULT_FAILURE_DETECTOR_CONFIG } from './FailureDetector';\n\nexport interface ClusterConfig {\n nodeId: string;\n host: string;\n port: number;\n peers: string[]; // List of \"host:port\"\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: ClusterTLSConfig;\n /** Heartbeat interval in milliseconds. Default: 1000 */\n heartbeatIntervalMs?: number;\n /** Failure detection configuration */\n failureDetection?: Partial<FailureDetectorConfig>;\n}\n\nexport interface ClusterMember {\n nodeId: string;\n host: string;\n port: number;\n socket: WebSocket;\n isSelf: boolean;\n}\n\nexport interface ClusterMessage {\n type: 'HELLO' | 'OP_FORWARD' | 'PARTITION_UPDATE' | 'HEARTBEAT' | 'CLUSTER_EVENT' | 'CLUSTER_QUERY_EXEC' | 'CLUSTER_QUERY_RESP' | 'CLUSTER_GC_REPORT' | 'CLUSTER_GC_COMMIT' | 'CLUSTER_LOCK_REQ' | 'CLUSTER_LOCK_RELEASE' | 'CLUSTER_LOCK_GRANTED' | 'CLUSTER_LOCK_RELEASED' | 'CLUSTER_CLIENT_DISCONNECTED' | 'CLUSTER_TOPIC_PUB';\n senderId: string;\n payload: any;\n}\n\nexport class ClusterManager extends EventEmitter {\n public readonly config: ClusterConfig;\n private server?: WebSocketServer;\n private members: Map<string, ClusterMember> = new Map();\n private pendingConnections: Set<string> = new Set();\n private reconnectIntervals: Map<string, NodeJS.Timeout> = new Map();\n private discoveryTimer?: NodeJS.Timeout;\n private heartbeatTimer?: NodeJS.Timeout;\n private failureDetector: FailureDetector;\n\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\n\n // Initialize failure detector\n this.failureDetector = new FailureDetector({\n ...DEFAULT_FAILURE_DETECTOR_CONFIG,\n heartbeatIntervalMs: config.heartbeatIntervalMs ?? 1000,\n ...config.failureDetection,\n });\n\n // Forward failure detector events\n this.failureDetector.on('nodeSuspected', (event) => {\n logger.warn({ nodeId: event.nodeId, phi: event.phi }, 'Node suspected (failure detector)');\n this.emit('nodeSuspected', event.nodeId, event.phi);\n });\n\n this.failureDetector.on('nodeRecovered', (event) => {\n logger.info({ nodeId: event.nodeId }, 'Node recovered (failure detector)');\n this.emit('nodeRecovered', event.nodeId);\n });\n\n this.failureDetector.on('nodeConfirmedFailed', (event) => {\n logger.error({ nodeId: event.nodeId }, 'Node failure confirmed');\n this.emit('nodeConfirmedFailed', event.nodeId);\n // Remove failed node from members\n this.handleNodeFailure(event.nodeId);\n });\n }\n\n /**\n * Get the failure detector instance.\n */\n public getFailureDetector(): FailureDetector {\n return this.failureDetector;\n }\n\n private _actualPort: number = 0;\n\n /** Get the actual port the cluster is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n public start(): Promise<number> {\n return new Promise((resolve) => {\n logger.info({ port: this.config.port, tls: !!this.config.tls?.enabled }, 'Starting Cluster Manager');\n\n if (this.config.tls?.enabled) {\n // HTTPS-based WebSocket Server for cluster\n const tlsOptions = this.buildClusterTLSOptions();\n const httpsServer = https.createServer(tlsOptions);\n this.server = new WebSocketServer({ server: httpsServer });\n\n httpsServer.listen(this.config.port, () => {\n const addr = httpsServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening (TLS enabled)');\n this.onServerReady(resolve);\n });\n } else {\n this.server = new WebSocketServer({ port: this.config.port });\n\n this.server.on('listening', () => {\n const addr = this.server!.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening');\n this.onServerReady(resolve);\n });\n }\n\n this.server?.on('connection', (ws, req) => {\n logger.info({ remoteAddress: req.socket.remoteAddress }, 'Incoming cluster connection');\n this.handleSocket(ws, false);\n });\n });\n }\n\n /** Called when server is ready - registers self and initiates peer connections */\n private onServerReady(resolve: (port: number) => void): void {\n // Add self to members with actual port\n this.members.set(this.config.nodeId, {\n nodeId: this.config.nodeId,\n host: this.config.host,\n port: this._actualPort,\n socket: null as any,\n isSelf: true\n });\n\n // Connect to peers after we know our port\n if (this.config.discovery === 'kubernetes' && this.config.serviceName) {\n this.startDiscovery();\n } else {\n this.connectToPeers();\n }\n\n resolve(this._actualPort);\n }\n\n public stop() {\n logger.info({ port: this.config.port }, 'Stopping Cluster Manager');\n\n // Stop heartbeat\n this.stopHeartbeat();\n\n // Stop failure detector\n this.failureDetector.stop();\n\n // Clear reconnect intervals\n for (const timeout of this.reconnectIntervals.values()) {\n clearTimeout(timeout);\n }\n this.reconnectIntervals.clear();\n if (this.discoveryTimer) {\n clearInterval(this.discoveryTimer);\n this.discoveryTimer = undefined;\n }\n this.pendingConnections.clear();\n\n // Close all peer connections\n for (const member of this.members.values()) {\n if (member.socket) {\n member.socket.terminate(); // Force close\n }\n }\n this.members.clear();\n\n // Close server\n if (this.server) {\n this.server.close();\n }\n }\n\n /**\n * Start sending heartbeats to all peers.\n */\n private startHeartbeat(): void {\n if (this.heartbeatTimer) return;\n\n const intervalMs = this.config.heartbeatIntervalMs ?? 1000;\n\n this.heartbeatTimer = setInterval(() => {\n this.sendHeartbeatToAll();\n }, intervalMs);\n\n // Also start failure detector\n this.failureDetector.start();\n\n logger.debug({ intervalMs }, 'Heartbeat started');\n }\n\n /**\n * Stop sending heartbeats.\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n /**\n * Send heartbeat to all connected peers.\n */\n private sendHeartbeatToAll(): void {\n for (const [nodeId, member] of this.members) {\n if (member.isSelf) continue;\n if (member.socket && member.socket.readyState === WebSocket.OPEN) {\n this.send(nodeId, 'HEARTBEAT', { timestamp: Date.now() });\n }\n }\n }\n\n /**\n * Handle incoming heartbeat from a peer.\n */\n private handleHeartbeat(senderId: string, _payload: { timestamp: number }): void {\n this.failureDetector.recordHeartbeat(senderId);\n }\n\n /**\n * Handle confirmed node failure.\n */\n private handleNodeFailure(nodeId: string): void {\n const member = this.members.get(nodeId);\n if (!member) return;\n\n logger.warn({ nodeId }, 'Removing failed node from cluster');\n\n // Close socket if still connected\n if (member.socket && member.socket.readyState !== WebSocket.CLOSED) {\n try {\n member.socket.terminate();\n } catch (e) {\n // Ignore close errors\n }\n }\n\n // Remove from members\n this.members.delete(nodeId);\n\n // Stop monitoring\n this.failureDetector.stopMonitoring(nodeId);\n\n // Emit memberLeft event\n this.emit('memberLeft', nodeId);\n }\n\n private connectToPeers() {\n for (const peer of this.config.peers) {\n this.connectToPeer(peer);\n }\n }\n\n private startDiscovery() {\n const runDiscovery = async () => {\n if (!this.config.serviceName) return;\n\n try {\n const addresses = await dns.promises.resolve4(this.config.serviceName);\n logger.debug({ addresses, serviceName: this.config.serviceName }, 'DNS discovery results');\n\n for (const ip of addresses) {\n // Use actual port if available (likely matching K8s config), fallback to config port\n const targetPort = this._actualPort || this.config.port;\n const peerAddress = `${ip}:${targetPort}`;\n // Attempt to connect. connectToPeer handles dupes and self-checks (via handshake eventually)\n this.connectToPeer(peerAddress);\n }\n } catch (err: any) {\n logger.error({ err: err.message, serviceName: this.config.serviceName }, 'DNS discovery failed');\n }\n };\n\n logger.info({ serviceName: this.config.serviceName }, 'Starting Kubernetes DNS discovery');\n runDiscovery();\n // Default to 10s if not specified, to be less aggressive\n this.discoveryTimer = setInterval(runDiscovery, this.config.discoveryInterval || 10000);\n }\n\n private scheduleReconnect(peerAddress: string, attempt: number = 0) {\n if (this.reconnectIntervals.has(peerAddress)) return;\n\n // Exponential backoff: 5s, 10s, 20s, 40s, 60s (max)\n const delay = Math.min(5000 * Math.pow(2, attempt), 60000);\n\n const timeout = setTimeout(() => {\n this.reconnectIntervals.delete(peerAddress);\n // Pass next attempt number\n this.connectToPeerWithBackoff(peerAddress, attempt + 1);\n }, delay);\n\n this.reconnectIntervals.set(peerAddress, timeout);\n }\n\n // Helper to track attempts\n private connectToPeerWithBackoff(peerAddress: string, attempt: number) {\n // We need to modify connectToPeer to accept attempt or create a wrapper.\n // To keep it simple without changing signature of connectToPeer everywhere,\n // we'll just call connectToPeer and let it fail -> scheduleReconnect -> increment attempt.\n // But connectToPeer logic needs to pass the attempt to scheduleReconnect on failure.\n // Refactoring connectToPeer to take optional attempt param.\n this._connectToPeerInternal(peerAddress, attempt);\n }\n\n private connectToPeer(peerAddress: string) {\n this._connectToPeerInternal(peerAddress, 0);\n }\n\n private _connectToPeerInternal(peerAddress: string, attempt: number) {\n if (this.pendingConnections.has(peerAddress)) return;\n\n // Check if already connected\n for (const member of this.members.values()) {\n if (`${member.host}:${member.port}` === peerAddress) return;\n }\n\n // PREVENT LOOP: ... (omitted comments)\n\n logger.info({ peerAddress, attempt, tls: !!this.config.tls?.enabled }, 'Connecting to peer');\n this.pendingConnections.add(peerAddress);\n\n try {\n let ws: WebSocket;\n\n if (this.config.tls?.enabled) {\n // Secure WebSocket connection\n const protocol = 'wss://';\n const wsOptions: WsClientOptions = {\n rejectUnauthorized: this.config.tls.rejectUnauthorized !== false,\n };\n\n // mTLS: Provide client certificate\n if (this.config.tls.certPath && this.config.tls.keyPath) {\n wsOptions.cert = readFileSync(this.config.tls.certPath);\n wsOptions.key = readFileSync(this.config.tls.keyPath);\n\n if (this.config.tls.passphrase) {\n wsOptions.passphrase = this.config.tls.passphrase;\n }\n }\n\n // CA for peer verification\n if (this.config.tls.caCertPath) {\n wsOptions.ca = readFileSync(this.config.tls.caCertPath);\n }\n\n ws = new WebSocket(`${protocol}${peerAddress}`, wsOptions);\n } else {\n // Plain WebSocket (development)\n ws = new WebSocket(`ws://${peerAddress}`);\n }\n\n ws.on('open', () => {\n this.pendingConnections.delete(peerAddress);\n logger.info({ peerAddress }, 'Connected to peer');\n // Reset backoff on success\n this.handleSocket(ws, true, peerAddress);\n });\n\n ws.on('error', (err) => {\n logger.error({ peerAddress, err: err.message }, 'Connection error to peer');\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n });\n\n ws.on('close', () => {\n this.pendingConnections.delete(peerAddress);\n });\n\n } catch (e) {\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n }\n }\n\n private handleSocket(ws: WebSocket, initiated: boolean, peerAddress?: string) {\n // Handshake: Send my NodeID with actual port (not config port which may be 0)\n const helloMsg: ClusterMessage = {\n type: 'HELLO',\n senderId: this.config.nodeId,\n payload: {\n host: this.config.host,\n port: this._actualPort || this.config.port\n }\n };\n ws.send(JSON.stringify(helloMsg));\n\n let remoteNodeId: string | null = null;\n\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as ClusterMessage;\n\n if (msg.type === 'HELLO') {\n remoteNodeId = msg.senderId;\n const { host, port } = msg.payload;\n logger.info({ nodeId: remoteNodeId, host, port }, 'Peer identified');\n\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n\n const myId = this.config.nodeId;\n const otherId = remoteNodeId;\n\n // Determine who initiated this specific socket\n const initiatorId = initiated ? myId : otherId;\n const receiverId = initiated ? otherId : myId;\n\n /*\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n // \n // DISABLED: This strict rule prevents High-ID nodes from joining Low-ID seeds (common pattern).\n // We only use this for duplicate resolution now.\n \n if (initiatorId >= receiverId) {\n logger.info({ initiatorId, receiverId }, 'Dropping connection (Low-ID Initiator Policy)');\n try {\n ws.close();\n } catch(e) {}\n return;\n }\n */\n\n // If we get here, this is a VALID connection.\n // Check if we somehow already have a connection\n if (this.members.has(remoteNodeId)) {\n logger.warn({ nodeId: remoteNodeId }, 'Duplicate valid connection. Replacing.');\n // In a real production system, we should use the Tie-Breaker here to decide which one to keep\n // to avoid split-brain socket usage.\n // For now, 'Replacing' means Last-Write-Wins on the connection slot.\n }\n\n this.members.set(remoteNodeId, {\n nodeId: remoteNodeId,\n host,\n port,\n socket: ws,\n isSelf: false\n });\n\n // Start monitoring this node for failures\n this.failureDetector.startMonitoring(remoteNodeId);\n\n // Start heartbeat if not already started\n this.startHeartbeat();\n\n this.emit('memberJoined', remoteNodeId);\n } else if (msg.type === 'HEARTBEAT') {\n // Handle incoming heartbeat\n if (remoteNodeId) {\n this.handleHeartbeat(remoteNodeId, msg.payload);\n }\n } else {\n this.emit('message', msg);\n }\n } catch (err) {\n logger.error({ err }, 'Failed to parse cluster message');\n }\n });\n\n ws.on('close', () => {\n if (remoteNodeId) {\n // Only handle disconnect if this was the ACTIVE socket\n // This prevents \"duplicate connection\" cleanup from killing the valid session\n const current = this.members.get(remoteNodeId);\n if (current && current.socket === ws) {\n logger.info({ nodeId: remoteNodeId }, 'Peer disconnected');\n this.members.delete(remoteNodeId);\n\n // Stop monitoring this node\n this.failureDetector.stopMonitoring(remoteNodeId);\n\n this.emit('memberLeft', remoteNodeId);\n\n // If we initiated, we should try to reconnect\n if (initiated && peerAddress) {\n // Start with 0 attempt on fresh disconnect?\n // Or maybe we should consider this a failure and backoff?\n // Let's restart with 0 for now as it might be a temp network blip\n this.scheduleReconnect(peerAddress, 0);\n }\n } else {\n // console.log(`Ignored close from stale/duplicate socket for ${remoteNodeId}`);\n }\n }\n });\n }\n\n public send(nodeId: string, type: ClusterMessage['type'], payload: any) {\n const member = this.members.get(nodeId);\n if (member && member.socket && member.socket.readyState === WebSocket.OPEN) {\n const msg: ClusterMessage = {\n type,\n senderId: this.config.nodeId,\n payload\n };\n member.socket.send(JSON.stringify(msg));\n } else {\n logger.warn({ nodeId }, 'Cannot send to node: not connected');\n }\n }\n\n public sendToNode(nodeId: string, message: any) {\n this.send(nodeId, 'OP_FORWARD', message);\n }\n\n public getMembers(): string[] {\n return Array.from(this.members.keys());\n }\n\n public isLocal(nodeId: string): boolean {\n return nodeId === this.config.nodeId;\n }\n\n private buildClusterTLSOptions(): https.ServerOptions {\n const config = this.config.tls!;\n\n const options: https.ServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.requireClientCert) {\n options.requestCert = true;\n options.rejectUnauthorized = true;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n\n","/**\n * FailureDetector - Phi Accrual Failure Detector\n *\n * Implements the Phi Accrual Failure Detection algorithm for distributed systems.\n * Based on the paper: \"The φ Accrual Failure Detector\" by Hayashibara et al.\n *\n * The detector provides a suspicion level (phi) rather than binary alive/dead status,\n * allowing the application to make decisions based on configurable thresholds.\n *\n * Hazelcast equivalent: com.hazelcast.internal.cluster.fd.PhiAccrualFailureDetector\n */\n\nimport { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface FailureDetectorConfig {\n /** Interval between heartbeat checks (ms). Default: 1000 */\n heartbeatIntervalMs: number;\n /** Time after which a node is suspected if no heartbeat received (ms). Default: 5000 */\n suspicionTimeoutMs: number;\n /** Time after suspicion before confirming failure (ms). Default: 10000 */\n confirmationTimeoutMs: number;\n /** Phi threshold above which a node is considered suspected. Default: 8 */\n phiThreshold: number;\n /** Minimum samples required for accurate phi calculation. Default: 10 */\n minSamples: number;\n /** Maximum samples to keep in history. Default: 100 */\n maxSamples: number;\n /** Initial heartbeat interval estimate (ms). Default: 1000 */\n initialHeartbeatIntervalMs: number;\n}\n\nexport const DEFAULT_FAILURE_DETECTOR_CONFIG: FailureDetectorConfig = {\n heartbeatIntervalMs: 1000,\n suspicionTimeoutMs: 5000,\n confirmationTimeoutMs: 10000,\n phiThreshold: 8,\n minSamples: 10,\n maxSamples: 100,\n initialHeartbeatIntervalMs: 1000,\n};\n\nexport interface NodeState {\n /** Last heartbeat timestamp */\n lastHeartbeat: number;\n /** Heartbeat interval history for phi calculation */\n intervalHistory: number[];\n /** Whether node is currently suspected */\n isSuspected: boolean;\n /** Timestamp when suspicion started */\n suspicionStartTime?: number;\n /** Whether failure has been confirmed */\n isConfirmedFailed: boolean;\n}\n\nexport interface FailureDetectorEvents {\n nodeSuspected: { nodeId: string; phi: number };\n nodeRecovered: { nodeId: string };\n nodeConfirmedFailed: { nodeId: string };\n}\n\nexport class FailureDetector extends EventEmitter {\n private config: FailureDetectorConfig;\n private nodeStates: Map<string, NodeState> = new Map();\n private monitoringNodes: Set<string> = new Set();\n private checkTimer?: ReturnType<typeof setInterval>;\n private confirmationTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private started = false;\n\n constructor(config: Partial<FailureDetectorConfig> = {}) {\n super();\n this.config = { ...DEFAULT_FAILURE_DETECTOR_CONFIG, ...config };\n }\n\n /**\n * Start the failure detector monitoring loop.\n */\n start(): void {\n if (this.started) return;\n this.started = true;\n\n this.checkTimer = setInterval(() => {\n this.checkAllNodes();\n }, this.config.heartbeatIntervalMs);\n\n logger.info({ config: this.config }, 'FailureDetector started');\n }\n\n /**\n * Stop the failure detector and clean up.\n */\n stop(): void {\n if (!this.started) return;\n this.started = false;\n\n if (this.checkTimer) {\n clearInterval(this.checkTimer);\n this.checkTimer = undefined;\n }\n\n // Clear all confirmation timers\n for (const timer of this.confirmationTimers.values()) {\n clearTimeout(timer);\n }\n this.confirmationTimers.clear();\n\n logger.info('FailureDetector stopped');\n }\n\n /**\n * Start monitoring a node.\n */\n startMonitoring(nodeId: string): void {\n if (this.monitoringNodes.has(nodeId)) return;\n\n this.monitoringNodes.add(nodeId);\n this.nodeStates.set(nodeId, {\n lastHeartbeat: Date.now(),\n intervalHistory: [],\n isSuspected: false,\n isConfirmedFailed: false,\n });\n\n logger.debug({ nodeId }, 'Started monitoring node');\n }\n\n /**\n * Stop monitoring a node.\n */\n stopMonitoring(nodeId: string): void {\n this.monitoringNodes.delete(nodeId);\n this.nodeStates.delete(nodeId);\n\n const timer = this.confirmationTimers.get(nodeId);\n if (timer) {\n clearTimeout(timer);\n this.confirmationTimers.delete(nodeId);\n }\n\n logger.debug({ nodeId }, 'Stopped monitoring node');\n }\n\n /**\n * Record a heartbeat from a node.\n * This updates the node's state and clears any suspicion.\n */\n recordHeartbeat(nodeId: string): void {\n const state = this.nodeStates.get(nodeId);\n if (!state) {\n // Auto-start monitoring if not already\n this.startMonitoring(nodeId);\n return;\n }\n\n const now = Date.now();\n const interval = now - state.lastHeartbeat;\n\n // Update interval history\n state.intervalHistory.push(interval);\n if (state.intervalHistory.length > this.config.maxSamples) {\n state.intervalHistory.shift();\n }\n\n state.lastHeartbeat = now;\n\n // If was suspected, clear suspicion\n if (state.isSuspected) {\n state.isSuspected = false;\n state.suspicionStartTime = undefined;\n state.isConfirmedFailed = false;\n\n // Cancel confirmation timer\n const timer = this.confirmationTimers.get(nodeId);\n if (timer) {\n clearTimeout(timer);\n this.confirmationTimers.delete(nodeId);\n }\n\n this.emit('nodeRecovered', { nodeId });\n logger.info({ nodeId }, 'Node recovered');\n }\n }\n\n /**\n * Check all monitored nodes for failure.\n */\n private checkAllNodes(): void {\n for (const nodeId of this.monitoringNodes) {\n const phi = this.calculatePhi(nodeId);\n const state = this.nodeStates.get(nodeId);\n\n if (!state) continue;\n\n if (phi > this.config.phiThreshold) {\n if (!state.isSuspected) {\n state.isSuspected = true;\n state.suspicionStartTime = Date.now();\n\n this.emit('nodeSuspected', { nodeId, phi });\n logger.warn({ nodeId, phi }, 'Node suspected');\n\n // Schedule confirmation\n this.scheduleConfirmation(nodeId);\n }\n }\n }\n }\n\n /**\n * Schedule failure confirmation after suspicion timeout.\n */\n private scheduleConfirmation(nodeId: string): void {\n // Cancel existing timer if any\n const existingTimer = this.confirmationTimers.get(nodeId);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n const timer = setTimeout(() => {\n this.confirmFailure(nodeId);\n }, this.config.confirmationTimeoutMs);\n\n this.confirmationTimers.set(nodeId, timer);\n }\n\n /**\n * Confirm node failure after confirmation timeout.\n */\n private confirmFailure(nodeId: string): void {\n const state = this.nodeStates.get(nodeId);\n if (!state) return;\n\n // Only confirm if still suspected\n if (state.isSuspected && !state.isConfirmedFailed) {\n state.isConfirmedFailed = true;\n this.emit('nodeConfirmedFailed', { nodeId });\n logger.error({ nodeId }, 'Node failure confirmed');\n }\n\n this.confirmationTimers.delete(nodeId);\n }\n\n /**\n * Calculate the phi value for a node using the Phi Accrual algorithm.\n *\n * Phi = -log10(P_later(t_now - t_last))\n *\n * where P_later is the probability that a heartbeat will arrive later than expected.\n */\n calculatePhi(nodeId: string): number {\n const state = this.nodeStates.get(nodeId);\n if (!state) return 0;\n\n const now = Date.now();\n const timeSinceLastHeartbeat = now - state.lastHeartbeat;\n\n // If we don't have enough samples, use simple timeout-based detection\n if (state.intervalHistory.length < this.config.minSamples) {\n // Simple fallback: phi increases linearly with time since last heartbeat\n const expectedInterval = this.config.initialHeartbeatIntervalMs;\n return timeSinceLastHeartbeat / expectedInterval;\n }\n\n // Calculate mean and variance of intervals\n const mean = this.calculateMean(state.intervalHistory);\n const variance = this.calculateVariance(state.intervalHistory, mean);\n const stdDev = Math.sqrt(variance);\n\n // Phi Accrual formula using normal distribution approximation\n // Phi = -log10(1 - CDF(timeSinceLastHeartbeat))\n // For simplicity, we use: phi ≈ (t - μ) / σ for t > μ\n if (timeSinceLastHeartbeat <= mean) {\n return 0; // No suspicion if within expected interval\n }\n\n // Calculate how many standard deviations away we are\n const deviations = stdDev > 0 ? (timeSinceLastHeartbeat - mean) / stdDev : 0;\n\n // Convert to phi using exponential distribution approximation\n // This gives phi values in a similar range to Hazelcast (0-16+)\n const phi = Math.max(0, deviations);\n\n return phi;\n }\n\n /**\n * Calculate mean of an array of numbers.\n */\n private calculateMean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((sum, v) => sum + v, 0) / values.length;\n }\n\n /**\n * Calculate variance of an array of numbers.\n */\n private calculateVariance(values: number[], mean: number): number {\n if (values.length < 2) return 0;\n return values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length;\n }\n\n /**\n * Get list of currently suspected nodes.\n */\n getSuspectedNodes(): string[] {\n const suspected: string[] = [];\n for (const [nodeId, state] of this.nodeStates) {\n if (state.isSuspected) {\n suspected.push(nodeId);\n }\n }\n return suspected;\n }\n\n /**\n * Get list of confirmed failed nodes.\n */\n getConfirmedFailedNodes(): string[] {\n const failed: string[] = [];\n for (const [nodeId, state] of this.nodeStates) {\n if (state.isConfirmedFailed) {\n failed.push(nodeId);\n }\n }\n return failed;\n }\n\n /**\n * Check if a specific node is suspected.\n */\n isSuspected(nodeId: string): boolean {\n return this.nodeStates.get(nodeId)?.isSuspected ?? false;\n }\n\n /**\n * Check if a specific node's failure is confirmed.\n */\n isConfirmedFailed(nodeId: string): boolean {\n return this.nodeStates.get(nodeId)?.isConfirmedFailed ?? false;\n }\n\n /**\n * Get the current phi value for a node.\n */\n getPhi(nodeId: string): number {\n return this.calculatePhi(nodeId);\n }\n\n /**\n * Get all monitored nodes.\n */\n getMonitoredNodes(): string[] {\n return Array.from(this.monitoringNodes);\n }\n\n /**\n * Get metrics for monitoring.\n */\n getMetrics(): {\n monitoredNodes: number;\n suspectedNodes: number;\n confirmedFailedNodes: number;\n } {\n let suspectedCount = 0;\n let confirmedCount = 0;\n\n for (const state of this.nodeStates.values()) {\n if (state.isSuspected) suspectedCount++;\n if (state.isConfirmedFailed) confirmedCount++;\n }\n\n return {\n monitoredNodes: this.monitoringNodes.size,\n suspectedNodes: suspectedCount,\n confirmedFailedNodes: confirmedCount,\n };\n }\n}\n","import { EventEmitter } from 'events';\nimport { ClusterManager } from './ClusterManager';\nimport { MigrationManager } from './MigrationManager';\nimport {\n hashString,\n PartitionMap,\n PartitionInfo,\n NodeInfo,\n PartitionChange,\n MigrationConfig,\n MigrationStatus,\n PARTITION_COUNT,\n DEFAULT_BACKUP_COUNT,\n DEFAULT_MIGRATION_CONFIG,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport interface PartitionServiceEvents {\n 'rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;\n 'partitionMoved': (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => void;\n}\n\nexport interface PartitionServiceConfig {\n /** Enable gradual rebalancing (default: false for backward compatibility) */\n gradualRebalancing: boolean;\n /** Migration configuration */\n migration: Partial<MigrationConfig>;\n}\n\nexport const DEFAULT_PARTITION_SERVICE_CONFIG: PartitionServiceConfig = {\n gradualRebalancing: false,\n migration: DEFAULT_MIGRATION_CONFIG,\n};\n\nexport class PartitionService extends EventEmitter {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = PARTITION_COUNT;\n private readonly BACKUP_COUNT = DEFAULT_BACKUP_COUNT;\n\n // Phase 4: Version tracking for partition map\n private mapVersion: number = 0;\n private lastRebalanceTime: number = 0;\n\n // Phase 4 Task 03: Gradual rebalancing\n private config: PartitionServiceConfig;\n private migrationManager: MigrationManager | null = null;\n\n constructor(cluster: ClusterManager, config: Partial<PartitionServiceConfig> = {}) {\n super();\n this.cluster = cluster;\n this.config = {\n ...DEFAULT_PARTITION_SERVICE_CONFIG,\n ...config,\n };\n\n // Initialize migration manager if gradual rebalancing is enabled\n if (this.config.gradualRebalancing) {\n this.migrationManager = new MigrationManager(\n cluster,\n this,\n this.config.migration\n );\n\n // Forward migration events\n this.migrationManager.on('migrationComplete', (partitionId: number) => {\n logger.info({ partitionId }, 'Migration completed, updating ownership');\n });\n\n this.migrationManager.on('migrationFailed', (partitionId: number, error: Error) => {\n logger.error({ partitionId, error: error.message }, 'Migration failed');\n });\n }\n\n this.cluster.on('memberJoined', (nodeId: string) => this.onMembershipChange('JOIN', nodeId));\n this.cluster.on('memberLeft', (nodeId: string) => this.onMembershipChange('LEAVE', nodeId));\n\n // Initial rebalance (always immediate on startup)\n this.rebalance('REBALANCE');\n }\n\n /**\n * Handle membership change\n */\n private onMembershipChange(reason: 'JOIN' | 'LEAVE', nodeId: string): void {\n if (this.config.gradualRebalancing && this.migrationManager) {\n // Use gradual rebalancing\n this.rebalanceGradual(reason, nodeId);\n } else {\n // Use immediate rebalancing (original behavior)\n this.rebalance(reason, nodeId);\n }\n }\n\n public getPartitionId(key: string): number {\n // Use Math.abs to ensure positive partition ID\n return Math.abs(hashString(key)) % this.PARTITION_COUNT;\n }\n\n public getDistribution(key: string): PartitionDistribution {\n const pId = this.getPartitionId(key);\n return this.partitions.get(pId) || { \n owner: this.cluster.config.nodeId, \n backups: [] \n };\n }\n\n public getOwner(key: string): string {\n return this.getDistribution(key).owner;\n }\n\n public isLocalOwner(key: string): boolean {\n return this.getOwner(key) === this.cluster.config.nodeId;\n }\n\n public isLocalBackup(key: string): boolean {\n const dist = this.getDistribution(key);\n return dist.backups.includes(this.cluster.config.nodeId);\n }\n\n public isRelated(key: string): boolean {\n return this.isLocalOwner(key) || this.isLocalBackup(key);\n }\n\n // ============================================\n // Phase 4: Partition Map Methods\n // ============================================\n\n /**\n * Get current partition map version\n */\n public getMapVersion(): number {\n return this.mapVersion;\n }\n\n /**\n * Generate full PartitionMap for client consumption\n */\n public getPartitionMap(): PartitionMap {\n const nodes: NodeInfo[] = [];\n const partitions: PartitionInfo[] = [];\n\n // Build node info from cluster members\n for (const nodeId of this.cluster.getMembers()) {\n const isSelf = nodeId === this.cluster.config.nodeId;\n const host = isSelf ? this.cluster.config.host : 'unknown';\n const port = isSelf ? this.cluster.port : 0;\n\n nodes.push({\n nodeId,\n endpoints: {\n websocket: `ws://${host}:${port}`,\n },\n status: 'ACTIVE',\n });\n }\n\n // Build partition info\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const dist = this.partitions.get(i);\n if (dist) {\n partitions.push({\n partitionId: i,\n ownerNodeId: dist.owner,\n backupNodeIds: dist.backups,\n });\n }\n }\n\n return {\n version: this.mapVersion,\n partitionCount: this.PARTITION_COUNT,\n nodes,\n partitions,\n generatedAt: Date.now(),\n };\n }\n\n /**\n * Get partition info by ID\n */\n public getPartitionInfo(partitionId: number): PartitionInfo | null {\n const dist = this.partitions.get(partitionId);\n if (!dist) return null;\n\n return {\n partitionId,\n ownerNodeId: dist.owner,\n backupNodeIds: dist.backups,\n };\n }\n\n /**\n * Get owner node for a partition ID\n */\n public getPartitionOwner(partitionId: number): string | null {\n const dist = this.partitions.get(partitionId);\n return dist?.owner ?? null;\n }\n\n private rebalance(reason: 'REBALANCE' | 'FAILOVER' | 'JOIN' | 'LEAVE' = 'REBALANCE', triggerNodeId?: string) {\n // Store old partitions for change detection\n const oldPartitions = new Map(this.partitions);\n\n // this.cluster.getMembers() includes self (added in ClusterManager.start)\n let allMembers = this.cluster.getMembers().sort();\n\n // If no other members, include self\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n logger.info({ memberCount: allMembers.length, members: allMembers, reason }, 'Rebalancing partitions');\n\n const changes: PartitionChange[] = [];\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n\n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n // Track changes\n const oldDist = oldPartitions.get(i);\n if (oldDist && oldDist.owner !== owner) {\n changes.push({\n partitionId: i,\n previousOwner: oldDist.owner,\n newOwner: owner,\n reason,\n });\n }\n\n this.partitions.set(i, { owner, backups });\n }\n\n // Increment version if there were changes\n if (changes.length > 0 || this.mapVersion === 0) {\n this.mapVersion++;\n this.lastRebalanceTime = Date.now();\n\n logger.info({\n version: this.mapVersion,\n changesCount: changes.length,\n reason,\n }, 'Partition map updated');\n\n // Emit event for ServerCoordinator to broadcast to clients\n this.emit('rebalanced', this.getPartitionMap(), changes);\n }\n }\n\n // ============================================\n // Phase 4 Task 03: Gradual Rebalancing\n // ============================================\n\n /**\n * Perform gradual rebalancing using MigrationManager\n */\n private rebalanceGradual(reason: 'JOIN' | 'LEAVE', triggerNodeId: string): void {\n if (!this.migrationManager) {\n // Fall back to immediate rebalancing\n this.rebalance(reason, triggerNodeId);\n return;\n }\n\n // Store old distribution\n const oldDistribution = new Map(this.partitions);\n\n // Calculate new distribution\n let allMembers = this.cluster.getMembers().sort();\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n const newDistribution = new Map<number, PartitionDistribution>();\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n\n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n newDistribution.set(i, { owner, backups });\n }\n\n logger.info({ memberCount: allMembers.length, reason, triggerNodeId }, 'Planning gradual rebalance');\n\n // Plan migrations (MigrationManager will handle gradual transfer)\n this.migrationManager.planMigration(oldDistribution, newDistribution);\n\n // Update partition map immediately for routing purposes\n // Actual data migration happens in background\n for (const [partitionId, dist] of newDistribution) {\n this.partitions.set(partitionId, dist);\n }\n\n this.mapVersion++;\n this.lastRebalanceTime = Date.now();\n\n // Emit event for clients\n const changes: PartitionChange[] = [];\n for (const [partitionId, newDist] of newDistribution) {\n const oldDist = oldDistribution.get(partitionId);\n if (oldDist && oldDist.owner !== newDist.owner) {\n changes.push({\n partitionId,\n previousOwner: oldDist.owner,\n newOwner: newDist.owner,\n reason,\n });\n }\n }\n\n this.emit('rebalanced', this.getPartitionMap(), changes);\n }\n\n /**\n * Set partition owner (called after migration completes)\n */\n public setOwner(partitionId: number, nodeId: string): void {\n const partition = this.partitions.get(partitionId);\n if (!partition) return;\n\n const previousOwner = partition.owner;\n if (previousOwner === nodeId) return; // No change\n\n partition.owner = nodeId;\n this.mapVersion++;\n\n logger.info({ partitionId, previousOwner, newOwner: nodeId, version: this.mapVersion }, 'Partition owner updated');\n\n this.emit('partitionMoved', {\n partitionId,\n previousOwner,\n newOwner: nodeId,\n version: this.mapVersion,\n });\n }\n\n /**\n * Get backups for a partition\n */\n public getBackups(partitionId: number): string[] {\n const dist = this.partitions.get(partitionId);\n return dist?.backups ?? [];\n }\n\n /**\n * Get migration status\n */\n public getMigrationStatus(): MigrationStatus | null {\n return this.migrationManager?.getStatus() ?? null;\n }\n\n /**\n * Check if partition is currently migrating\n */\n public isMigrating(partitionId: number): boolean {\n return this.migrationManager?.isActive(partitionId) ?? false;\n }\n\n /**\n * Check if any partition is currently migrating\n */\n public isRebalancing(): boolean {\n const status = this.getMigrationStatus();\n return status?.inProgress ?? false;\n }\n\n /**\n * Get MigrationManager for configuration\n */\n public getMigrationManager(): MigrationManager | null {\n return this.migrationManager;\n }\n\n /**\n * Cancel all migrations\n */\n public async cancelMigrations(): Promise<void> {\n if (this.migrationManager) {\n await this.migrationManager.cancelAll();\n }\n }\n}\n","/**\n * MigrationManager - Manages gradual partition rebalancing\n *\n * Phase 4 Task 03: Parallel Partition Sync\n *\n * Features:\n * - Gradual rebalancing with configurable batch size\n * - State machine for migration lifecycle\n * - Backpressure via chunk acknowledgments\n * - Retry logic for failed migrations\n * - Metrics and observability\n */\n\nimport { EventEmitter } from 'events';\nimport {\n PartitionState,\n PartitionMigration,\n MigrationConfig,\n MigrationStatus,\n MigrationMetrics,\n DEFAULT_MIGRATION_CONFIG,\n PartitionChange,\n serialize,\n} from '@topgunbuild/core';\nimport { xxhash64AsNumber, createXxHash64State } from '@topgunbuild/native';\nimport { ClusterManager, ClusterMessage } from './ClusterManager';\nimport { PartitionService, PartitionDistribution } from './PartitionService';\nimport { logger } from '../utils/logger';\n\nexport interface IncomingMigration {\n sourceNode: string;\n chunks: Uint8Array[];\n expectedSize: number;\n receivedSize: number;\n startTime: number;\n}\n\nexport interface MigrationManagerEvents {\n 'migrationPlanned': (info: { total: number }) => void;\n 'batchStarted': (info: { count: number; remaining: number }) => void;\n 'migrationProgress': (migration: PartitionMigration) => void;\n 'migrationComplete': (partitionId: number) => void;\n 'migrationFailed': (partitionId: number, error: Error) => void;\n 'error': (error: Error) => void;\n}\n\nexport class MigrationManager extends EventEmitter {\n private readonly config: MigrationConfig;\n private readonly clusterManager: ClusterManager;\n private readonly partitionService: PartitionService;\n\n // Active outgoing migrations (this node is source)\n private activeMigrations: Map<number, PartitionMigration> = new Map();\n // Queue of migrations to process\n private migrationQueue: PartitionMigration[] = [];\n // Incoming migrations (this node is target)\n private incomingMigrations: Map<number, IncomingMigration> = new Map();\n // Pending chunk acknowledgments\n private pendingChunkAcks: Map<string, { resolve: () => void; reject: (err: Error) => void; timeout: ReturnType<typeof setTimeout> }> = new Map();\n // Pending verification results\n private pendingVerifications: Map<number, { resolve: (success: boolean) => void; timeout: ReturnType<typeof setTimeout> }> = new Map();\n\n // Metrics tracking\n private metrics: MigrationMetrics = {\n migrationsStarted: 0,\n migrationsCompleted: 0,\n migrationsFailed: 0,\n chunksTransferred: 0,\n bytesTransferred: 0,\n activeMigrations: 0,\n queuedMigrations: 0,\n };\n\n // Batch processing timer\n private batchTimer: ReturnType<typeof setInterval> | null = null;\n\n // Data collection callback (injected from ServerCoordinator)\n private dataCollector: ((partitionId: number) => Promise<Uint8Array[]>) | null = null;\n // Data storage callback (injected from ServerCoordinator)\n private dataStorer: ((partitionId: number, data: Uint8Array[]) => Promise<void>) | null = null;\n\n constructor(\n clusterManager: ClusterManager,\n partitionService: PartitionService,\n config: Partial<MigrationConfig> = {}\n ) {\n super();\n this.clusterManager = clusterManager;\n this.partitionService = partitionService;\n this.config = {\n ...DEFAULT_MIGRATION_CONFIG,\n ...config,\n };\n\n this.setupMessageHandlers();\n }\n\n // ============================================\n // Configuration\n // ============================================\n\n /**\n * Set the data collector callback\n * Called to collect all records for a partition before migration\n */\n public setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void {\n this.dataCollector = collector;\n }\n\n /**\n * Set the data storer callback\n * Called to store received records after successful migration\n */\n public setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void {\n this.dataStorer = storer;\n }\n\n // ============================================\n // Migration Planning\n // ============================================\n\n /**\n * Plan migration for topology change\n */\n public planMigration(\n oldDistribution: Map<number, PartitionDistribution>,\n newDistribution: Map<number, PartitionDistribution>\n ): void {\n const migrations: PartitionMigration[] = [];\n\n for (const [partitionId, newDist] of newDistribution) {\n const oldDist = oldDistribution.get(partitionId);\n const oldOwner = oldDist?.owner ?? this.clusterManager.config.nodeId;\n const newOwner = newDist.owner;\n\n // Only plan migration if owner changed AND we are the source\n if (oldOwner !== newOwner && oldOwner === this.clusterManager.config.nodeId) {\n migrations.push({\n partitionId,\n state: PartitionState.STABLE,\n sourceNode: oldOwner,\n targetNode: newOwner,\n startTime: 0,\n bytesTransferred: 0,\n totalBytes: 0,\n retryCount: 0,\n });\n }\n }\n\n // Sort by partition ID for deterministic ordering\n migrations.sort((a, b) => a.partitionId - b.partitionId);\n\n this.migrationQueue = migrations;\n this.metrics.queuedMigrations = migrations.length;\n\n logger.info({ total: migrations.length }, 'Migration planned');\n this.emit('migrationPlanned', { total: migrations.length });\n\n // Start processing if we have migrations\n if (migrations.length > 0) {\n this.startBatchProcessing();\n }\n }\n\n /**\n * Start batch processing timer\n */\n private startBatchProcessing(): void {\n if (this.batchTimer) return;\n\n // Process first batch immediately\n this.startNextBatch().catch(err => {\n logger.error({ error: err }, 'Failed to start first migration batch');\n this.emit('error', err);\n });\n\n // Then process batches on interval\n this.batchTimer = setInterval(() => {\n this.startNextBatch().catch(err => {\n logger.error({ error: err }, 'Failed to start migration batch');\n this.emit('error', err);\n });\n }, this.config.batchIntervalMs);\n }\n\n /**\n * Stop batch processing\n */\n private stopBatchProcessing(): void {\n if (this.batchTimer) {\n clearInterval(this.batchTimer);\n this.batchTimer = null;\n }\n }\n\n /**\n * Start next batch of migrations\n */\n public async startNextBatch(): Promise<void> {\n // Check if we have capacity\n if (this.activeMigrations.size >= this.config.parallelTransfers) {\n return; // Wait for current batch to complete\n }\n\n const slotsAvailable = this.config.parallelTransfers - this.activeMigrations.size;\n const batch = this.migrationQueue.splice(0, Math.min(slotsAvailable, this.config.batchSize));\n\n if (batch.length === 0) {\n // No more migrations, stop timer\n if (this.migrationQueue.length === 0 && this.activeMigrations.size === 0) {\n this.stopBatchProcessing();\n }\n return;\n }\n\n for (const migration of batch) {\n migration.state = PartitionState.MIGRATING;\n migration.startTime = Date.now();\n this.activeMigrations.set(migration.partitionId, migration);\n this.metrics.migrationsStarted++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n this.metrics.queuedMigrations = this.migrationQueue.length;\n\n // Start async migration\n this.startPartitionMigration(migration).catch(error => {\n this.onMigrationFailed(migration.partitionId, error);\n });\n }\n\n logger.info({ count: batch.length, remaining: this.migrationQueue.length }, 'Batch started');\n this.emit('batchStarted', { count: batch.length, remaining: this.migrationQueue.length });\n }\n\n // ============================================\n // Migration Execution\n // ============================================\n\n /**\n * Start migration for a single partition\n */\n private async startPartitionMigration(migration: PartitionMigration): Promise<void> {\n const { partitionId, targetNode } = migration;\n\n logger.info({ partitionId, targetNode }, 'Starting partition migration');\n\n // 1. Collect all records for partition\n let records: Uint8Array[];\n if (this.dataCollector) {\n records = await this.dataCollector(partitionId);\n } else {\n // No data collector set, send empty migration\n records = [];\n }\n\n migration.totalBytes = records.reduce((sum, r) => sum + r.length, 0);\n\n // 2. Send start message\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_START',\n payload: {\n partitionId,\n sourceNode: this.clusterManager.config.nodeId,\n estimatedSize: migration.totalBytes,\n },\n },\n });\n\n // 3. Stream chunks with backpressure\n const chunks = this.chunkify(records);\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n const checksum = this.calculateChecksum(chunk);\n\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_CHUNK',\n payload: {\n partitionId,\n chunkIndex: i,\n totalChunks: chunks.length,\n data: Array.from(chunk), // Convert Uint8Array to array for JSON serialization\n checksum,\n },\n },\n });\n\n // Wait for acknowledgment (backpressure)\n await this.waitForChunkAck(partitionId, i);\n\n migration.bytesTransferred += chunk.length;\n this.metrics.chunksTransferred++;\n this.metrics.bytesTransferred += chunk.length;\n this.emit('migrationProgress', migration);\n }\n\n // 4. Send completion and wait for verification\n const fullChecksum = this.calculatePartitionChecksum(records);\n\n migration.state = PartitionState.SYNC;\n\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_COMPLETE',\n payload: {\n partitionId,\n totalRecords: records.length,\n checksum: fullChecksum,\n },\n },\n });\n\n // 5. Wait for verification\n const verified = await this.waitForVerification(partitionId);\n\n if (verified) {\n await this.onMigrationComplete(partitionId);\n } else {\n throw new Error(`Migration verification failed for partition ${partitionId}`);\n }\n }\n\n /**\n * Split records into chunks\n */\n private chunkify(records: Uint8Array[]): Uint8Array[] {\n const chunks: Uint8Array[] = [];\n let currentChunk: number[] = [];\n let currentSize = 0;\n\n for (const record of records) {\n // Add record length prefix (4 bytes)\n const lengthPrefix = new Uint8Array(4);\n new DataView(lengthPrefix.buffer).setUint32(0, record.length, true);\n\n currentChunk.push(...lengthPrefix, ...record);\n currentSize += 4 + record.length;\n\n if (currentSize >= this.config.transferChunkSize) {\n chunks.push(new Uint8Array(currentChunk));\n currentChunk = [];\n currentSize = 0;\n }\n }\n\n // Add remaining data as last chunk\n if (currentChunk.length > 0) {\n chunks.push(new Uint8Array(currentChunk));\n }\n\n // Ensure at least one chunk (even if empty)\n if (chunks.length === 0) {\n chunks.push(new Uint8Array(0));\n }\n\n return chunks;\n }\n\n /**\n * Calculate checksum for a chunk using native xxhash\n */\n private calculateChecksum(data: Uint8Array): string {\n return String(xxhash64AsNumber(data));\n }\n\n /**\n * Calculate checksum for all partition records using streaming xxhash\n */\n private calculatePartitionChecksum(records: Uint8Array[]): string {\n const state = createXxHash64State();\n for (const record of records) {\n state.update(record);\n }\n return String(state.digestAsNumber());\n }\n\n /**\n * Wait for chunk acknowledgment\n */\n private waitForChunkAck(partitionId: number, chunkIndex: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const key = `${partitionId}:${chunkIndex}`;\n\n const timeout = setTimeout(() => {\n this.pendingChunkAcks.delete(key);\n reject(new Error(`Chunk ack timeout for partition ${partitionId}, chunk ${chunkIndex}`));\n }, this.config.syncTimeoutMs);\n\n this.pendingChunkAcks.set(key, { resolve, reject, timeout });\n });\n }\n\n /**\n * Wait for migration verification\n */\n private waitForVerification(partitionId: number): Promise<boolean> {\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n this.pendingVerifications.delete(partitionId);\n resolve(false); // Verification timed out\n }, this.config.syncTimeoutMs);\n\n this.pendingVerifications.set(partitionId, { resolve, timeout });\n });\n }\n\n // ============================================\n // Migration Completion\n // ============================================\n\n /**\n * Handle successful migration completion\n */\n private async onMigrationComplete(partitionId: number): Promise<void> {\n const migration = this.activeMigrations.get(partitionId);\n if (!migration) return;\n\n migration.state = PartitionState.STABLE;\n this.activeMigrations.delete(partitionId);\n\n this.metrics.migrationsCompleted++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.info({\n partitionId,\n duration: Date.now() - migration.startTime,\n bytesTransferred: migration.bytesTransferred,\n }, 'Migration completed');\n\n this.emit('migrationComplete', partitionId);\n }\n\n /**\n * Handle migration failure\n */\n private async onMigrationFailed(partitionId: number, error: Error): Promise<void> {\n const migration = this.activeMigrations.get(partitionId);\n if (!migration) return;\n\n migration.retryCount++;\n\n if (migration.retryCount <= this.config.maxRetries) {\n // Requeue for retry\n migration.state = PartitionState.STABLE;\n migration.bytesTransferred = 0;\n this.activeMigrations.delete(partitionId);\n this.migrationQueue.unshift(migration); // Add to front of queue\n this.metrics.queuedMigrations = this.migrationQueue.length;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.warn({\n partitionId,\n retryCount: migration.retryCount,\n error: error.message,\n }, 'Migration failed, will retry');\n } else {\n // Max retries exceeded\n migration.state = PartitionState.FAILED;\n this.activeMigrations.delete(partitionId);\n this.metrics.migrationsFailed++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.error({\n partitionId,\n retryCount: migration.retryCount,\n error: error.message,\n }, 'Migration failed permanently');\n\n this.emit('migrationFailed', partitionId, error);\n }\n }\n\n // ============================================\n // Incoming Migration Handlers (Target Node)\n // ============================================\n\n /**\n * Handle MIGRATION_START message\n */\n private handleMigrationStart(payload: { partitionId: number; sourceNode: string; estimatedSize: number }): void {\n const { partitionId, sourceNode, estimatedSize } = payload;\n\n logger.info({ partitionId, sourceNode, estimatedSize }, 'Receiving migration');\n\n this.incomingMigrations.set(partitionId, {\n sourceNode,\n chunks: [],\n expectedSize: estimatedSize,\n receivedSize: 0,\n startTime: Date.now(),\n });\n }\n\n /**\n * Handle MIGRATION_CHUNK message\n */\n private handleMigrationChunk(payload: {\n partitionId: number;\n chunkIndex: number;\n totalChunks: number;\n data: number[];\n checksum: string;\n }): void {\n const { partitionId, chunkIndex, data, checksum } = payload;\n const incoming = this.incomingMigrations.get(partitionId);\n\n if (!incoming) {\n logger.warn({ partitionId, chunkIndex }, 'Received chunk for unknown migration');\n return;\n }\n\n const chunkData = new Uint8Array(data);\n\n // Verify chunk checksum\n const actualChecksum = this.calculateChecksum(chunkData);\n const success = actualChecksum === checksum;\n\n if (success) {\n // Store chunk\n incoming.chunks[chunkIndex] = chunkData;\n incoming.receivedSize += chunkData.length;\n } else {\n logger.warn({ partitionId, chunkIndex, expected: checksum, actual: actualChecksum }, 'Chunk checksum mismatch');\n }\n\n // Send acknowledgment\n this.clusterManager.send(incoming.sourceNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_CHUNK_ACK',\n payload: {\n partitionId,\n chunkIndex,\n success,\n },\n },\n });\n }\n\n /**\n * Handle MIGRATION_COMPLETE message\n */\n private async handleMigrationComplete(payload: {\n partitionId: number;\n totalRecords: number;\n checksum: string;\n }): Promise<void> {\n const { partitionId, totalRecords, checksum } = payload;\n const incoming = this.incomingMigrations.get(partitionId);\n\n if (!incoming) {\n logger.warn({ partitionId }, 'Received complete for unknown migration');\n return;\n }\n\n // Reassemble data\n const allData = this.reassemble(incoming.chunks);\n const records = this.deserializeRecords(allData);\n\n // Verify checksum\n const actualChecksum = this.calculatePartitionChecksum(records);\n const checksumMatch = actualChecksum === checksum;\n const success = checksumMatch && records.length === totalRecords;\n\n if (success && this.dataStorer) {\n // Store records\n await this.dataStorer(partitionId, records);\n }\n\n logger.info({\n partitionId,\n duration: Date.now() - incoming.startTime,\n records: records.length,\n checksumMatch,\n }, 'Migration received');\n\n // Send verification result\n this.clusterManager.send(incoming.sourceNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_VERIFY',\n payload: {\n partitionId,\n success,\n checksumMatch,\n },\n },\n });\n\n this.incomingMigrations.delete(partitionId);\n }\n\n /**\n * Handle MIGRATION_CHUNK_ACK message\n */\n private handleMigrationChunkAck(payload: {\n partitionId: number;\n chunkIndex: number;\n success: boolean;\n }): void {\n const { partitionId, chunkIndex, success } = payload;\n const key = `${partitionId}:${chunkIndex}`;\n const pending = this.pendingChunkAcks.get(key);\n\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingChunkAcks.delete(key);\n\n if (success) {\n pending.resolve();\n } else {\n pending.reject(new Error(`Chunk ${chunkIndex} rejected by target`));\n }\n }\n }\n\n /**\n * Handle MIGRATION_VERIFY message\n */\n private handleMigrationVerify(payload: {\n partitionId: number;\n success: boolean;\n checksumMatch: boolean;\n }): void {\n const { partitionId, success } = payload;\n const pending = this.pendingVerifications.get(partitionId);\n\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingVerifications.delete(partitionId);\n pending.resolve(success);\n }\n }\n\n /**\n * Reassemble chunks into continuous data\n */\n private reassemble(chunks: Uint8Array[]): Uint8Array {\n const totalLength = chunks.reduce((sum, c) => sum + (c?.length ?? 0), 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n if (chunk) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n }\n\n return result;\n }\n\n /**\n * Deserialize records from chunk data\n */\n private deserializeRecords(data: Uint8Array): Uint8Array[] {\n const records: Uint8Array[] = [];\n let offset = 0;\n\n while (offset < data.length) {\n if (offset + 4 > data.length) break;\n\n const length = new DataView(data.buffer, data.byteOffset + offset, 4).getUint32(0, true);\n offset += 4;\n\n if (offset + length > data.length) break;\n\n records.push(data.slice(offset, offset + length));\n offset += length;\n }\n\n return records;\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n /**\n * Setup cluster message handlers\n */\n private setupMessageHandlers(): void {\n this.clusterManager.on('message', (msg: ClusterMessage) => {\n if (msg.payload?._migration) {\n const migration = msg.payload._migration;\n\n switch (migration.type) {\n case 'MIGRATION_START':\n this.handleMigrationStart(migration.payload);\n break;\n case 'MIGRATION_CHUNK':\n this.handleMigrationChunk(migration.payload);\n break;\n case 'MIGRATION_COMPLETE':\n this.handleMigrationComplete(migration.payload).catch(err => {\n logger.error({ error: err }, 'Error handling migration complete');\n });\n break;\n case 'MIGRATION_CHUNK_ACK':\n this.handleMigrationChunkAck(migration.payload);\n break;\n case 'MIGRATION_VERIFY':\n this.handleMigrationVerify(migration.payload);\n break;\n }\n }\n });\n }\n\n // ============================================\n // Status and Metrics\n // ============================================\n\n /**\n * Check if a partition is currently migrating\n */\n public isActive(partitionId: number): boolean {\n return this.activeMigrations.has(partitionId) || this.incomingMigrations.has(partitionId);\n }\n\n /**\n * Get migration status\n */\n public getStatus(): MigrationStatus {\n const avgMigrationTime = this.metrics.migrationsCompleted > 0\n ? (Date.now() - (this.activeMigrations.values().next().value?.startTime ?? Date.now()))\n : 0;\n\n const estimatedTimeRemainingMs =\n (this.migrationQueue.length + this.activeMigrations.size) *\n (avgMigrationTime || 1000); // Default to 1s if no data\n\n return {\n inProgress: this.activeMigrations.size > 0 || this.migrationQueue.length > 0,\n active: Array.from(this.activeMigrations.values()),\n queued: this.migrationQueue.length,\n completed: this.metrics.migrationsCompleted,\n failed: this.metrics.migrationsFailed,\n estimatedTimeRemainingMs,\n };\n }\n\n /**\n * Get migration metrics\n */\n public getMetrics(): MigrationMetrics {\n return { ...this.metrics };\n }\n\n /**\n * Cancel all active and queued migrations\n */\n public async cancelAll(): Promise<void> {\n this.stopBatchProcessing();\n\n // Clear queued migrations\n this.migrationQueue = [];\n this.metrics.queuedMigrations = 0;\n\n // Mark active migrations as failed\n for (const [partitionId, migration] of this.activeMigrations) {\n migration.state = PartitionState.FAILED;\n this.metrics.migrationsFailed++;\n this.emit('migrationFailed', partitionId, new Error('Migration cancelled'));\n }\n\n this.activeMigrations.clear();\n this.metrics.activeMigrations = 0;\n\n // Clear pending acks\n for (const pending of this.pendingChunkAcks.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Migration cancelled'));\n }\n this.pendingChunkAcks.clear();\n\n // Clear pending verifications\n for (const pending of this.pendingVerifications.values()) {\n clearTimeout(pending.timeout);\n pending.resolve(false);\n }\n this.pendingVerifications.clear();\n\n // Clear incoming migrations\n this.incomingMigrations.clear();\n\n logger.info('All migrations cancelled');\n }\n\n /**\n * Cleanup resources (sync version for backwards compatibility)\n */\n public close(): void {\n this.cancelAll();\n }\n\n /**\n * Async cleanup - waits for cancellation to complete\n */\n public async closeAsync(): Promise<void> {\n await this.cancelAll();\n this.removeAllListeners();\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface LockRequest {\n clientId: string;\n requestId: string;\n ttl: number;\n timestamp: number;\n}\n\nexport interface LockState {\n name: string;\n owner: string; // clientId\n fencingToken: number;\n expiry: number;\n queue: LockRequest[];\n}\n\nexport class LockManager extends EventEmitter {\n private locks: Map<string, LockState> = new Map();\n private checkInterval: NodeJS.Timeout;\n\n private static readonly MIN_TTL = 1000; // 1 second\n private static readonly MAX_TTL = 300000; // 5 minutes\n\n constructor() {\n super();\n this.checkInterval = setInterval(() => this.cleanupExpiredLocks(), 1000);\n }\n\n public stop() {\n clearInterval(this.checkInterval);\n }\n\n public acquire(name: string, clientId: string, requestId: string, ttl: number): { granted: boolean; fencingToken?: number; error?: string } {\n // Validate TTL\n const safeTtl = Math.max(LockManager.MIN_TTL, Math.min(ttl || LockManager.MIN_TTL, LockManager.MAX_TTL));\n\n let lock = this.locks.get(name);\n if (!lock) {\n lock = {\n name,\n owner: '',\n fencingToken: 0,\n expiry: 0,\n queue: []\n };\n this.locks.set(name, lock);\n }\n\n const now = Date.now();\n\n // If lock is free or expired\n if (!lock.owner || lock.expiry < now) {\n this.grantLock(lock, clientId, safeTtl);\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // If already owned by same client, extend lease\n if (lock.owner === clientId) {\n lock.expiry = Math.max(lock.expiry, now + safeTtl);\n logger.info({ name, clientId, fencingToken: lock.fencingToken }, 'Lock lease extended');\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // Queue request\n lock.queue.push({ clientId, requestId, ttl: safeTtl, timestamp: now });\n logger.info({ name, clientId, queueLength: lock.queue.length }, 'Lock queued');\n return { granted: false };\n }\n\n public release(name: string, clientId: string, fencingToken: number): boolean {\n const lock = this.locks.get(name);\n if (!lock) return false;\n\n if (lock.owner !== clientId) {\n logger.warn({ name, clientId, owner: lock.owner }, 'Release failed: Not owner');\n return false;\n }\n\n if (lock.fencingToken !== fencingToken) {\n logger.warn({ name, clientId, sentToken: fencingToken, actualToken: lock.fencingToken }, 'Release failed: Token mismatch');\n return false;\n }\n\n this.processNext(lock);\n return true;\n }\n\n public handleClientDisconnect(clientId: string) {\n for (const lock of this.locks.values()) {\n // 1. If client owns the lock, force release\n if (lock.owner === clientId) {\n logger.info({ name: lock.name, clientId }, 'Releasing lock due to disconnect');\n this.processNext(lock);\n } else {\n // 2. Remove from queue if present\n const initialLen = lock.queue.length;\n lock.queue = lock.queue.filter(req => req.clientId !== clientId);\n if (lock.queue.length < initialLen) {\n logger.info({ name: lock.name, clientId }, 'Removed from lock queue due to disconnect');\n }\n }\n }\n }\n\n private grantLock(lock: LockState, clientId: string, ttl: number) {\n lock.owner = clientId;\n lock.expiry = Date.now() + ttl;\n lock.fencingToken++;\n logger.info({ name: lock.name, clientId, fencingToken: lock.fencingToken }, 'Lock granted');\n }\n\n private processNext(lock: LockState) {\n const now = Date.now();\n \n // Reset owner\n lock.owner = '';\n lock.expiry = 0;\n\n // Process queue\n while (lock.queue.length > 0) {\n const next = lock.queue.shift()!;\n \n // Grant to next\n this.grantLock(lock, next.clientId, next.ttl);\n \n // Emit event so ServerCoordinator can notify the client\n this.emit('lockGranted', {\n clientId: next.clientId,\n requestId: next.requestId,\n name: lock.name,\n fencingToken: lock.fencingToken\n });\n \n return;\n }\n \n // No one waiting\n if (lock.queue.length === 0) {\n this.locks.delete(lock.name);\n }\n }\n\n private cleanupExpiredLocks() {\n const now = Date.now();\n // Use a copy of keys to avoid concurrent modification issues during iteration\n const lockNames = Array.from(this.locks.keys());\n \n for (const name of lockNames) {\n const lock = this.locks.get(name);\n if (!lock) continue;\n\n if (lock.owner && lock.expiry < now) {\n logger.info({ name: lock.name, owner: lock.owner }, 'Lock expired, processing next');\n this.processNext(lock);\n } else if (!lock.owner && lock.queue.length === 0) {\n // Cleanup empty orphaned locks\n this.locks.delete(name);\n }\n }\n }\n}\n\n","import { PermissionPolicy, Principal, PermissionType } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class SecurityManager {\n private policies: PermissionPolicy[] = [];\n\n constructor(policies: PermissionPolicy[] = []) {\n this.policies = policies;\n }\n\n public addPolicy(policy: PermissionPolicy) {\n this.policies.push(policy);\n }\n\n public checkPermission(principal: Principal, mapName: string, action: PermissionType): boolean {\n // 1. Superuser check (optional, but good practice)\n if (principal.roles.includes('ADMIN')) {\n return true;\n }\n\n // 2. System Map Protection\n if (mapName.startsWith('$sys/')) {\n logger.warn({ userId: principal.userId, mapName }, 'Access Denied: System Map requires ADMIN role');\n return false;\n }\n\n // 2. Iterate policies to find a match\n for (const policy of this.policies) {\n const hasRole = this.hasRole(principal, policy.role);\n const matchesMap = this.matchesMap(mapName, policy.mapNamePattern, principal);\n\n if (hasRole && matchesMap) {\n if (policy.actions.includes('ALL') || policy.actions.includes(action)) {\n return true;\n }\n } else {\n // Trace why it failed matching if needed (verbose)\n // logger.trace({ policy, hasRole, matchesMap, mapName, user: principal.userId }, 'Policy mismatch');\n }\n }\n\n logger.warn({\n userId: principal.userId,\n roles: principal.roles,\n mapName,\n action,\n policyCount: this.policies.length\n }, 'SecurityManager: Access Denied - No matching policy found');\n\n return false;\n }\n\n public filterObject(object: any, principal: Principal, mapName: string): any {\n if (!object || typeof object !== 'object') return object;\n if (principal.roles.includes('ADMIN')) return object;\n\n if (Array.isArray(object)) {\n return object.map(item => this.filterObject(item, principal, mapName));\n }\n\n let allowedFields: Set<string> | null = null;\n let accessGranted = false;\n\n for (const policy of this.policies) {\n if (this.hasRole(principal, policy.role) && this.matchesMap(mapName, policy.mapNamePattern, principal)) {\n if (policy.actions.includes('ALL') || policy.actions.includes('READ')) {\n accessGranted = true;\n\n // If any policy allows everything, return immediately\n if (!policy.allowedFields || policy.allowedFields.length === 0 || policy.allowedFields.includes('*')) {\n return object;\n }\n\n if (allowedFields === null) allowedFields = new Set();\n policy.allowedFields.forEach(f => allowedFields!.add(f));\n }\n }\n }\n\n if (!accessGranted) return null;\n if (allowedFields === null) return object; // Should have returned above, but as fallback\n\n const filtered: any = {};\n for (const key of Object.keys(object)) {\n if (allowedFields.has(key)) {\n filtered[key] = object[key];\n }\n }\n return filtered;\n }\n\n private hasRole(principal: Principal, role: string): boolean {\n return principal.roles.includes(role);\n }\n\n private matchesMap(mapName: string, pattern: string, principal?: Principal): boolean {\n // Dynamic substitution for {userId}\n let finalPattern = pattern;\n if (pattern.includes('{userId}') && principal) {\n finalPattern = pattern.replace('{userId}', principal.userId);\n }\n\n if (finalPattern === '*') return true;\n if (finalPattern === mapName) return true;\n\n if (finalPattern.endsWith('*')) {\n const prefix = finalPattern.slice(0, -1);\n return mapName.startsWith(prefix);\n }\n\n return false;\n }\n}\n","import { Registry, Gauge, Counter, Summary, collectDefaultMetrics } from 'prom-client';\n\nexport class MetricsService {\n public readonly registry: Registry;\n\n // Metrics\n private connectedClients: Gauge;\n private mapSizeItems: Gauge;\n private opsTotal: Counter;\n private memoryUsage: Gauge;\n private clusterMembers: Gauge;\n\n // Subscription-based routing metrics\n private eventsRoutedTotal: Counter;\n private eventsFilteredBySubscription: Counter;\n private subscribersPerEvent: Summary;\n\n // Bounded event queue metrics\n private eventQueueSize: Gauge;\n private eventQueueEnqueued: Counter;\n private eventQueueDequeued: Counter;\n private eventQueueRejected: Counter;\n\n // Backpressure metrics\n private backpressureSyncForcedTotal: Counter;\n private backpressurePendingOps: Gauge;\n private backpressureWaitsTotal: Counter;\n private backpressureTimeoutsTotal: Counter;\n\n // Connection scaling metrics\n private connectionsAcceptedTotal: Counter;\n private connectionsRejectedTotal: Counter;\n private connectionsPending: Gauge;\n private connectionRatePerSecond: Gauge;\n\n constructor() {\n this.registry = new Registry();\n\n // Enable default nodejs metrics (cpu, memory, etc.)\n collectDefaultMetrics({ register: this.registry, prefix: 'topgun_' });\n\n this.connectedClients = new Gauge({\n name: 'topgun_connected_clients',\n help: 'Number of currently connected clients',\n registers: [this.registry],\n });\n\n this.mapSizeItems = new Gauge({\n name: 'topgun_map_size_items',\n help: 'Number of items in a map',\n labelNames: ['map'],\n registers: [this.registry],\n });\n\n this.opsTotal = new Counter({\n name: 'topgun_ops_total',\n help: 'Total number of operations',\n labelNames: ['type', 'map'],\n registers: [this.registry],\n });\n\n this.memoryUsage = new Gauge({\n name: 'topgun_memory_usage_bytes',\n help: 'Current memory usage in bytes',\n registers: [this.registry],\n collect() {\n this.set(process.memoryUsage().heapUsed);\n }\n });\n\n this.clusterMembers = new Gauge({\n name: 'topgun_cluster_members',\n help: 'Number of active cluster members',\n registers: [this.registry],\n });\n\n // === Subscription-based routing metrics ===\n this.eventsRoutedTotal = new Counter({\n name: 'topgun_events_routed_total',\n help: 'Total number of events processed for routing',\n registers: [this.registry],\n });\n\n this.eventsFilteredBySubscription = new Counter({\n name: 'topgun_events_filtered_by_subscription',\n help: 'Events NOT sent due to no active subscriptions',\n registers: [this.registry],\n });\n\n this.subscribersPerEvent = new Summary({\n name: 'topgun_subscribers_per_event',\n help: 'Distribution of subscribers per event',\n percentiles: [0.5, 0.9, 0.99],\n registers: [this.registry],\n });\n\n // === Bounded event queue metrics ===\n this.eventQueueSize = new Gauge({\n name: 'topgun_event_queue_size',\n help: 'Current size of the event queue across all stripes',\n labelNames: ['stripe'],\n registers: [this.registry],\n });\n\n this.eventQueueEnqueued = new Counter({\n name: 'topgun_event_queue_enqueued_total',\n help: 'Total number of events enqueued',\n registers: [this.registry],\n });\n\n this.eventQueueDequeued = new Counter({\n name: 'topgun_event_queue_dequeued_total',\n help: 'Total number of events dequeued',\n registers: [this.registry],\n });\n\n this.eventQueueRejected = new Counter({\n name: 'topgun_event_queue_rejected_total',\n help: 'Total number of events rejected due to queue capacity',\n registers: [this.registry],\n });\n\n // === Backpressure metrics ===\n this.backpressureSyncForcedTotal = new Counter({\n name: 'topgun_backpressure_sync_forced_total',\n help: 'Total number of times sync processing was forced',\n registers: [this.registry],\n });\n\n this.backpressurePendingOps = new Gauge({\n name: 'topgun_backpressure_pending_ops',\n help: 'Current number of pending async operations',\n registers: [this.registry],\n });\n\n this.backpressureWaitsTotal = new Counter({\n name: 'topgun_backpressure_waits_total',\n help: 'Total number of times had to wait for capacity',\n registers: [this.registry],\n });\n\n this.backpressureTimeoutsTotal = new Counter({\n name: 'topgun_backpressure_timeouts_total',\n help: 'Total number of backpressure timeouts',\n registers: [this.registry],\n });\n\n // === Connection scaling metrics ===\n this.connectionsAcceptedTotal = new Counter({\n name: 'topgun_connections_accepted_total',\n help: 'Total number of connections accepted',\n registers: [this.registry],\n });\n\n this.connectionsRejectedTotal = new Counter({\n name: 'topgun_connections_rejected_total',\n help: 'Total number of connections rejected due to rate limiting',\n registers: [this.registry],\n });\n\n this.connectionsPending = new Gauge({\n name: 'topgun_connections_pending',\n help: 'Number of connections currently pending (handshake in progress)',\n registers: [this.registry],\n });\n\n this.connectionRatePerSecond = new Gauge({\n name: 'topgun_connection_rate_per_second',\n help: 'Current connection rate per second',\n registers: [this.registry],\n });\n }\n\n public destroy() {\n this.registry.clear();\n }\n\n public setConnectedClients(count: number) {\n this.connectedClients.set(count);\n }\n\n public setMapSize(mapName: string, size: number) {\n this.mapSizeItems.set({ map: mapName }, size);\n }\n\n public incOp(type: 'PUT' | 'GET' | 'DELETE' | 'SUBSCRIBE', mapName: string) {\n this.opsTotal.inc({ type, map: mapName });\n }\n\n public setClusterMembers(count: number) {\n this.clusterMembers.set(count);\n }\n\n // === Subscription-based routing metric methods ===\n\n /**\n * Increment counter for total events processed for routing.\n */\n public incEventsRouted(): void {\n this.eventsRoutedTotal.inc();\n }\n\n /**\n * Increment counter for events filtered out due to no subscribers.\n */\n public incEventsFilteredBySubscription(): void {\n this.eventsFilteredBySubscription.inc();\n }\n\n /**\n * Record the number of subscribers for an event (for average calculation).\n */\n public recordSubscribersPerEvent(count: number): void {\n this.subscribersPerEvent.observe(count);\n }\n\n // === Bounded event queue metric methods ===\n\n /**\n * Set the current size of a specific queue stripe.\n */\n public setEventQueueSize(stripe: number, size: number): void {\n this.eventQueueSize.set({ stripe: String(stripe) }, size);\n }\n\n /**\n * Increment counter for events enqueued.\n */\n public incEventQueueEnqueued(): void {\n this.eventQueueEnqueued.inc();\n }\n\n /**\n * Increment counter for events dequeued.\n */\n public incEventQueueDequeued(): void {\n this.eventQueueDequeued.inc();\n }\n\n /**\n * Increment counter for events rejected due to queue capacity.\n */\n public incEventQueueRejected(): void {\n this.eventQueueRejected.inc();\n }\n\n // === Backpressure metric methods ===\n\n /**\n * Increment counter for forced sync operations.\n */\n public incBackpressureSyncForced(): void {\n this.backpressureSyncForcedTotal.inc();\n }\n\n /**\n * Set the current number of pending async operations.\n */\n public setBackpressurePendingOps(count: number): void {\n this.backpressurePendingOps.set(count);\n }\n\n /**\n * Increment counter for times had to wait for capacity.\n */\n public incBackpressureWaits(): void {\n this.backpressureWaitsTotal.inc();\n }\n\n /**\n * Increment counter for backpressure timeouts.\n */\n public incBackpressureTimeouts(): void {\n this.backpressureTimeoutsTotal.inc();\n }\n\n // === Connection scaling metric methods ===\n\n /**\n * Increment counter for accepted connections.\n */\n public incConnectionsAccepted(): void {\n this.connectionsAcceptedTotal.inc();\n }\n\n /**\n * Increment counter for rejected connections.\n */\n public incConnectionsRejected(): void {\n this.connectionsRejectedTotal.inc();\n }\n\n /**\n * Set the current number of pending connections.\n */\n public setConnectionsPending(count: number): void {\n this.connectionsPending.set(count);\n }\n\n /**\n * Set the current connection rate per second.\n */\n public setConnectionRatePerSecond(rate: number): void {\n this.connectionRatePerSecond.set(rate);\n }\n\n public async getMetrics(): Promise<string> {\n return this.registry.metrics();\n }\n\n public async getMetricsJson(): Promise<Record<string, any>> {\n const metrics = await this.registry.getMetricsAsJSON();\n // Flatten or simplify for dashboard if needed, but raw JSON is fine for now\n const result: Record<string, any> = {};\n for (const metric of metrics) {\n // Simple flattening: name -> value (if single value)\n // metric.type is an enum/number in some versions or string in others. \n // To be safe and avoid type errors, we just check values length.\n if (metric.values.length === 1) {\n result[metric.name] = metric.values[0].value;\n } else {\n // Complex metrics\n result[metric.name] = metric.values;\n }\n }\n return result;\n }\n\n public getContentType(): string {\n return this.registry.contentType;\n }\n}\n\n","import { LWWMap, LWWRecord } from '@topgunbuild/core';\nimport { ClusterManager } from '../cluster/ClusterManager';\nimport { MetricsService } from '../monitoring/MetricsService';\nimport { logger } from '../utils/logger';\n\nexport class SystemManager {\n private cluster: ClusterManager;\n private metrics: MetricsService;\n private getMap: (name: string) => LWWMap<string, any>;\n\n private statsInterval?: NodeJS.Timeout;\n\n constructor(\n cluster: ClusterManager,\n metrics: MetricsService,\n getMap: (name: string) => LWWMap<string, any>\n ) {\n this.cluster = cluster;\n this.metrics = metrics;\n this.getMap = getMap;\n }\n\n public start() {\n this.setupClusterMap();\n this.setupStatsMap();\n this.setupMapsMap();\n\n // Update stats every 5 seconds\n this.statsInterval = setInterval(() => this.updateStats(), 5000);\n\n // Listen for cluster events\n this.cluster.on('memberJoined', () => this.updateClusterMap());\n this.cluster.on('memberLeft', () => this.updateClusterMap());\n\n // Initial updates\n this.updateClusterMap();\n this.updateStats();\n }\n\n public stop() {\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n }\n }\n\n public notifyMapCreated(mapName: string) {\n if (mapName.startsWith('$sys/')) return; // Don't track system maps\n this.updateMapsMap(mapName);\n }\n\n private setupClusterMap() {\n // Ensure map exists\n this.getMap('$sys/cluster');\n }\n\n private setupStatsMap() {\n this.getMap('$sys/stats');\n }\n\n private setupMapsMap() {\n this.getMap('$sys/maps');\n }\n\n private updateClusterMap() {\n try {\n const map = this.getMap('$sys/cluster');\n const members = this.cluster.getMembers();\n\n // We can't easily \"remove\" missing members without iterating the whole map\n // For now, we just put current members.\n // A proper sync would require diffing.\n\n // In a real implementation, we might want to store more info than just ID.\n // But ClusterManager currently only gives us IDs easily or we have to look them up.\n // Let's iterate members map from ClusterManager if possible, or just use IDs.\n\n // Accessing private members map via any cast for now or just using IDs\n // The ClusterManager.getMembers() returns IDs.\n\n for (const memberId of members) {\n const isLocal = this.cluster.isLocal(memberId);\n map.set(memberId, {\n id: memberId,\n status: 'UP',\n isLocal,\n lastUpdated: Date.now()\n });\n }\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/cluster');\n }\n }\n\n private async updateStats() {\n try {\n const map = this.getMap('$sys/stats');\n const metrics = await this.metrics.getMetricsJson(); // We need to add getMetricsJson to MetricsService\n\n map.set(this.cluster.config.nodeId, {\n ...metrics,\n timestamp: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/stats');\n }\n }\n\n private updateMapsMap(mapName: string) {\n try {\n const map = this.getMap('$sys/maps');\n map.set(mapName, {\n name: mapName,\n createdAt: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/maps');\n }\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from './logger';\n\nexport interface QueueMetrics {\n enqueued: number;\n dequeued: number;\n rejected: number;\n currentSize: number;\n}\n\nexport interface BoundedEventQueueOptions {\n maxSize: number;\n name: string;\n onReject?: (item: any) => void;\n /** High water mark percentage (0-1). Emits 'highWater' when reached. Default: 0.8 */\n highWaterMark?: number;\n}\n\nexport class BoundedEventQueue<T> extends EventEmitter {\n private queue: T[] = [];\n private readonly maxSize: number;\n private readonly name: string;\n private readonly onReject?: (item: any) => void;\n private readonly highWaterMark: number;\n private highWaterEmitted: boolean = false;\n private metrics: QueueMetrics = {\n enqueued: 0,\n dequeued: 0,\n rejected: 0,\n currentSize: 0\n };\n\n constructor(options: BoundedEventQueueOptions) {\n super();\n this.maxSize = options.maxSize;\n this.name = options.name;\n this.onReject = options.onReject;\n this.highWaterMark = options.highWaterMark ?? 0.8;\n }\n\n /**\n * Attempt to enqueue an item.\n * @returns true if enqueued, false if rejected due to capacity\n */\n enqueue(item: T): boolean {\n if (this.queue.length >= this.maxSize) {\n this.metrics.rejected++;\n logger.warn(\n { queue: this.name, currentSize: this.queue.length, maxSize: this.maxSize },\n 'Queue full, rejecting item'\n );\n this.onReject?.(item);\n return false;\n }\n\n this.queue.push(item);\n this.metrics.enqueued++;\n this.metrics.currentSize = this.queue.length;\n\n // Check for high water mark\n const usage = this.queue.length / this.maxSize;\n if (usage >= this.highWaterMark && !this.highWaterEmitted) {\n this.highWaterEmitted = true;\n this.emit('highWater', { name: this.name, usage, size: this.queue.length });\n }\n\n // Check if queue just became full\n if (this.queue.length === this.maxSize) {\n this.emit('full', { name: this.name, size: this.queue.length });\n }\n\n return true;\n }\n\n /**\n * Dequeue an item. Returns undefined if queue is empty.\n */\n dequeue(): T | undefined {\n const item = this.queue.shift();\n\n if (item !== undefined) {\n this.metrics.dequeued++;\n this.metrics.currentSize = this.queue.length;\n\n // Reset high water mark flag when below threshold\n const usage = this.queue.length / this.maxSize;\n if (usage < this.highWaterMark) {\n this.highWaterEmitted = false;\n }\n\n // Check if queue just became empty\n if (this.queue.length === 0) {\n this.emit('empty', { name: this.name });\n }\n }\n\n return item;\n }\n\n /**\n * Peek at front item without removing.\n */\n peek(): T | undefined {\n return this.queue[0];\n }\n\n /**\n * Current queue size.\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is full.\n */\n get isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Check if queue is empty.\n */\n get isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Get queue metrics.\n */\n getMetrics(): QueueMetrics {\n return { ...this.metrics };\n }\n\n /**\n * Clear the queue.\n */\n clear(): void {\n const wasNotEmpty = this.queue.length > 0;\n this.queue = [];\n this.metrics.currentSize = 0;\n this.highWaterEmitted = false;\n\n if (wasNotEmpty) {\n this.emit('empty', { name: this.name });\n }\n }\n}\n","import { BoundedEventQueue, QueueMetrics } from './BoundedEventQueue';\nimport { logger } from './logger';\n\nexport interface StripedExecutorOptions {\n /** Number of worker stripes (default: 4) */\n stripeCount: number;\n /** Total capacity across all stripes */\n queueCapacity: number;\n name: string;\n /** Callback when a task is rejected */\n onReject?: (task: StripedTask) => void;\n}\n\nexport interface StripedTask {\n /** Used for stripe selection (hash % stripeCount) */\n key: string | number;\n /** The task to execute */\n execute: () => Promise<void> | void;\n}\n\nexport interface StripeMetrics {\n stripe: number;\n metrics: QueueMetrics;\n}\n\n/**\n * Hazelcast-style striped executor that distributes tasks across multiple bounded queues.\n * Tasks with the same key are guaranteed to be processed in order on the same stripe.\n */\nexport class StripedEventExecutor {\n private stripes: BoundedEventQueue<StripedTask>[];\n private processing: boolean[];\n private readonly stripeCount: number;\n private readonly name: string;\n private readonly onReject?: (task: StripedTask) => void;\n private isShuttingDown: boolean = false;\n private pendingPromises: Set<Promise<void>> = new Set();\n\n constructor(options: StripedExecutorOptions) {\n this.stripeCount = options.stripeCount;\n this.name = options.name;\n this.onReject = options.onReject;\n\n // Distribute capacity across stripes\n const capacityPerStripe = Math.ceil(options.queueCapacity / options.stripeCount);\n\n this.stripes = [];\n this.processing = [];\n\n for (let i = 0; i < options.stripeCount; i++) {\n const queue = new BoundedEventQueue<StripedTask>({\n maxSize: capacityPerStripe,\n name: `${options.name}-stripe-${i}`,\n onReject: (task) => this.handleReject(task)\n });\n\n // Set up event handlers for monitoring\n queue.on('highWater', (event) => {\n logger.warn(\n { executor: this.name, stripe: i, ...event },\n 'Stripe approaching capacity'\n );\n });\n\n this.stripes.push(queue);\n this.processing.push(false);\n }\n }\n\n /**\n * Submit a task. Tasks with same key go to same stripe (ordering guarantee).\n * @returns true if accepted, false if rejected\n */\n submit(task: StripedTask): boolean {\n if (this.isShuttingDown) {\n logger.warn({ executor: this.name }, 'Executor is shutting down, rejecting task');\n this.handleReject(task);\n return false;\n }\n\n const stripeIndex = this.getStripeIndex(task.key);\n const stripe = this.stripes[stripeIndex];\n\n const accepted = stripe.enqueue(task);\n\n if (accepted) {\n this.processStripe(stripeIndex);\n }\n\n return accepted;\n }\n\n /**\n * Get metrics for all stripes.\n */\n getMetrics(): StripeMetrics[] {\n return this.stripes.map((stripe, index) => ({\n stripe: index,\n metrics: stripe.getMetrics()\n }));\n }\n\n /**\n * Get aggregated metrics across all stripes.\n */\n getTotalMetrics(): QueueMetrics {\n const total: QueueMetrics = {\n enqueued: 0,\n dequeued: 0,\n rejected: 0,\n currentSize: 0\n };\n\n for (const stripe of this.stripes) {\n const metrics = stripe.getMetrics();\n total.enqueued += metrics.enqueued;\n total.dequeued += metrics.dequeued;\n total.rejected += metrics.rejected;\n total.currentSize += metrics.currentSize;\n }\n\n return total;\n }\n\n /**\n * Check if all stripes are full.\n */\n get isFull(): boolean {\n return this.stripes.every(stripe => stripe.isFull);\n }\n\n /**\n * Get current total size across all stripes.\n */\n get size(): number {\n return this.stripes.reduce((sum, stripe) => sum + stripe.size, 0);\n }\n\n /**\n * Shutdown executor, optionally waiting for pending tasks.\n */\n async shutdown(waitForPending: boolean = true): Promise<void> {\n this.isShuttingDown = true;\n\n if (waitForPending) {\n // Wait for all currently processing tasks to complete\n const promises = Array.from(this.pendingPromises);\n if (promises.length > 0) {\n await Promise.all(promises);\n }\n\n // Process any remaining items in queues\n const drainPromises: Promise<void>[] = [];\n for (let i = 0; i < this.stripeCount; i++) {\n if (!this.stripes[i].isEmpty) {\n drainPromises.push(this.drainStripe(i));\n }\n }\n await Promise.all(drainPromises);\n }\n\n // Clear all queues\n for (const stripe of this.stripes) {\n stripe.clear();\n }\n\n logger.info({ executor: this.name }, 'Executor shutdown complete');\n }\n\n private getStripeIndex(key: string | number): number {\n const hash = typeof key === 'number' ? key : this.hashString(key);\n return Math.abs(hash) % this.stripeCount;\n }\n\n private hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return hash;\n }\n\n private handleReject(task: StripedTask): void {\n this.onReject?.(task);\n }\n\n private async processStripe(stripeIndex: number): Promise<void> {\n // Prevent concurrent processing of the same stripe\n if (this.processing[stripeIndex]) {\n return;\n }\n\n this.processing[stripeIndex] = true;\n const stripe = this.stripes[stripeIndex];\n\n try {\n while (!stripe.isEmpty && !this.isShuttingDown) {\n const task = stripe.dequeue();\n if (!task) break;\n\n try {\n const result = task.execute();\n if (result instanceof Promise) {\n this.pendingPromises.add(result);\n await result;\n this.pendingPromises.delete(result);\n }\n } catch (error) {\n logger.error(\n { executor: this.name, stripe: stripeIndex, key: task.key, error },\n 'Task execution failed'\n );\n }\n }\n } finally {\n this.processing[stripeIndex] = false;\n }\n }\n\n private async drainStripe(stripeIndex: number): Promise<void> {\n const stripe = this.stripes[stripeIndex];\n\n while (!stripe.isEmpty) {\n const task = stripe.dequeue();\n if (!task) break;\n\n try {\n const result = task.execute();\n if (result instanceof Promise) {\n await result;\n }\n } catch (error) {\n logger.error(\n { executor: this.name, stripe: stripeIndex, key: task.key, error },\n 'Task execution failed during drain'\n );\n }\n }\n }\n}\n","import { logger } from './logger';\n\nexport interface BackpressureConfig {\n /**\n * How often to force sync (every N operations).\n * Default: 100\n */\n syncFrequency: number;\n\n /**\n * Maximum pending async operations before blocking.\n * Default: 1000\n */\n maxPendingOps: number;\n\n /**\n * Backoff timeout in ms when at capacity.\n * Default: 5000\n */\n backoffTimeoutMs: number;\n\n /**\n * Enable/disable backpressure.\n * Default: true\n */\n enabled: boolean;\n}\n\nexport interface BackpressureStats {\n pendingOps: number;\n syncCounter: number;\n utilizationPercent: number;\n syncForcedTotal: number;\n waitsTotal: number;\n timeoutsTotal: number;\n}\n\nconst DEFAULT_CONFIG: BackpressureConfig = {\n syncFrequency: 100,\n maxPendingOps: 1000,\n backoffTimeoutMs: 5000,\n enabled: true\n};\n\n/**\n * BackpressureRegulator implements backpressure control similar to Hazelcast's approach.\n *\n * It periodically forces synchronous processing to drain queues and prevent\n * unbounded accumulation of async work. Key features:\n *\n * 1. Sync Window: Every N operations, force sync processing\n * 2. Capacity Limiting: Block when too many operations are pending\n * 3. Randomization: Prevents thundering herd on sync points\n */\nexport class BackpressureRegulator {\n private syncCounter = 0;\n private pendingOps = 0;\n private readonly config: BackpressureConfig;\n\n // Metrics tracking\n private syncForcedTotal = 0;\n private waitsTotal = 0;\n private timeoutsTotal = 0;\n\n // Waiters queue for capacity\n private waiters: Array<{ resolve: () => void; reject: (err: Error) => void }> = [];\n\n constructor(config?: Partial<BackpressureConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Check if current operation should be forced to sync.\n * Based on Hazelcast's sync window with randomization.\n *\n * Uses randomization within the sync window to prevent all operations\n * from syncing at the same point, which could cause thundering herd.\n */\n shouldForceSync(): boolean {\n if (!this.config.enabled) {\n return false;\n }\n\n this.syncCounter++;\n\n // Randomize within the sync window to prevent thundering herd\n // Each operation has a 1/syncFrequency chance of being forced to sync\n // This distributes sync points evenly across the window\n const syncThreshold = this.config.syncFrequency;\n const shouldSync = this.syncCounter >= syncThreshold;\n\n if (shouldSync) {\n this.syncCounter = 0;\n this.syncForcedTotal++;\n logger.debug({ syncForcedTotal: this.syncForcedTotal }, 'Forcing sync operation');\n return true;\n }\n\n // Additional randomization: 10% chance of early sync if above 80% capacity\n const utilization = this.pendingOps / this.config.maxPendingOps;\n if (utilization > 0.8 && Math.random() < 0.1) {\n this.syncForcedTotal++;\n logger.debug({ utilization, pendingOps: this.pendingOps }, 'Early sync due to high utilization');\n return true;\n }\n\n return false;\n }\n\n /**\n * Register a new pending async operation.\n * @returns true if allowed, false if should wait\n */\n registerPending(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n if (this.pendingOps >= this.config.maxPendingOps) {\n return false;\n }\n\n this.pendingOps++;\n return true;\n }\n\n /**\n * Mark an async operation as complete.\n * Also notifies any waiters that capacity may be available.\n */\n completePending(): void {\n if (this.pendingOps > 0) {\n this.pendingOps--;\n }\n\n // Notify a waiter if there's capacity now\n if (this.waiters.length > 0 && this.pendingOps < this.config.maxPendingOps) {\n const waiter = this.waiters.shift();\n if (waiter) {\n waiter.resolve();\n }\n }\n }\n\n /**\n * Wait until there's capacity for new operations.\n * Throws after backoffTimeoutMs if still no capacity.\n */\n async waitForCapacity(): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n if (this.pendingOps < this.config.maxPendingOps) {\n return;\n }\n\n this.waitsTotal++;\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n // Remove from waiters queue\n const index = this.waiters.findIndex(w => w.resolve === resolve);\n if (index !== -1) {\n this.waiters.splice(index, 1);\n }\n this.timeoutsTotal++;\n reject(new Error(`Backpressure timeout after ${this.config.backoffTimeoutMs}ms`));\n }, this.config.backoffTimeoutMs);\n\n this.waiters.push({\n resolve: () => {\n clearTimeout(timeoutId);\n resolve();\n },\n reject\n });\n });\n }\n\n /**\n * Get current stats.\n */\n getStats(): BackpressureStats {\n return {\n pendingOps: this.pendingOps,\n syncCounter: this.syncCounter,\n utilizationPercent: this.config.maxPendingOps > 0\n ? (this.pendingOps / this.config.maxPendingOps) * 100\n : 0,\n syncForcedTotal: this.syncForcedTotal,\n waitsTotal: this.waitsTotal,\n timeoutsTotal: this.timeoutsTotal\n };\n }\n\n /**\n * Check if backpressure is currently active (at capacity).\n */\n isAtCapacity(): boolean {\n return this.config.enabled && this.pendingOps >= this.config.maxPendingOps;\n }\n\n /**\n * Get current pending operations count.\n */\n getPendingOps(): number {\n return this.pendingOps;\n }\n\n /**\n * Check if backpressure is enabled.\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Reset all counters and clear waiters (for testing).\n */\n reset(): void {\n this.syncCounter = 0;\n this.pendingOps = 0;\n this.syncForcedTotal = 0;\n this.waitsTotal = 0;\n this.timeoutsTotal = 0;\n\n // Reject all pending waiters\n for (const waiter of this.waiters) {\n waiter.reject(new Error('BackpressureRegulator reset'));\n }\n this.waiters = [];\n }\n}\n","import { WebSocket } from 'ws';\nimport { serialize } from '@topgunbuild/core';\nimport { BufferPool, getGlobalBufferPool } from '../memory';\n\nexport interface CoalescingWriterOptions {\n /**\n * Maximum messages to batch before forcing flush.\n * Default: 100\n */\n maxBatchSize: number;\n\n /**\n * Maximum time to wait before flushing (ms).\n * Default: 5 (similar to Nagle's algorithm)\n */\n maxDelayMs: number;\n\n /**\n * Maximum batch size in bytes.\n * Default: 65536 (64KB)\n */\n maxBatchBytes: number;\n\n /**\n * Optional BufferPool for batch buffer reuse.\n * If not provided, uses the global buffer pool.\n */\n bufferPool?: BufferPool;\n}\n\ninterface QueuedMessage {\n data: Uint8Array;\n urgent: boolean;\n}\n\n/**\n * Extended metrics for CoalescingWriter performance analysis.\n */\nexport interface CoalescingWriterMetrics {\n /** Total messages sent */\n messagesSent: number;\n /** Total batches sent */\n batchesSent: number;\n /** Total bytes sent */\n bytesSent: number;\n /** Average messages per batch */\n avgMessagesPerBatch: number;\n /** Average bytes per batch */\n avgBytesPerBatch: number;\n /** Messages currently in queue */\n pendingMessages: number;\n /** Bytes currently pending */\n pendingBytes: number;\n /** Count of flushes triggered by size limits (batch full or bytes exceeded) */\n immediateFlushes: number;\n /** Count of flushes triggered by timer expiration */\n timedFlushes: number;\n /** Ratio of actual batch size to maxBatchSize (0-1, higher = better utilization) */\n batchUtilization: number;\n /** Ratio of immediate flushes to total flushes (high = batches filling up quickly) */\n immediateFlushRatio: number;\n /** Count of pooled buffer acquisitions (for monitoring buffer pool usage) */\n pooledBuffersUsed: number;\n /** Count of oversized (non-pooled) buffers that were allocated directly */\n oversizedBuffers: number;\n}\n\n/**\n * State machine for flush scheduling.\n * Similar to Hazelcast's NioOutboundPipeline.State\n */\nenum WriterState {\n IDLE, // No pending writes\n PENDING, // Has pending writes, flush scheduled\n FLUSHING, // Currently flushing\n}\n\nconst DEFAULT_OPTIONS: CoalescingWriterOptions = {\n maxBatchSize: 100,\n maxDelayMs: 5,\n maxBatchBytes: 65536,\n};\n\n/**\n * Per-connection write coalescing that batches multiple messages into single WebSocket frames.\n * Inspired by Hazelcast's NioOutboundPipeline batching strategy.\n */\nexport class CoalescingWriter {\n private socket: WebSocket;\n private queue: QueuedMessage[] = [];\n private urgentQueue: QueuedMessage[] = []; // Priority queue for urgent messages\n private pendingBytes = 0;\n private flushTimer: NodeJS.Immediate | null = null;\n private delayTimer: NodeJS.Timeout | null = null;\n private state: WriterState = WriterState.IDLE;\n private readonly options: CoalescingWriterOptions;\n private readonly bufferPool: BufferPool;\n private closed = false;\n\n // Metrics\n private messagesSent = 0;\n private batchesSent = 0;\n private bytesSent = 0;\n private immediateFlushCount = 0; // Size-triggered flushes\n private timedFlushCount = 0; // Timer-triggered flushes\n private pooledBuffersUsed = 0; // Count of pooled buffer acquisitions\n private oversizedBuffers = 0; // Count of oversized (non-pooled) buffers\n\n constructor(socket: WebSocket, options?: Partial<CoalescingWriterOptions>) {\n this.socket = socket;\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.bufferPool = options?.bufferPool ?? getGlobalBufferPool();\n }\n\n /**\n * Queue a message for sending.\n * @param message - The message object to serialize and send\n * @param urgent - If true, bypass batching and send immediately\n */\n write(message: any, urgent?: boolean): void {\n if (this.closed) {\n return;\n }\n\n const data = serialize(message);\n this.writeRaw(data, urgent);\n }\n\n /**\n * Queue pre-serialized data.\n */\n writeRaw(data: Uint8Array, urgent?: boolean): void {\n if (this.closed) {\n return;\n }\n\n if (urgent) {\n // Urgent messages bypass batching entirely\n this.sendImmediate(data);\n return;\n }\n\n const msg: QueuedMessage = { data, urgent: false };\n this.queue.push(msg);\n this.pendingBytes += data.length;\n\n // Check if we should flush immediately due to size limits\n if (this.queue.length >= this.options.maxBatchSize ||\n this.pendingBytes >= this.options.maxBatchBytes) {\n this.immediateFlushCount++;\n this.flush();\n return;\n }\n\n // Schedule flush if not already scheduled\n this.scheduleFlush();\n }\n\n /**\n * Force flush all pending messages immediately.\n */\n flush(): void {\n if (this.closed) {\n return;\n }\n\n // Cancel any pending timers\n this.cancelTimers();\n\n // Nothing to flush\n if (this.queue.length === 0) {\n this.state = WriterState.IDLE;\n return;\n }\n\n this.state = WriterState.FLUSHING;\n\n try {\n if (this.socket.readyState !== WebSocket.OPEN) {\n // Socket not ready, discard messages\n this.queue = [];\n this.pendingBytes = 0;\n this.state = WriterState.IDLE;\n return;\n }\n\n if (this.queue.length === 1) {\n // Single message - send directly without batching overhead\n const msg = this.queue[0];\n this.socket.send(msg.data);\n this.messagesSent++;\n this.batchesSent++;\n this.bytesSent += msg.data.length;\n } else {\n // Multiple messages - create a batch\n const batch = this.createBatch(this.queue);\n this.socket.send(batch);\n this.messagesSent += this.queue.length;\n this.batchesSent++;\n this.bytesSent += batch.length;\n }\n } catch (error) {\n // Socket error - discard messages silently\n // The connection close handler will clean up\n }\n\n // Clear queue\n this.queue = [];\n this.pendingBytes = 0;\n this.state = WriterState.IDLE;\n }\n\n /**\n * Get writer metrics.\n */\n getMetrics(): CoalescingWriterMetrics {\n const totalFlushes = this.immediateFlushCount + this.timedFlushCount;\n return {\n messagesSent: this.messagesSent,\n batchesSent: this.batchesSent,\n bytesSent: this.bytesSent,\n avgMessagesPerBatch: this.batchesSent > 0 ? this.messagesSent / this.batchesSent : 0,\n avgBytesPerBatch: this.batchesSent > 0 ? this.bytesSent / this.batchesSent : 0,\n pendingMessages: this.queue.length,\n pendingBytes: this.pendingBytes,\n // Extended metrics for tuning analysis\n immediateFlushes: this.immediateFlushCount,\n timedFlushes: this.timedFlushCount,\n batchUtilization: this.batchesSent > 0\n ? (this.messagesSent / this.batchesSent) / this.options.maxBatchSize\n : 0,\n immediateFlushRatio: totalFlushes > 0\n ? this.immediateFlushCount / totalFlushes\n : 0,\n pooledBuffersUsed: this.pooledBuffersUsed,\n oversizedBuffers: this.oversizedBuffers,\n };\n }\n\n /**\n * Get current configuration options.\n */\n getOptions(): Readonly<CoalescingWriterOptions> {\n return { ...this.options };\n }\n\n /**\n * Close and cleanup.\n */\n close(): void {\n if (this.closed) {\n return;\n }\n\n this.cancelTimers();\n\n // Flush any remaining messages before marking as closed\n if (this.queue.length > 0) {\n this.flush();\n }\n\n this.closed = true;\n this.queue = [];\n this.urgentQueue = [];\n this.pendingBytes = 0;\n }\n\n /**\n * Send a message immediately without batching.\n */\n private sendImmediate(data: Uint8Array): void {\n if (this.socket.readyState !== WebSocket.OPEN) {\n return;\n }\n\n try {\n this.socket.send(data);\n this.messagesSent++;\n this.batchesSent++;\n this.bytesSent += data.length;\n } catch (error) {\n // Socket error - ignore\n }\n }\n\n /**\n * Schedule a flush using setImmediate + setTimeout for the delay.\n */\n private scheduleFlush(): void {\n if (this.state === WriterState.PENDING) {\n // Already scheduled\n return;\n }\n\n this.state = WriterState.PENDING;\n\n // Use setTimeout for the delay (similar to Nagle's algorithm)\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n // Use setImmediate to batch with other I/O in the same tick\n this.flushTimer = setImmediate(() => {\n this.flushTimer = null;\n this.timedFlushCount++;\n this.flush();\n });\n }, this.options.maxDelayMs);\n }\n\n /**\n * Cancel any pending flush timers.\n */\n private cancelTimers(): void {\n if (this.flushTimer) {\n clearImmediate(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.delayTimer) {\n clearTimeout(this.delayTimer);\n this.delayTimer = null;\n }\n }\n\n /**\n * Create a batch message from multiple queued messages.\n * Uses length-prefixed format for efficiency:\n * [4 bytes: message count][4 bytes: msg1 length][msg1 bytes][4 bytes: msg2 length][msg2 bytes]...\n *\n * OPTIMIZATION: Uses BufferPool for batch buffer allocation to reduce GC pressure.\n * Pooled buffers are reused across batch operations.\n */\n private createBatch(messages: QueuedMessage[]): Uint8Array {\n // Calculate total size needed\n // 4 bytes for count + (4 bytes length + data) for each message\n let totalSize = 4; // message count\n for (const msg of messages) {\n totalSize += 4 + msg.data.length; // length prefix + data\n }\n\n // Acquire buffer from pool (or get oversized buffer if larger than pool chunk)\n const poolConfig = this.bufferPool.getConfig();\n const isPooled = totalSize <= poolConfig.chunkSize!;\n const batch = this.bufferPool.acquireSize(totalSize);\n\n if (isPooled) {\n this.pooledBuffersUsed++;\n } else {\n this.oversizedBuffers++;\n }\n\n const view = new DataView(batch.buffer, batch.byteOffset, batch.byteLength);\n let offset = 0;\n\n // Write message count\n view.setUint32(offset, messages.length, true); // little-endian\n offset += 4;\n\n // Write each message with length prefix\n for (const msg of messages) {\n view.setUint32(offset, msg.data.length, true);\n offset += 4;\n batch.set(msg.data, offset);\n offset += msg.data.length;\n }\n\n // Create the actual used portion of the buffer\n const usedBatch = batch.subarray(0, totalSize);\n\n // Wrap in a BATCH message envelope\n const batchEnvelope = serialize({\n type: 'BATCH',\n count: messages.length,\n data: usedBatch,\n });\n\n // Release the pooled buffer back to pool after serialization is complete\n // (serialize() copies the data, so we can safely release)\n if (isPooled) {\n this.bufferPool.release(batch);\n }\n\n return batchEnvelope;\n }\n}\n","/**\n * BufferPool - High-performance buffer reuse for serialization operations.\n * Phase 2.03: Memory Pooling\n *\n * Reduces GC pressure by reusing pre-allocated Uint8Array buffers instead of\n * creating new ones for each serialization operation.\n *\n * Reference: Hazelcast MemoryAllocator pattern\n */\n\nexport interface BufferPoolConfig {\n /**\n * Size of each buffer chunk in bytes.\n * Larger chunks = fewer allocations, but more memory waste.\n * Default: 64KB (65536)\n */\n chunkSize?: number;\n\n /**\n * Initial number of buffers to pre-allocate.\n * Default: 16\n */\n initialSize?: number;\n\n /**\n * Maximum buffers to keep in pool.\n * Excess buffers are released to GC.\n * Default: 256\n */\n maxSize?: number;\n\n /**\n * Auto-shrink pool when idle buffers exceed this ratio.\n * Default: true\n */\n autoShrink?: boolean;\n\n /**\n * Shrink threshold - shrink if (available / maxSize) > threshold.\n * Default: 0.75 (75% idle)\n */\n shrinkThreshold?: number;\n\n /**\n * Enable metrics collection.\n * Default: true\n */\n metricsEnabled?: boolean;\n}\n\nexport interface BufferPoolStats {\n /** Buffers currently available in pool */\n available: number;\n /** Buffers currently in use */\n inUse: number;\n /** Total buffers ever created */\n created: number;\n /** Total times a buffer was reused */\n reused: number;\n /** Reuse ratio: reused / (created + reused) */\n reuseRatio: number;\n /** Total bytes in pool (available buffers only) */\n poolSizeBytes: number;\n /** Peak concurrent usage */\n peakUsage: number;\n /** Times pool was exhausted (had to create new buffer) */\n misses: number;\n /** Chunk size configuration */\n chunkSize: number;\n /** Max pool size configuration */\n maxSize: number;\n}\n\nconst DEFAULT_CONFIG: Required<BufferPoolConfig> = {\n chunkSize: 65536, // 64KB\n initialSize: 16,\n maxSize: 256,\n autoShrink: true,\n shrinkThreshold: 0.75,\n metricsEnabled: true,\n};\n\n/**\n * High-performance buffer pool for serialization operations.\n *\n * Usage:\n * ```typescript\n * const pool = new BufferPool({ chunkSize: 64 * 1024 });\n * const buffer = pool.acquire();\n * // ... use buffer for serialization ...\n * pool.release(buffer);\n * ```\n *\n * Thread Safety:\n * This pool is designed for single-threaded use (Node.js main thread).\n * For worker threads, create separate pools or copy buffers.\n */\nexport class BufferPool {\n private readonly pool: Uint8Array[] = [];\n private readonly chunkSize: number;\n private readonly maxSize: number;\n private readonly autoShrink: boolean;\n private readonly shrinkThreshold: number;\n private readonly metricsEnabled: boolean;\n\n // Metrics\n private inUseCount = 0;\n private createdCount = 0;\n private reusedCount = 0;\n private peakUsage = 0;\n private missCount = 0;\n\n // Track buffers from this pool (for debugging/validation)\n private readonly pooledBuffers = new WeakSet<Uint8Array>();\n\n // Track released buffers to detect double-release\n private readonly releasedBuffers = new WeakSet<Uint8Array>();\n\n constructor(config?: BufferPoolConfig) {\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n this.chunkSize = cfg.chunkSize;\n this.maxSize = cfg.maxSize;\n this.autoShrink = cfg.autoShrink;\n this.shrinkThreshold = cfg.shrinkThreshold;\n this.metricsEnabled = cfg.metricsEnabled;\n\n // Pre-warm pool\n this.prewarm(cfg.initialSize);\n }\n\n /**\n * Acquire a buffer from the pool.\n * Creates new buffer if pool is empty.\n *\n * @returns A Uint8Array of exactly chunkSize bytes\n */\n acquire(): Uint8Array {\n let buffer: Uint8Array;\n\n if (this.pool.length > 0) {\n buffer = this.pool.pop()!;\n this.releasedBuffers.delete(buffer);\n if (this.metricsEnabled) {\n this.reusedCount++;\n }\n } else {\n buffer = this.createBuffer(this.chunkSize);\n if (this.metricsEnabled) {\n this.missCount++;\n }\n }\n\n if (this.metricsEnabled) {\n this.inUseCount++;\n this.peakUsage = Math.max(this.peakUsage, this.inUseCount);\n }\n\n return buffer;\n }\n\n /**\n * Release a buffer back to the pool.\n * Buffer contents are cleared before returning to pool.\n *\n * @param buffer - The buffer to release\n */\n release(buffer: Uint8Array): void {\n // Skip if already released (idempotent)\n if (this.releasedBuffers.has(buffer)) {\n return;\n }\n\n if (this.metricsEnabled) {\n this.inUseCount = Math.max(0, this.inUseCount - 1);\n }\n\n // Don't return oversized buffers to pool - let GC handle\n if (buffer.byteLength > this.chunkSize) {\n return;\n }\n\n // Don't return undersized buffers (shouldn't happen, but be safe)\n if (buffer.byteLength < this.chunkSize) {\n return;\n }\n\n // Don't exceed max pool size - let GC handle excess\n if (this.pool.length >= this.maxSize) {\n return;\n }\n\n // Clear buffer before returning (security + helps with debugging)\n // Note: fill(0) is optimized in V8 for typed arrays\n buffer.fill(0);\n\n this.pool.push(buffer);\n this.releasedBuffers.add(buffer);\n\n // Auto-shrink if needed\n if (this.autoShrink && this.shouldShrink()) {\n this.shrink();\n }\n }\n\n /**\n * Acquire a buffer of specific minimum size.\n * May return a buffer larger than requested.\n * For sizes > chunkSize, creates new buffer (not pooled).\n *\n * @param minSize - Minimum required size in bytes\n * @returns A Uint8Array of at least minSize bytes\n */\n acquireSize(minSize: number): Uint8Array {\n // For zero or negative, return standard chunk\n if (minSize <= 0) {\n return this.acquire();\n }\n\n // If fits in chunk, use pool\n if (minSize <= this.chunkSize) {\n return this.acquire();\n }\n\n // Large buffer - create directly, not pooled\n // These will be GC'd after use\n return this.createBuffer(minSize);\n }\n\n /**\n * Get current pool statistics.\n */\n getStats(): BufferPoolStats {\n const total = this.createdCount + this.reusedCount;\n return {\n available: this.pool.length,\n inUse: this.inUseCount,\n created: this.createdCount,\n reused: this.reusedCount,\n reuseRatio: total > 0 ? this.reusedCount / total : 0,\n poolSizeBytes: this.pool.length * this.chunkSize,\n peakUsage: this.peakUsage,\n misses: this.missCount,\n chunkSize: this.chunkSize,\n maxSize: this.maxSize,\n };\n }\n\n /**\n * Clear all buffers from pool.\n * Use for shutdown or memory pressure situations.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Manually trigger shrink operation.\n * Removes excess idle buffers to reduce memory footprint.\n */\n shrink(): void {\n // Target 50% of threshold to avoid immediate re-shrink\n const targetSize = Math.floor(this.maxSize * (this.shrinkThreshold - 0.25));\n const safeTarget = Math.max(0, targetSize);\n\n while (this.pool.length > safeTarget) {\n this.pool.pop(); // Let GC collect\n }\n }\n\n /**\n * Pre-warm pool by creating buffers up to specified count.\n * Called automatically in constructor.\n *\n * @param count - Number of buffers to create\n */\n prewarm(count?: number): void {\n const targetCount = count ?? DEFAULT_CONFIG.initialSize;\n const toCreate = Math.min(targetCount, this.maxSize) - this.pool.length;\n\n for (let i = 0; i < toCreate; i++) {\n const buffer = this.createBuffer(this.chunkSize);\n this.pool.push(buffer);\n this.releasedBuffers.add(buffer);\n }\n }\n\n /**\n * Reset all metrics to zero.\n * Useful for testing or periodic metric collection.\n */\n resetStats(): void {\n this.createdCount = 0;\n this.reusedCount = 0;\n this.peakUsage = this.inUseCount;\n this.missCount = 0;\n }\n\n /**\n * Get configuration values.\n */\n getConfig(): Readonly<Required<BufferPoolConfig>> {\n return {\n chunkSize: this.chunkSize,\n initialSize: DEFAULT_CONFIG.initialSize,\n maxSize: this.maxSize,\n autoShrink: this.autoShrink,\n shrinkThreshold: this.shrinkThreshold,\n metricsEnabled: this.metricsEnabled,\n };\n }\n\n // ============ Private Methods ============\n\n private createBuffer(size: number): Uint8Array {\n const buffer = new Uint8Array(size);\n this.pooledBuffers.add(buffer);\n if (this.metricsEnabled) {\n this.createdCount++;\n }\n return buffer;\n }\n\n private shouldShrink(): boolean {\n if (this.maxSize === 0) return false;\n const ratio = this.pool.length / this.maxSize;\n return ratio > this.shrinkThreshold;\n }\n}\n\n/**\n * Global shared buffer pool instance.\n * Use this for most serialization operations.\n *\n * For custom configurations or isolated pools, create new BufferPool instances.\n */\nlet globalPool: BufferPool | null = null;\n\n/**\n * Get or create the global buffer pool.\n * Uses default configuration (64KB chunks, 256 max).\n */\nexport function getGlobalBufferPool(): BufferPool {\n if (!globalPool) {\n globalPool = new BufferPool();\n }\n return globalPool;\n}\n\n/**\n * Replace the global buffer pool with a custom instance.\n * Useful for testing or custom configurations.\n *\n * @param pool - New pool instance, or null to reset to default\n */\nexport function setGlobalBufferPool(pool: BufferPool | null): void {\n globalPool = pool;\n}\n","/**\n * ObjectPool - Generic object pooling for reducing GC pressure.\n * Phase 2.04: Object Pool Implementation\n *\n * Reuses objects instead of creating new ones in hot paths.\n * Works with any plain data structure type.\n */\n\nexport interface ObjectPoolConfig<T> {\n /**\n * Factory function to create new objects.\n * Called when pool is empty.\n */\n factory: () => T;\n\n /**\n * Reset function to clean object before reuse.\n * Should clear all fields to initial state.\n */\n reset: (obj: T) => void;\n\n /**\n * Optional validator to check if object is reusable.\n * Return false to discard corrupted objects.\n */\n validate?: (obj: T) => boolean;\n\n /**\n * Initial pool size.\n * Default: 32\n */\n initialSize?: number;\n\n /**\n * Maximum pool size.\n * Excess objects are discarded.\n * Default: 512\n */\n maxSize?: number;\n\n /**\n * Name for debugging/metrics.\n */\n name?: string;\n}\n\nexport interface ObjectPoolStats {\n /** Pool name */\n name: string;\n /** Objects currently available */\n available: number;\n /** Objects currently in use */\n inUse: number;\n /** Total objects created */\n created: number;\n /** Total times an object was reused */\n reused: number;\n /** Reuse ratio */\n reuseRatio: number;\n /** Objects discarded (failed validation) */\n discarded: number;\n /** Max pool size */\n maxSize: number;\n /** Peak concurrent usage */\n peakUsage: number;\n}\n\nconst DEFAULT_INITIAL_SIZE = 32;\nconst DEFAULT_MAX_SIZE = 512;\n\n/**\n * Generic object pool for reusing plain data structures.\n *\n * Usage:\n * ```typescript\n * const pool = new ObjectPool({\n * factory: () => ({ name: '', value: 0 }),\n * reset: (obj) => { obj.name = ''; obj.value = 0; },\n * });\n *\n * const obj = pool.acquire();\n * obj.name = 'test';\n * obj.value = 42;\n * // ... use obj ...\n * pool.release(obj);\n * ```\n *\n * Important:\n * - Only use for plain data objects\n * - Don't pool objects with closures or event listeners\n * - Don't keep references after release\n */\nexport class ObjectPool<T> {\n private readonly pool: T[] = [];\n private readonly factory: () => T;\n private readonly resetFn: (obj: T) => void;\n private readonly validate?: (obj: T) => boolean;\n private readonly maxSize: number;\n private readonly name: string;\n\n // Metrics\n private inUseCount = 0;\n private createdCount = 0;\n private reusedCount = 0;\n private discardedCount = 0;\n private peakUsage = 0;\n\n // Track released objects for idempotent release\n private readonly releasedObjects = new WeakSet<object>();\n\n constructor(config: ObjectPoolConfig<T>) {\n this.factory = config.factory;\n this.resetFn = config.reset;\n this.validate = config.validate;\n this.maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;\n this.name = config.name ?? 'unnamed';\n\n // Pre-warm\n const initialSize = config.initialSize ?? DEFAULT_INITIAL_SIZE;\n this.prewarm(initialSize);\n }\n\n /**\n * Acquire an object from the pool.\n * Creates new object via factory if pool is empty.\n *\n * @returns A clean, ready-to-use object\n */\n acquire(): T {\n let obj: T;\n\n if (this.pool.length > 0) {\n obj = this.pool.pop()!;\n\n // Remove from released set\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.delete(obj as object);\n }\n\n // Validate if validator provided\n if (this.validate && !this.validate(obj)) {\n this.discardedCount++;\n obj = this.createObject();\n } else {\n this.reusedCount++;\n }\n } else {\n obj = this.createObject();\n }\n\n this.inUseCount++;\n this.peakUsage = Math.max(this.peakUsage, this.inUseCount);\n return obj;\n }\n\n /**\n * Release an object back to the pool.\n * Object is reset before returning to pool.\n *\n * @param obj - The object to release\n */\n release(obj: T): void {\n // Skip if already released (idempotent)\n if (typeof obj === 'object' && obj !== null) {\n if (this.releasedObjects.has(obj as object)) {\n return;\n }\n }\n\n this.inUseCount = Math.max(0, this.inUseCount - 1);\n\n // Don't exceed max pool size\n if (this.pool.length >= this.maxSize) {\n return; // Let GC handle it\n }\n\n // Validate before returning\n if (this.validate && !this.validate(obj)) {\n this.discardedCount++;\n return;\n }\n\n // Reset object\n this.resetFn(obj);\n\n // Track as released\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.add(obj as object);\n }\n\n this.pool.push(obj);\n }\n\n /**\n * Acquire multiple objects at once.\n * More efficient than multiple acquire() calls.\n *\n * @param count - Number of objects to acquire\n * @returns Array of objects\n */\n acquireBatch(count: number): T[] {\n const result: T[] = new Array(count);\n for (let i = 0; i < count; i++) {\n result[i] = this.acquire();\n }\n return result;\n }\n\n /**\n * Release multiple objects at once.\n *\n * @param objects - Objects to release\n */\n releaseBatch(objects: T[]): void {\n for (const obj of objects) {\n this.release(obj);\n }\n }\n\n /**\n * Get pool statistics.\n */\n getStats(): ObjectPoolStats {\n const total = this.createdCount + this.reusedCount;\n return {\n name: this.name,\n available: this.pool.length,\n inUse: this.inUseCount,\n created: this.createdCount,\n reused: this.reusedCount,\n reuseRatio: total > 0 ? this.reusedCount / total : 0,\n discarded: this.discardedCount,\n maxSize: this.maxSize,\n peakUsage: this.peakUsage,\n };\n }\n\n /**\n * Clear all objects from pool.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Pre-warm pool with objects.\n *\n * @param count - Number of objects to create\n */\n prewarm(count: number = DEFAULT_INITIAL_SIZE): void {\n const toCreate = Math.min(count, this.maxSize) - this.pool.length;\n for (let i = 0; i < toCreate; i++) {\n const obj = this.createObject();\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.add(obj as object);\n }\n this.pool.push(obj);\n }\n }\n\n /**\n * Reset statistics.\n */\n resetStats(): void {\n this.createdCount = 0;\n this.reusedCount = 0;\n this.discardedCount = 0;\n this.peakUsage = this.inUseCount;\n }\n\n // ============ Private Methods ============\n\n private createObject(): T {\n this.createdCount++;\n return this.factory();\n }\n}\n","/**\n * MessagePool - Pre-configured ObjectPool for message objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure when processing incoming messages.\n */\n\nimport { ObjectPool } from '../ObjectPool';\n\n/**\n * Pooled message structure matching common message format.\n */\nexport interface PooledMessage {\n type: string;\n payload: unknown;\n timestamp: number | null;\n clientId: string | null;\n mapName: string | null;\n key: string | null;\n}\n\nconst DEFAULT_MAX_SIZE = 1024;\n\n/**\n * Create a new MessagePool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for messages\n */\nexport function createMessagePool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledMessage> {\n return new ObjectPool<PooledMessage>({\n name: 'message',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n type: '',\n payload: null,\n timestamp: null,\n clientId: null,\n mapName: null,\n key: null,\n }),\n reset: (msg) => {\n msg.type = '';\n msg.payload = null;\n msg.timestamp = null;\n msg.clientId = null;\n msg.mapName = null;\n msg.key = null;\n },\n });\n}\n\n// Global singleton instance\nlet globalMessagePool: ObjectPool<PooledMessage> | null = null;\n\n/**\n * Get or create the global message pool.\n */\nexport function getGlobalMessagePool(): ObjectPool<PooledMessage> {\n if (!globalMessagePool) {\n globalMessagePool = createMessagePool();\n }\n return globalMessagePool;\n}\n\n/**\n * Replace the global message pool (for testing).\n */\nexport function setGlobalMessagePool(pool: ObjectPool<PooledMessage> | null): void {\n globalMessagePool = pool;\n}\n","/**\n * TimestampPool - Pre-configured ObjectPool for HLC timestamp objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure for frequent timestamp operations.\n * Timestamps are created on every operation (set, merge, etc.).\n */\n\nimport { ObjectPool } from '../ObjectPool';\n\n/**\n * Pooled timestamp structure matching HLC format.\n */\nexport interface PooledTimestamp {\n millis: number;\n counter: number;\n nodeId: string;\n}\n\nconst DEFAULT_MAX_SIZE = 2048;\n\n/**\n * Create a new TimestampPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for timestamps\n */\nexport function createTimestampPool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledTimestamp> {\n return new ObjectPool<PooledTimestamp>({\n name: 'timestamp',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 128,\n factory: () => ({\n millis: 0,\n counter: 0,\n nodeId: '',\n }),\n reset: (ts) => {\n ts.millis = 0;\n ts.counter = 0;\n ts.nodeId = '';\n },\n validate: (ts) => {\n // Ensure fields are correct type (for corruption detection)\n return (\n typeof ts.millis === 'number' &&\n typeof ts.counter === 'number' &&\n typeof ts.nodeId === 'string'\n );\n },\n });\n}\n\n// Global singleton instance\nlet globalTimestampPool: ObjectPool<PooledTimestamp> | null = null;\n\n/**\n * Get or create the global timestamp pool.\n */\nexport function getGlobalTimestampPool(): ObjectPool<PooledTimestamp> {\n if (!globalTimestampPool) {\n globalTimestampPool = createTimestampPool();\n }\n return globalTimestampPool;\n}\n\n/**\n * Replace the global timestamp pool (for testing).\n */\nexport function setGlobalTimestampPool(pool: ObjectPool<PooledTimestamp> | null): void {\n globalTimestampPool = pool;\n}\n","/**\n * RecordPool - Pre-configured ObjectPool for LWW/OR record objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure when processing CRDT operations.\n */\n\nimport { ObjectPool } from '../ObjectPool';\nimport type { PooledTimestamp } from './TimestampPool';\n\n/**\n * Pooled record structure matching LWWRecord format.\n */\nexport interface PooledRecord<T = unknown> {\n value: T | null;\n timestamp: PooledTimestamp | null;\n ttlMs?: number;\n}\n\n/**\n * Pooled event payload structure.\n */\nexport interface PooledEventPayload {\n mapName: string;\n key: string;\n eventType: string;\n record: PooledRecord | null;\n orRecord: PooledRecord | null;\n orTag: string | null;\n}\n\nconst DEFAULT_MAX_SIZE = 4096;\nconst DEFAULT_EVENT_MAX_SIZE = 2048;\n\n/**\n * Create a new RecordPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for records\n */\nexport function createRecordPool<T = unknown>(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledRecord<T>> {\n return new ObjectPool<PooledRecord<T>>({\n name: 'record',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n value: null,\n timestamp: null,\n ttlMs: undefined,\n }),\n reset: (rec) => {\n rec.value = null;\n rec.timestamp = null;\n rec.ttlMs = undefined;\n },\n });\n}\n\n/**\n * Create a new EventPayloadPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for event payloads\n */\nexport function createEventPayloadPool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledEventPayload> {\n return new ObjectPool<PooledEventPayload>({\n name: 'eventPayload',\n maxSize: config?.maxSize ?? DEFAULT_EVENT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n mapName: '',\n key: '',\n eventType: '',\n record: null,\n orRecord: null,\n orTag: null,\n }),\n reset: (payload) => {\n payload.mapName = '';\n payload.key = '';\n payload.eventType = '';\n payload.record = null;\n payload.orRecord = null;\n payload.orTag = null;\n },\n });\n}\n\n// Global singleton instances\nlet globalRecordPool: ObjectPool<PooledRecord> | null = null;\nlet globalEventPayloadPool: ObjectPool<PooledEventPayload> | null = null;\n\n/**\n * Get or create the global record pool.\n */\nexport function getGlobalRecordPool(): ObjectPool<PooledRecord> {\n if (!globalRecordPool) {\n globalRecordPool = createRecordPool();\n }\n return globalRecordPool;\n}\n\n/**\n * Replace the global record pool (for testing).\n */\nexport function setGlobalRecordPool(pool: ObjectPool<PooledRecord> | null): void {\n globalRecordPool = pool;\n}\n\n/**\n * Get or create the global event payload pool.\n */\nexport function getGlobalEventPayloadPool(): ObjectPool<PooledEventPayload> {\n if (!globalEventPayloadPool) {\n globalEventPayloadPool = createEventPayloadPool();\n }\n return globalEventPayloadPool;\n}\n\n/**\n * Replace the global event payload pool (for testing).\n */\nexport function setGlobalEventPayloadPool(pool: ObjectPool<PooledEventPayload> | null): void {\n globalEventPayloadPool = pool;\n}\n","import type { CoalescingWriterOptions } from './CoalescingWriter';\n\n/**\n * Preset configurations for CoalescingWriter.\n * Based on Hazelcast OutboxImpl (batch size 2048) and real-world benchmarking.\n *\n * Trade-offs:\n * - Larger batch size = higher throughput, higher latency\n * - Longer delay = more messages per batch, higher latency\n * - Larger maxBatchBytes = handles larger payloads, more memory\n *\n * NOTE: A/B testing (Dec 2024) showed maxDelayMs is the primary bottleneck:\n * - 10ms delay: ~10K ops/sec, p50=11ms\n * - 1ms delay: ~18K ops/sec, p50=8ms (+80% throughput)\n * - 0ms (disabled): ~18K ops/sec, p50=2ms (best latency)\n */\nexport const coalescingPresets = {\n /**\n * Low latency - optimized for minimal response time.\n * Best for: gaming, real-time chat, interactive applications.\n * Benchmark: p50=2ms, ~18K ops/sec\n */\n lowLatency: {\n maxBatchSize: 100,\n maxDelayMs: 1,\n maxBatchBytes: 65536, // 64KB\n },\n\n /**\n * Conservative - good balance of latency and batching.\n * Use for: general purpose with latency sensitivity.\n */\n conservative: {\n maxBatchSize: 100,\n maxDelayMs: 2,\n maxBatchBytes: 65536, // 64KB\n },\n\n /**\n * Balanced - good for most workloads.\n * Reasonable trade-off between throughput and latency.\n * Use for: mixed read/write applications, general purpose.\n */\n balanced: {\n maxBatchSize: 300,\n maxDelayMs: 2,\n maxBatchBytes: 131072, // 128KB\n },\n\n /**\n * High throughput - optimized for write-heavy workloads.\n * Higher batching for better network utilization.\n * Use for: data ingestion, logging, IoT data streams.\n * Benchmark: p50=7ms, ~18K ops/sec\n */\n highThroughput: {\n maxBatchSize: 500,\n maxDelayMs: 2,\n maxBatchBytes: 262144, // 256KB\n },\n\n /**\n * Aggressive - maximum batching for batch processing.\n * Closest to Hazelcast's OutboxImpl (batch size 2048).\n * Use for: batch imports, bulk operations, offline sync.\n */\n aggressive: {\n maxBatchSize: 1000,\n maxDelayMs: 5,\n maxBatchBytes: 524288, // 512KB\n },\n} as const satisfies Record<string, CoalescingWriterOptions>;\n\n/**\n * Available preset names for type safety.\n */\nexport type CoalescingPreset = keyof typeof coalescingPresets;\n\n/**\n * Get preset configuration by name.\n * @param preset - Preset name\n * @returns CoalescingWriterOptions\n */\nexport function getCoalescingPreset(preset: CoalescingPreset): CoalescingWriterOptions {\n return { ...coalescingPresets[preset] };\n}\n","/**\n * ConnectionRateLimiter - Rate limiter for incoming WebSocket connections.\n *\n * Implements connection rate limiting to prevent connection storms and\n * protect the server from being overwhelmed during high load scenarios.\n *\n * Features:\n * - Limits connections per second to prevent TCP backlog exhaustion\n * - Tracks pending connections (in-progress handshakes)\n * - Provides graceful rejection when limits are exceeded\n * - Auto-resets counters after cooldown period\n */\n\nimport { logger } from './logger';\n\nexport interface RateLimiterConfig {\n /** Maximum new connections allowed per second (default: 100) */\n maxConnectionsPerSecond: number;\n /** Maximum pending connections waiting for handshake (default: 1000) */\n maxPendingConnections: number;\n /** Cooldown period in ms after which counters reset (default: 1000) */\n cooldownMs: number;\n}\n\nexport interface RateLimiterStats {\n /** Current connections per second rate */\n connectionsPerSecond: number;\n /** Number of connections currently pending (handshake in progress) */\n pendingConnections: number;\n /** Total connections established since start */\n totalConnections: number;\n /** Total connections rejected due to rate limiting */\n totalRejected: number;\n}\n\nconst DEFAULT_CONFIG: RateLimiterConfig = {\n maxConnectionsPerSecond: 100,\n maxPendingConnections: 1000,\n cooldownMs: 1000,\n};\n\nexport class ConnectionRateLimiter {\n private config: RateLimiterConfig;\n\n /** Connection attempts in current window */\n private connectionCount: number = 0;\n\n /** Timestamp when current window started */\n private windowStart: number = Date.now();\n\n /** Currently pending connections (handshake in progress) */\n private pendingCount: number = 0;\n\n /** Total connections established since start */\n private totalConnections: number = 0;\n\n /** Total connections rejected since start */\n private totalRejected: number = 0;\n\n constructor(config?: Partial<RateLimiterConfig>) {\n this.config = {\n ...DEFAULT_CONFIG,\n ...config,\n };\n }\n\n /**\n * Check if a new connection should be accepted.\n * @returns true if the connection should be accepted, false if it should be rejected\n */\n shouldAccept(): boolean {\n this.maybeResetWindow();\n\n // Check if pending connections limit is exceeded\n if (this.pendingCount >= this.config.maxPendingConnections) {\n logger.debug(\n { pendingCount: this.pendingCount, maxPending: this.config.maxPendingConnections },\n 'Connection rejected: pending connections limit exceeded'\n );\n return false;\n }\n\n // Check if connections per second limit is exceeded\n if (this.connectionCount >= this.config.maxConnectionsPerSecond) {\n logger.debug(\n { connectionCount: this.connectionCount, maxPerSecond: this.config.maxConnectionsPerSecond },\n 'Connection rejected: rate limit exceeded'\n );\n return false;\n }\n\n return true;\n }\n\n /**\n * Register a connection attempt.\n * Call this when a new connection is initiated (before handshake completes).\n */\n onConnectionAttempt(): void {\n this.maybeResetWindow();\n this.connectionCount++;\n this.pendingCount++;\n }\n\n /**\n * Register that a connection has been established (handshake complete).\n * Call this when the connection is fully established and authenticated.\n */\n onConnectionEstablished(): void {\n this.pendingCount = Math.max(0, this.pendingCount - 1);\n this.totalConnections++;\n }\n\n /**\n * Register that a connection has been closed.\n * Call this when a pending connection is closed before completing handshake.\n */\n onConnectionClosed(): void {\n // If connection was pending, decrement pending count\n // Note: We can't distinguish between pending and established connections here,\n // so this is a best-effort tracking. In practice, onConnectionEstablished\n // should be called first for established connections.\n }\n\n /**\n * Register that a connection was rejected.\n * Call this when shouldAccept() returns false and the connection is rejected.\n */\n onConnectionRejected(): void {\n this.totalRejected++;\n }\n\n /**\n * Decrease pending count when a connection times out or fails.\n * Call this when a pending connection fails to complete handshake.\n */\n onPendingConnectionFailed(): void {\n this.pendingCount = Math.max(0, this.pendingCount - 1);\n }\n\n /**\n * Get current rate limiter statistics.\n */\n getStats(): RateLimiterStats {\n this.maybeResetWindow();\n\n // Calculate actual connections per second\n const elapsed = Date.now() - this.windowStart;\n const connectionsPerSecond = elapsed > 0\n ? Math.round((this.connectionCount / elapsed) * 1000)\n : this.connectionCount;\n\n return {\n connectionsPerSecond,\n pendingConnections: this.pendingCount,\n totalConnections: this.totalConnections,\n totalRejected: this.totalRejected,\n };\n }\n\n /**\n * Reset the rate limiter state.\n * Useful for testing or when recovering from errors.\n */\n reset(): void {\n this.connectionCount = 0;\n this.windowStart = Date.now();\n this.pendingCount = 0;\n this.totalConnections = 0;\n this.totalRejected = 0;\n }\n\n /**\n * Update configuration at runtime.\n */\n updateConfig(config: Partial<RateLimiterConfig>): void {\n this.config = {\n ...this.config,\n ...config,\n };\n }\n\n /**\n * Check if window has expired and reset if needed.\n */\n private maybeResetWindow(): void {\n const now = Date.now();\n if (now - this.windowStart >= this.config.cooldownMs) {\n this.connectionCount = 0;\n this.windowStart = now;\n }\n }\n}\n","/**\n * WorkerPool Implementation\n * Phase 1.02: Worker Threads Implementation\n *\n * Manages a pool of worker threads for CPU-bound operations.\n * Features:\n * - Auto-scaling (minWorkers to maxWorkers)\n * - Priority queue (high > normal > low)\n * - Task timeouts\n * - Worker crash recovery\n * - Graceful shutdown\n */\n\nimport { Worker } from 'worker_threads';\nimport { cpus } from 'os';\nimport { join } from 'path';\nimport {\n WorkerPoolConfig,\n WorkerTask,\n WorkerPoolStats,\n WorkerState,\n PendingTask,\n WorkerMessage,\n WorkerResponse,\n TaskPriority,\n} from './types';\nimport {\n WorkerTimeoutError,\n WorkerTaskError,\n WorkerPoolShutdownError,\n WorkerCrashError,\n} from './errors';\n\n// Priority order for queue sorting\nconst PRIORITY_ORDER: Record<TaskPriority, number> = {\n high: 0,\n normal: 1,\n low: 2,\n};\n\nexport class WorkerPool {\n private readonly config: Required<Omit<WorkerPoolConfig, 'workerScript'>> & { workerScript: string };\n private readonly workers: Map<number, WorkerState> = new Map();\n private readonly taskQueue: PendingTask[] = [];\n private readonly pendingTasks: Map<string, PendingTask> = new Map();\n\n private workerIdCounter = 0;\n private isShuttingDown = false;\n private isShutdown = false;\n\n // Statistics\n private completedTaskCount = 0;\n private failedTaskCount = 0;\n private totalTaskDuration = 0;\n\n // Idle check interval\n private idleCheckInterval?: NodeJS.Timeout;\n\n constructor(config?: WorkerPoolConfig) {\n const cpuCount = cpus().length;\n\n // Determine worker script path\n // In production: compiled .js file\n // In tests: .ts file needs ts-node registration\n const defaultWorkerScript = this.resolveWorkerScript();\n\n this.config = {\n minWorkers: config?.minWorkers ?? 2,\n maxWorkers: config?.maxWorkers ?? Math.max(1, cpuCount - 1),\n taskTimeout: config?.taskTimeout ?? 5000,\n idleTimeout: config?.idleTimeout ?? 30000,\n autoRestart: config?.autoRestart ?? true,\n workerScript: config?.workerScript ?? defaultWorkerScript,\n };\n\n // Validate config\n if (this.config.minWorkers < 1) {\n this.config.minWorkers = 1;\n }\n if (this.config.maxWorkers < this.config.minWorkers) {\n this.config.maxWorkers = this.config.minWorkers;\n }\n\n // Initialize minimum workers\n this.initializeWorkers();\n\n // Start idle check interval\n this.startIdleCheck();\n }\n\n /**\n * Resolve the worker script path based on environment\n */\n private resolveWorkerScript(): string {\n // Check if we're in a compiled environment (.js files)\n const jsPath = join(__dirname, 'worker-scripts', 'base.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'base.worker.ts');\n\n // Prefer .js if it exists (production), otherwise use .ts (development/test)\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n // .js not found, try .ts for ts-node environments\n return tsPath;\n }\n }\n\n /**\n * Submit a task to the pool\n */\n public submit<TPayload, TResult>(\n task: WorkerTask<TPayload, TResult>\n ): Promise<TResult> {\n return new Promise((resolve, reject) => {\n if (this.isShuttingDown || this.isShutdown) {\n reject(new WorkerPoolShutdownError());\n return;\n }\n\n const pendingTask: PendingTask<TResult> = {\n task: task as WorkerTask<unknown, TResult>,\n resolve,\n reject,\n submittedAt: Date.now(),\n };\n\n // Set up timeout\n if (this.config.taskTimeout > 0) {\n pendingTask.timeoutId = setTimeout(() => {\n this.handleTaskTimeout(task.id);\n }, this.config.taskTimeout);\n }\n\n // Add to pending map\n this.pendingTasks.set(task.id, pendingTask as PendingTask);\n\n // Try to assign to idle worker\n const idleWorker = this.findIdleWorker();\n if (idleWorker) {\n this.assignTaskToWorker(idleWorker, pendingTask as PendingTask);\n } else {\n // Queue the task with priority\n this.enqueueTask(pendingTask as PendingTask);\n\n // Try to scale up if possible\n this.tryScaleUp();\n }\n });\n }\n\n /**\n * Get current pool statistics\n */\n public getStats(): WorkerPoolStats {\n let activeWorkers = 0;\n let idleWorkers = 0;\n\n for (const state of this.workers.values()) {\n if (state.busy) {\n activeWorkers++;\n } else {\n idleWorkers++;\n }\n }\n\n return {\n activeWorkers,\n idleWorkers,\n pendingTasks: this.taskQueue.length,\n completedTasks: this.completedTaskCount,\n failedTasks: this.failedTaskCount,\n avgTaskDuration:\n this.completedTaskCount > 0\n ? this.totalTaskDuration / this.completedTaskCount\n : 0,\n };\n }\n\n /**\n * Gracefully shutdown all workers\n */\n public async shutdown(timeout = 10000): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n // Stop idle check\n if (this.idleCheckInterval) {\n clearInterval(this.idleCheckInterval);\n this.idleCheckInterval = undefined;\n }\n\n // Reject all queued tasks\n for (const pendingTask of this.taskQueue) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n pendingTask.reject(new WorkerPoolShutdownError());\n }\n this.taskQueue.length = 0;\n\n // Wait for active tasks to complete (with timeout)\n const startTime = Date.now();\n while (this.pendingTasks.size > 0 && Date.now() - startTime < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n // Force reject remaining pending tasks\n for (const [taskId, pendingTask] of this.pendingTasks) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n pendingTask.reject(new WorkerPoolShutdownError());\n this.pendingTasks.delete(taskId);\n }\n\n // Terminate all workers\n const terminatePromises: Promise<number>[] = [];\n for (const [workerId, state] of this.workers) {\n terminatePromises.push(\n state.worker.terminate().then(() => workerId)\n );\n }\n\n await Promise.all(terminatePromises);\n this.workers.clear();\n\n this.isShutdown = true;\n this.isShuttingDown = false;\n }\n\n /**\n * Check if pool is accepting tasks\n */\n public isRunning(): boolean {\n return !this.isShuttingDown && !this.isShutdown;\n }\n\n // ============ Private Methods ============\n\n private initializeWorkers(): void {\n for (let i = 0; i < this.config.minWorkers; i++) {\n this.createWorker();\n }\n }\n\n private createWorker(): WorkerState | null {\n if (this.isShuttingDown || this.isShutdown) {\n return null;\n }\n\n try {\n const workerId = ++this.workerIdCounter;\n\n // For TypeScript files in test environment, use ts-node\n const workerOptions = this.config.workerScript.endsWith('.ts')\n ? { execArgv: ['--require', 'ts-node/register'] }\n : {};\n\n const worker = new Worker(this.config.workerScript, workerOptions);\n\n const state: WorkerState = {\n worker,\n busy: false,\n idleSince: Date.now(),\n tasksCompleted: 0,\n };\n\n // Handle messages from worker\n worker.on('message', (response: WorkerResponse) => {\n this.handleWorkerResponse(workerId, response);\n });\n\n // Handle worker errors\n worker.on('error', (error) => {\n console.error(`Worker ${workerId} error:`, error);\n this.handleWorkerError(workerId, error);\n });\n\n // Handle worker exit\n worker.on('exit', (code) => {\n this.handleWorkerExit(workerId, code);\n });\n\n this.workers.set(workerId, state);\n return state;\n } catch (error) {\n console.error('Failed to create worker:', error);\n return null;\n }\n }\n\n private findIdleWorker(): WorkerState | undefined {\n for (const state of this.workers.values()) {\n if (!state.busy) {\n return state;\n }\n }\n return undefined;\n }\n\n private assignTaskToWorker(\n workerState: WorkerState,\n pendingTask: PendingTask\n ): void {\n workerState.busy = true;\n workerState.currentTaskId = pendingTask.task.id;\n workerState.idleSince = undefined;\n\n const message: WorkerMessage = {\n id: pendingTask.task.id,\n type: pendingTask.task.type,\n payload: pendingTask.task.payload,\n };\n\n workerState.worker.postMessage(message);\n }\n\n private enqueueTask(pendingTask: PendingTask): void {\n const priority = pendingTask.task.priority ?? 'normal';\n const priorityOrder = PRIORITY_ORDER[priority];\n\n // Find insertion point (maintain priority order, FIFO within same priority)\n let insertIndex = this.taskQueue.length;\n for (let i = 0; i < this.taskQueue.length; i++) {\n const queuedPriority = this.taskQueue[i].task.priority ?? 'normal';\n if (PRIORITY_ORDER[queuedPriority] > priorityOrder) {\n insertIndex = i;\n break;\n }\n }\n\n this.taskQueue.splice(insertIndex, 0, pendingTask);\n }\n\n private tryScaleUp(): void {\n if (this.workers.size < this.config.maxWorkers) {\n const newWorker = this.createWorker();\n if (newWorker && this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(newWorker, nextTask);\n }\n }\n }\n\n private handleWorkerResponse(\n workerId: number,\n response: WorkerResponse\n ): void {\n const pendingTask = this.pendingTasks.get(response.id);\n if (!pendingTask) {\n // Task already timed out or cancelled\n return;\n }\n\n // Clear timeout\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n\n // Remove from pending\n this.pendingTasks.delete(response.id);\n\n // Update stats\n const duration = Date.now() - pendingTask.submittedAt;\n this.totalTaskDuration += duration;\n\n // Resolve or reject\n if (response.success) {\n this.completedTaskCount++;\n pendingTask.resolve(response.result);\n } else {\n this.failedTaskCount++;\n pendingTask.reject(\n new WorkerTaskError(response.id, response.error ?? 'Unknown error')\n );\n }\n\n // Mark worker as idle and process next task\n const workerState = this.workers.get(workerId);\n if (workerState) {\n workerState.busy = false;\n workerState.currentTaskId = undefined;\n workerState.idleSince = Date.now();\n workerState.tasksCompleted++;\n\n // Process next task from queue\n if (this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(workerState, nextTask);\n }\n }\n }\n\n private handleTaskTimeout(taskId: string): void {\n const pendingTask = this.pendingTasks.get(taskId);\n if (!pendingTask) {\n return;\n }\n\n // Remove from pending\n this.pendingTasks.delete(taskId);\n this.failedTaskCount++;\n\n // Reject with timeout error\n pendingTask.reject(\n new WorkerTimeoutError(taskId, this.config.taskTimeout)\n );\n\n // Note: The worker will continue processing and send a response,\n // but it will be ignored since we removed from pendingTasks.\n // The worker itself is not killed - it will become available for\n // next task when it finishes.\n }\n\n private handleWorkerError(workerId: number, error: Error): void {\n const workerState = this.workers.get(workerId);\n if (!workerState) {\n return;\n }\n\n // If worker was processing a task, reject it\n if (workerState.currentTaskId) {\n const pendingTask = this.pendingTasks.get(workerState.currentTaskId);\n if (pendingTask) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n this.pendingTasks.delete(workerState.currentTaskId);\n this.failedTaskCount++;\n pendingTask.reject(\n new WorkerTaskError(workerState.currentTaskId, error.message)\n );\n }\n }\n }\n\n private handleWorkerExit(workerId: number, exitCode: number | null): void {\n const workerState = this.workers.get(workerId);\n this.workers.delete(workerId);\n\n if (!workerState) {\n return;\n }\n\n // If worker was processing a task, reject it\n if (workerState.currentTaskId) {\n const pendingTask = this.pendingTasks.get(workerState.currentTaskId);\n if (pendingTask) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n this.pendingTasks.delete(workerState.currentTaskId);\n this.failedTaskCount++;\n pendingTask.reject(new WorkerCrashError(workerId, exitCode));\n }\n }\n\n // Auto-restart if needed\n if (\n this.config.autoRestart &&\n !this.isShuttingDown &&\n !this.isShutdown &&\n this.workers.size < this.config.minWorkers\n ) {\n const newWorker = this.createWorker();\n if (newWorker && this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(newWorker, nextTask);\n }\n }\n }\n\n private startIdleCheck(): void {\n this.idleCheckInterval = setInterval(() => {\n this.checkIdleWorkers();\n }, 5000); // Check every 5 seconds\n\n // Don't prevent process from exiting\n this.idleCheckInterval.unref();\n }\n\n private checkIdleWorkers(): void {\n if (this.isShuttingDown || this.isShutdown) {\n return;\n }\n\n const now = Date.now();\n const workersToTerminate: number[] = [];\n\n for (const [workerId, state] of this.workers) {\n // Don't terminate if we're at minimum\n if (this.workers.size <= this.config.minWorkers) {\n break;\n }\n\n // Check if worker is idle and past timeout\n if (\n !state.busy &&\n state.idleSince &&\n now - state.idleSince > this.config.idleTimeout\n ) {\n workersToTerminate.push(workerId);\n\n // Don't terminate below minimum\n if (\n this.workers.size - workersToTerminate.length <=\n this.config.minWorkers\n ) {\n break;\n }\n }\n }\n\n // Terminate idle workers\n for (const workerId of workersToTerminate) {\n const state = this.workers.get(workerId);\n if (state) {\n this.workers.delete(workerId);\n state.worker.terminate().catch(() => {\n // Ignore termination errors\n });\n }\n }\n }\n}\n","/**\n * WorkerPool Custom Errors\n * Phase 1.02: Worker Threads Implementation\n */\n\n/**\n * Base error class for worker-related errors\n */\nexport class WorkerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'WorkerError';\n // Maintain proper stack trace in V8\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Thrown when a task exceeds its timeout\n */\nexport class WorkerTimeoutError extends WorkerError {\n public readonly taskId: string;\n public readonly timeout: number;\n\n constructor(taskId: string, timeout: number) {\n super(`Task ${taskId} timed out after ${timeout}ms`);\n this.name = 'WorkerTimeoutError';\n this.taskId = taskId;\n this.timeout = timeout;\n }\n}\n\n/**\n * Thrown when a task fails during execution\n */\nexport class WorkerTaskError extends WorkerError {\n public readonly taskId: string;\n public readonly originalError: string;\n\n constructor(taskId: string, originalError: string) {\n super(`Task ${taskId} failed: ${originalError}`);\n this.name = 'WorkerTaskError';\n this.taskId = taskId;\n this.originalError = originalError;\n }\n}\n\n/**\n * Thrown when trying to submit a task to a shutdown pool\n */\nexport class WorkerPoolShutdownError extends WorkerError {\n constructor() {\n super('Cannot submit task: WorkerPool is shutting down or already shut down');\n this.name = 'WorkerPoolShutdownError';\n }\n}\n\n/**\n * Thrown when a worker crashes unexpectedly\n */\nexport class WorkerCrashError extends WorkerError {\n public readonly workerId: number;\n public readonly exitCode: number | null;\n\n constructor(workerId: number, exitCode: number | null) {\n super(`Worker ${workerId} crashed with exit code ${exitCode}`);\n this.name = 'WorkerCrashError';\n this.workerId = workerId;\n this.exitCode = exitCode;\n }\n}\n","/**\n * MerkleWorker - High-level API for Merkle tree operations in worker threads\n * Phase 1.03: MerkleWorker Implementation\n *\n * Provides a clean interface for CPU-intensive Merkle tree operations.\n * Delegates actual work to worker threads via WorkerPool.\n */\n\nimport { join } from 'path';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n MerkleHashPayload,\n MerkleHashResult,\n ORMapMerkleHashPayload,\n ORMapMerkleHashResult,\n MerkleDiffPayload,\n MerkleDiffResult,\n MerkleRebuildPayload,\n MerkleRebuildResult,\n ORMapMerkleRebuildPayload,\n BucketInfo,\n} from './merkle-types';\nimport { hashString as coreHashString } from '@topgunbuild/core';\n\n// Threshold: use worker only if entries exceed this count\nconst WORKER_THRESHOLD = 10;\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `merkle-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * MerkleWorker provides methods for Merkle tree operations.\n * Automatically decides whether to use worker threads based on workload size.\n */\nexport class MerkleWorker {\n private readonly pool: WorkerPool;\n private readonly workerScript: string;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n // Resolve path to merkle worker script\n this.workerScript = this.resolveMerkleWorkerScript();\n }\n\n private resolveMerkleWorkerScript(): string {\n const jsPath = join(__dirname, 'worker-scripts', 'merkle.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'merkle.worker.ts');\n\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n return tsPath;\n }\n }\n\n /**\n * Compute hashes for a batch of LWWMap entries.\n * Uses worker thread if entries count exceeds threshold.\n */\n async computeHashes(payload: MerkleHashPayload): Promise<MerkleHashResult> {\n if (payload.entries.length < WORKER_THRESHOLD) {\n // Execute inline for small batches\n return this.computeHashesInline(payload);\n }\n\n const task: WorkerTask<MerkleHashPayload, MerkleHashResult> = {\n id: generateTaskId(),\n type: 'merkle-hash' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Compute hashes for a batch of ORMap entries.\n * Uses worker thread if entries count exceeds threshold.\n */\n async computeORMapHashes(payload: ORMapMerkleHashPayload): Promise<ORMapMerkleHashResult> {\n if (payload.entries.length < WORKER_THRESHOLD) {\n return this.computeORMapHashesInline(payload);\n }\n\n const task: WorkerTask<ORMapMerkleHashPayload, ORMapMerkleHashResult> = {\n id: generateTaskId(),\n type: 'merkle-hash-ormap' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Find differences between local and remote Merkle trees.\n */\n async diff(payload: MerkleDiffPayload): Promise<MerkleDiffResult> {\n const totalKeys =\n payload.localBuckets.reduce((sum, [, b]) => sum + b.keys.length, 0) +\n payload.remoteBuckets.reduce((sum, [, b]) => sum + b.keys.length, 0);\n\n if (totalKeys < WORKER_THRESHOLD * 2) {\n return this.diffInline(payload);\n }\n\n const task: WorkerTask<MerkleDiffPayload, MerkleDiffResult> = {\n id: generateTaskId(),\n type: 'merkle-diff' as WorkerTaskType,\n payload,\n priority: 'high', // Sync operations should be prioritized\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Rebuild Merkle tree from LWWMap records.\n * Always uses worker thread as this is typically a heavy operation.\n */\n async rebuild(payload: MerkleRebuildPayload): Promise<MerkleRebuildResult> {\n if (payload.records.length < WORKER_THRESHOLD) {\n return this.rebuildInline(payload);\n }\n\n const task: WorkerTask<MerkleRebuildPayload, MerkleRebuildResult> = {\n id: generateTaskId(),\n type: 'merkle-rebuild' as WorkerTaskType,\n payload,\n priority: 'low', // Rebuild can wait\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Rebuild Merkle tree from ORMap records.\n */\n async rebuildORMap(payload: ORMapMerkleRebuildPayload): Promise<MerkleRebuildResult> {\n if (payload.records.length < WORKER_THRESHOLD) {\n return this.rebuildORMapInline(payload);\n }\n\n const task: WorkerTask<ORMapMerkleRebuildPayload, MerkleRebuildResult> = {\n id: generateTaskId(),\n type: 'merkle-rebuild-ormap' as WorkerTaskType,\n payload,\n priority: 'low',\n };\n\n return this.pool.submit(task);\n }\n\n // ============ Inline implementations for small batches ============\n\n private computeHashesInline(payload: MerkleHashPayload): MerkleHashResult {\n const { entries, depth = 3 } = payload;\n const hashes: Array<[string, number]> = [];\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const entry of entries) {\n const itemHash = this.hashString(\n `${entry.key}:${entry.timestamp.millis}:${entry.timestamp.counter}:${entry.timestamp.nodeId}`\n );\n hashes.push([entry.key, itemHash]);\n hashEntries.push({ key: entry.key, hash: itemHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n hashes,\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private computeORMapHashesInline(payload: ORMapMerkleHashPayload): ORMapMerkleHashResult {\n const { entries, depth = 3 } = payload;\n const hashes: Array<[string, number]> = [];\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const entry of entries) {\n const sortedRecords = [...entry.records].sort((a, b) => a.tag.localeCompare(b.tag));\n let combinedStr = entry.key;\n for (const record of sortedRecords) {\n combinedStr += `:${record.tag}:${record.timestamp.millis}:${record.timestamp.counter}:${record.timestamp.nodeId}`;\n }\n const entryHash = this.hashString(combinedStr);\n hashes.push([entry.key, entryHash]);\n hashEntries.push({ key: entry.key, hash: entryHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n hashes,\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private diffInline(payload: MerkleDiffPayload): MerkleDiffResult {\n const localMap = new Map<string, BucketInfo>(payload.localBuckets);\n const remoteMap = new Map<string, BucketInfo>(payload.remoteBuckets);\n\n const missingLocal: string[] = [];\n const missingRemote: string[] = [];\n const differingPaths: string[] = [];\n\n for (const [path, remoteBucket] of remoteMap) {\n const localBucket = localMap.get(path);\n\n if (!localBucket) {\n missingLocal.push(...remoteBucket.keys);\n } else if (localBucket.hash !== remoteBucket.hash) {\n differingPaths.push(path);\n\n const localKeys = new Set(localBucket.keys);\n const remoteKeys = new Set(remoteBucket.keys);\n\n for (const key of remoteKeys) {\n if (!localKeys.has(key)) {\n missingLocal.push(key);\n }\n }\n\n for (const key of localKeys) {\n if (!remoteKeys.has(key)) {\n missingRemote.push(key);\n }\n }\n }\n }\n\n for (const [path, localBucket] of localMap) {\n if (!remoteMap.has(path)) {\n missingRemote.push(...localBucket.keys);\n }\n }\n\n return { missingLocal, missingRemote, differingPaths };\n }\n\n private rebuildInline(payload: MerkleRebuildPayload): MerkleRebuildResult {\n const { records, depth = 3 } = payload;\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const record of records) {\n const itemHash = this.hashString(\n `${record.key}:${record.timestamp.millis}:${record.timestamp.counter}:${record.timestamp.nodeId}`\n );\n hashEntries.push({ key: record.key, hash: itemHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private rebuildORMapInline(payload: ORMapMerkleRebuildPayload): MerkleRebuildResult {\n const { records, depth = 3 } = payload;\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const record of records) {\n const sortedTags = [...record.tags].sort((a, b) => a.tag.localeCompare(b.tag));\n let combinedStr = record.key;\n for (const tag of sortedTags) {\n combinedStr += `:${tag.tag}:${tag.timestamp.millis}:${tag.timestamp.counter}:${tag.timestamp.nodeId}`;\n }\n const entryHash = this.hashString(combinedStr);\n hashEntries.push({ key: record.key, hash: entryHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n // ============ Hash utilities ============\n\n private hashString(str: string): number {\n return coreHashString(str);\n }\n\n private buildTree(\n entries: Array<{ key: string; hash: number }>,\n depth: number\n ): { root: { hash: number }; buckets: Map<string, { hash: number; keys: string[] }> } {\n interface Node {\n hash: number;\n children?: { [key: string]: Node };\n entries?: Map<string, number>;\n }\n\n const root: Node = { hash: 0, children: {} };\n const buckets = new Map<string, { hash: number; keys: string[] }>();\n\n const updateNode = (\n node: Node,\n key: string,\n itemHash: number,\n pathHash: string,\n level: number\n ): number => {\n if (level >= depth) {\n if (!node.entries) node.entries = new Map();\n node.entries.set(key, itemHash);\n\n let h = 0;\n for (const val of node.entries.values()) {\n h = (h + val) | 0;\n }\n node.hash = h >>> 0;\n return node.hash;\n }\n\n const bucketChar = pathHash[level];\n if (!node.children) node.children = {};\n if (!node.children[bucketChar]) {\n node.children[bucketChar] = { hash: 0 };\n }\n\n updateNode(node.children[bucketChar], key, itemHash, pathHash, level + 1);\n\n let h = 0;\n for (const child of Object.values(node.children)) {\n h = (h + child.hash) | 0;\n }\n node.hash = h >>> 0;\n return node.hash;\n };\n\n for (const { key, hash } of entries) {\n const pathHash = this.hashString(key).toString(16).padStart(8, '0');\n updateNode(root, key, hash, pathHash, 0);\n }\n\n // Collect buckets\n const collectBuckets = (node: Node, path: string): void => {\n if (path.length >= depth) {\n if (node.entries && node.entries.size > 0) {\n buckets.set(path, {\n hash: node.hash,\n keys: Array.from(node.entries.keys()),\n });\n }\n return;\n }\n\n if (node.children) {\n for (const [char, child] of Object.entries(node.children)) {\n collectBuckets(child, path + char);\n }\n }\n };\n\n collectBuckets(root, '');\n\n return { root, buckets };\n }\n}\n","/**\n * CRDTMergeWorker - High-level API for CRDT merge operations in worker threads\n * Phase 1.04: CRDTMergeWorker Implementation\n *\n * Provides a clean interface for CPU-intensive CRDT merge operations.\n * Delegates actual work to worker threads via WorkerPool for large batches.\n */\n\nimport { join } from 'path';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n LWWMergePayload,\n LWWMergeResult,\n ORMapMergePayload,\n ORMapMergeResult,\n LWWMergeRecord,\n LWWExistingRecord,\n ORMapMergeItem,\n ORMapMergeTombstone,\n} from './crdt-types';\n\n// Threshold: use worker only if batch exceeds this count\nconst WORKER_THRESHOLD = 10;\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `crdt-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * Compare two timestamps (same logic as HLC.compare)\n */\nfunction compareTimestamps(\n a: { millis: number; counter: number; nodeId: string },\n b: { millis: number; counter: number; nodeId: string }\n): number {\n if (a.millis !== b.millis) {\n return a.millis - b.millis;\n }\n if (a.counter !== b.counter) {\n return a.counter - b.counter;\n }\n return a.nodeId.localeCompare(b.nodeId);\n}\n\n/**\n * CRDTMergeWorker provides methods for CRDT merge operations.\n * Automatically decides whether to use worker threads based on batch size.\n */\nexport class CRDTMergeWorker {\n private readonly pool: WorkerPool;\n\n /** Threshold for using worker (operations below this go to main thread) */\n static readonly BATCH_THRESHOLD = WORKER_THRESHOLD;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n }\n\n /**\n * Decide if batch should go to worker\n */\n shouldUseWorker(batchSize: number): boolean {\n return batchSize >= WORKER_THRESHOLD;\n }\n\n /**\n * Merge LWW records\n * @param payload - Records to merge and existing state\n * @returns Which records should be applied\n */\n async mergeLWW(payload: LWWMergePayload): Promise<LWWMergeResult> {\n if (!this.shouldUseWorker(payload.records.length)) {\n return this.mergeLWWInline(payload);\n }\n\n const task: WorkerTask<LWWMergePayload, LWWMergeResult> = {\n id: generateTaskId(),\n type: 'lww-merge' as WorkerTaskType,\n payload,\n priority: 'high', // Merge operations should be prioritized\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Merge ORMap items and tombstones\n * @param payload - Items, tombstones, and existing state\n * @returns Which items/tombstones should be applied\n */\n async mergeORMap(payload: ORMapMergePayload): Promise<ORMapMergeResult> {\n const totalOps = payload.items.length + payload.tombstones.length;\n\n if (!this.shouldUseWorker(totalOps)) {\n return this.mergeORMapInline(payload);\n }\n\n const task: WorkerTask<ORMapMergePayload, ORMapMergeResult> = {\n id: generateTaskId(),\n type: 'ormap-merge' as WorkerTaskType,\n payload,\n priority: 'high',\n };\n\n return this.pool.submit(task);\n }\n\n // ============ Inline implementations for small batches ============\n\n private mergeLWWInline(payload: LWWMergePayload): LWWMergeResult {\n const { records, existingState } = payload;\n\n // Build existing state map\n const existingMap = new Map<string, {\n value: unknown;\n timestamp: { millis: number; counter: number; nodeId: string };\n ttlMs?: number;\n }>();\n\n for (const existing of existingState) {\n existingMap.set(existing.key, {\n value: existing.value,\n timestamp: existing.timestamp,\n ttlMs: existing.ttlMs,\n });\n }\n\n const toApply: LWWMergeResult['toApply'] = [];\n const conflicts: string[] = [];\n let skipped = 0;\n\n for (const record of records) {\n const existing = existingMap.get(record.key);\n\n if (!existing) {\n toApply.push({\n key: record.key,\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n existingMap.set(record.key, {\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n continue;\n }\n\n const cmp = compareTimestamps(record.timestamp, existing.timestamp);\n\n // Detect conflict: same millis but different counter/nodeId (concurrent writes)\n const isConflict = record.timestamp.millis === existing.timestamp.millis &&\n (record.timestamp.counter !== existing.timestamp.counter ||\n record.timestamp.nodeId !== existing.timestamp.nodeId);\n\n if (cmp > 0) {\n // New record wins\n toApply.push({\n key: record.key,\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n existingMap.set(record.key, {\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n if (isConflict) {\n conflicts.push(record.key);\n }\n } else if (cmp === 0) {\n // Identical timestamps - skip but note conflict\n conflicts.push(record.key);\n skipped++;\n } else {\n // Old record - skip\n if (isConflict) {\n conflicts.push(record.key);\n }\n skipped++;\n }\n }\n\n return { toApply, skipped, conflicts };\n }\n\n private mergeORMapInline(payload: ORMapMergePayload): ORMapMergeResult {\n const { items, tombstones, existingTags, existingTombstones } = payload;\n\n const tagSet = new Set(existingTags);\n const tombstoneSet = new Set(existingTombstones);\n\n const itemsToApply: ORMapMergeResult['itemsToApply'] = [];\n const tombstonesToApply: string[] = [];\n const tagsToRemove: string[] = [];\n let itemsSkipped = 0;\n let tombstonesSkipped = 0;\n\n // Process tombstones first\n for (const tombstone of tombstones) {\n if (tombstoneSet.has(tombstone.tag)) {\n tombstonesSkipped++;\n continue;\n }\n\n tombstonesToApply.push(tombstone.tag);\n tombstoneSet.add(tombstone.tag);\n\n if (tagSet.has(tombstone.tag)) {\n tagsToRemove.push(tombstone.tag);\n tagSet.delete(tombstone.tag);\n }\n }\n\n // Process items\n for (const item of items) {\n if (tombstoneSet.has(item.tag)) {\n itemsSkipped++;\n continue;\n }\n\n if (tagSet.has(item.tag)) {\n itemsSkipped++;\n continue;\n }\n\n itemsToApply.push({\n key: item.key,\n value: item.value,\n timestamp: item.timestamp,\n tag: item.tag,\n ttlMs: item.ttlMs,\n });\n tagSet.add(item.tag);\n }\n\n return {\n itemsToApply,\n tombstonesToApply,\n tagsToRemove,\n itemsSkipped,\n tombstonesSkipped,\n };\n }\n}\n","/**\n * SerializationWorker - High-level API for serialization operations in worker threads\n * Phase 1.07: SerializationWorker Implementation\n *\n * Provides a clean interface for CPU-intensive serialization/deserialization.\n * Delegates actual work to worker threads via WorkerPool for large payloads.\n *\n * Uses base64 encoding to transfer binary data through postMessage.\n * This adds ~33% overhead but is necessary since Uint8Array cannot be\n * directly transferred through structured clone algorithm for our use case.\n */\n\nimport { join } from 'path';\nimport { serialize as coreSerialize, deserialize as coreDeserialize } from '@topgunbuild/core';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n SerializeBatchPayload,\n SerializeBatchResult,\n DeserializeBatchPayload,\n DeserializeBatchResult,\n} from './serialization-types';\n\n// Threshold: use worker only if batch exceeds this count OR total size is large\nconst WORKER_BATCH_THRESHOLD = 10;\nconst WORKER_SIZE_THRESHOLD = 50 * 1024; // 50 KB estimated payload size\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `ser-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * Convert Uint8Array to base64 string\n */\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Convert base64 string back to Uint8Array\n */\nfunction base64ToUint8Array(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Estimate serialized size of an object (rough heuristic)\n * MessagePack typically produces smaller output than JSON.stringify\n */\nfunction estimateSize(obj: unknown): number {\n if (obj === null || obj === undefined) return 1;\n if (typeof obj === 'boolean') return 1;\n if (typeof obj === 'number') return 9; // worst case: double\n if (typeof obj === 'string') return (obj as string).length + 5;\n if (Array.isArray(obj)) {\n let size = 5;\n for (const item of obj) {\n size += estimateSize(item);\n }\n return size;\n }\n if (typeof obj === 'object') {\n let size = 5;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n size += key.length + 5 + estimateSize(value);\n }\n return size;\n }\n return 10;\n}\n\n/**\n * SerializationWorker provides methods for serialization operations.\n * Automatically decides whether to use worker threads based on payload size.\n */\nexport class SerializationWorker {\n private readonly pool: WorkerPool;\n private readonly workerScript: string;\n\n /** Threshold for using worker (items below this go to main thread) */\n static readonly BATCH_THRESHOLD = WORKER_BATCH_THRESHOLD;\n /** Size threshold for using worker (bytes) */\n static readonly SIZE_THRESHOLD = WORKER_SIZE_THRESHOLD;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n this.workerScript = this.resolveWorkerScript();\n }\n\n private resolveWorkerScript(): string {\n const jsPath = join(__dirname, 'worker-scripts', 'serialization.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'serialization.worker.ts');\n\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n return tsPath;\n }\n }\n\n /**\n * Decide if batch should go to worker based on count or size\n */\n shouldUseWorker(items: unknown[]): boolean {\n if (items.length >= WORKER_BATCH_THRESHOLD) {\n return true;\n }\n\n // Estimate total size\n let totalSize = 0;\n for (const item of items) {\n totalSize += estimateSize(item);\n if (totalSize >= WORKER_SIZE_THRESHOLD) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Serialize multiple objects to MessagePack binary format.\n * Uses worker thread for large batches.\n *\n * @param items - Objects to serialize\n * @returns Array of Uint8Array containing serialized data\n */\n async serializeBatch(items: unknown[]): Promise<Uint8Array[]> {\n if (items.length === 0) {\n return [];\n }\n\n if (!this.shouldUseWorker(items)) {\n return this.serializeBatchInline(items);\n }\n\n const payload: SerializeBatchPayload = { items };\n const task: WorkerTask<SerializeBatchPayload, SerializeBatchResult> = {\n id: generateTaskId(),\n type: 'serialize' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n const result = await this.pool.submit(task);\n\n // Convert base64 strings back to Uint8Array\n return result.serialized.map(base64ToUint8Array);\n }\n\n /**\n * Deserialize multiple MessagePack binary payloads to objects.\n * Uses worker thread for large batches.\n *\n * @param items - Binary data to deserialize\n * @returns Array of deserialized objects\n */\n async deserializeBatch<T = unknown>(items: Uint8Array[]): Promise<T[]> {\n if (items.length === 0) {\n return [];\n }\n\n if (items.length < WORKER_BATCH_THRESHOLD) {\n return this.deserializeBatchInline(items);\n }\n\n // Convert to base64 for worker transfer\n const base64Items = items.map(uint8ArrayToBase64);\n\n const payload: DeserializeBatchPayload = { items: base64Items };\n const task: WorkerTask<DeserializeBatchPayload, DeserializeBatchResult> = {\n id: generateTaskId(),\n type: 'deserialize' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n const result = await this.pool.submit(task);\n return result.deserialized as T[];\n }\n\n /**\n * Serialize a single object (always inline, too small for worker)\n */\n serialize(data: unknown): Uint8Array {\n return coreSerialize(data);\n }\n\n /**\n * Deserialize a single payload (always inline, too small for worker)\n */\n deserialize<T = unknown>(data: Uint8Array | ArrayBuffer): T {\n return coreDeserialize(data) as T;\n }\n\n // ============ Inline implementations for small batches ============\n\n private serializeBatchInline(items: unknown[]): Uint8Array[] {\n const results: Uint8Array[] = [];\n for (const item of items) {\n results.push(coreSerialize(item));\n }\n return results;\n }\n\n private deserializeBatchInline<T>(items: Uint8Array[]): T[] {\n const results: T[] = [];\n for (const item of items) {\n results.push(coreDeserialize(item) as T);\n }\n return results;\n }\n}\n","/**\n * SharedMemoryManager - Zero-copy data transfer between main thread and workers\n *\n * Uses SharedArrayBuffer with Atomics for synchronization.\n * Provides slot-based allocation for concurrent operations.\n *\n * Phase 3.04: SharedArrayBuffer Integration\n */\n\nexport interface SharedMemoryConfig {\n /**\n * Size of shared buffer in bytes.\n * Default: 16MB\n */\n bufferSize?: number;\n\n /**\n * Number of slots for concurrent operations.\n * Each slot can hold one transfer.\n * Default: 256\n */\n slotCount?: number;\n\n /**\n * Reserved bytes per slot for metadata (length, status, etc).\n * Default: 16 (must be multiple of 8 for alignment)\n */\n metadataSize?: number;\n}\n\nexport interface SharedMemoryStats {\n /** Total buffer size in bytes */\n totalSize: number;\n /** Number of slots */\n slotCount: number;\n /** Size of each slot in bytes */\n slotSize: number;\n /** Currently allocated slots */\n allocatedSlots: number;\n /** Available slots */\n availableSlots: number;\n /** Peak concurrent allocations */\n peakUsage: number;\n /** Total allocations since creation */\n totalAllocations: number;\n /** Total releases since creation */\n totalReleases: number;\n}\n\nexport interface SharedSlot {\n /** Slot index */\n index: number;\n /** View into shared buffer for this slot's data area */\n dataView: Uint8Array;\n /** Maximum data size (excluding metadata) */\n maxDataSize: number;\n}\n\n/**\n * Slot status values (stored in first 4 bytes of slot metadata)\n */\nexport enum SlotStatus {\n FREE = 0, // Slot is available\n ALLOCATED = 1, // Slot is allocated, no data yet\n DATA_READY = 2, // Data written, ready for reading\n PROCESSING = 3, // Worker is processing\n RESULT_READY = 4, // Worker has written result\n ERROR = 255, // Error occurred\n}\n\n/**\n * Slot metadata layout (16 bytes):\n * - Bytes 0-3: Status (Int32 for Atomics)\n * - Bytes 4-7: Data length (Uint32)\n * - Bytes 8-15: Reserved for future use\n */\nconst DEFAULT_METADATA_SIZE = 16;\n\n/**\n * Manages shared memory for zero-copy data transfer between threads.\n *\n * Usage:\n * 1. Main thread allocates a slot\n * 2. Main thread writes data to slot\n * 3. Main thread sends slot index to worker via postMessage\n * 4. Worker reads data from shared memory (zero-copy)\n * 5. Worker writes result to slot\n * 6. Main thread reads result (zero-copy)\n * 7. Main thread releases slot\n */\nexport class SharedMemoryManager {\n private readonly buffer: SharedArrayBuffer;\n private readonly statusArray: Int32Array; // For Atomics operations\n private readonly slotSize: number;\n private readonly slotCount: number;\n private readonly metadataSize: number;\n private readonly freeSlots: Set<number>;\n\n // Stats\n private allocatedCount = 0;\n private peakUsage = 0;\n private totalAllocations = 0;\n private totalReleases = 0;\n\n constructor(config?: SharedMemoryConfig) {\n const bufferSize = config?.bufferSize ?? 16 * 1024 * 1024; // 16MB\n this.slotCount = config?.slotCount ?? 256;\n this.metadataSize = config?.metadataSize ?? DEFAULT_METADATA_SIZE;\n\n // Ensure metadata size is aligned to 8 bytes\n if (this.metadataSize % 8 !== 0) {\n throw new Error('metadataSize must be a multiple of 8');\n }\n\n // Calculate slot size\n this.slotSize = Math.floor(bufferSize / this.slotCount);\n\n // Ensure slot size is aligned to 8 bytes\n this.slotSize = Math.floor(this.slotSize / 8) * 8;\n\n if (this.slotSize <= this.metadataSize) {\n throw new Error('Buffer too small for given slot count');\n }\n\n // Allocate shared buffer\n const actualSize = this.slotSize * this.slotCount;\n this.buffer = new SharedArrayBuffer(actualSize);\n\n // Create Int32Array view for Atomics operations on status fields\n this.statusArray = new Int32Array(this.buffer);\n\n // Initialize free slots\n this.freeSlots = new Set();\n for (let i = 0; i < this.slotCount; i++) {\n this.freeSlots.add(i);\n // Initialize status to FREE\n Atomics.store(this.statusArray, this.getStatusOffset(i), SlotStatus.FREE);\n }\n }\n\n /**\n * Get offset in Int32Array for slot status.\n * Status is at the beginning of each slot's metadata.\n */\n private getStatusOffset(slotIndex: number): number {\n return (slotIndex * this.slotSize) / 4; // Int32 = 4 bytes\n }\n\n /**\n * Get byte offset for slot length field.\n */\n private getLengthOffset(slotIndex: number): number {\n return slotIndex * this.slotSize + 4; // After status (4 bytes)\n }\n\n /**\n * Get byte offset for slot data (after metadata).\n */\n private getDataOffset(slotIndex: number): number {\n return slotIndex * this.slotSize + this.metadataSize;\n }\n\n /**\n * Allocate a slot for data transfer.\n * Returns null if no slots available.\n */\n allocate(): SharedSlot | null {\n // Find free slot\n const iterator = this.freeSlots.values();\n const result = iterator.next();\n\n if (result.done) {\n return null; // No free slots\n }\n\n const index = result.value;\n this.freeSlots.delete(index);\n\n // Update stats\n this.allocatedCount++;\n this.totalAllocations++;\n this.peakUsage = Math.max(this.peakUsage, this.allocatedCount);\n\n // Set status to ALLOCATED using Atomics\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(index),\n SlotStatus.ALLOCATED\n );\n\n // Create data view\n const dataOffset = this.getDataOffset(index);\n const maxDataSize = this.slotSize - this.metadataSize;\n\n return {\n index,\n dataView: new Uint8Array(this.buffer, dataOffset, maxDataSize),\n maxDataSize,\n };\n }\n\n /**\n * Release a slot back to the pool.\n */\n release(slot: SharedSlot): void {\n if (this.freeSlots.has(slot.index)) {\n return; // Already free\n }\n\n // Set status to FREE\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(slot.index),\n SlotStatus.FREE\n );\n\n // Return to pool\n this.freeSlots.add(slot.index);\n this.allocatedCount--;\n this.totalReleases++;\n }\n\n /**\n * Write data to a slot and signal it's ready.\n * Returns false if data is too large.\n */\n writeData(slot: SharedSlot, data: Uint8Array): boolean {\n if (data.length > slot.maxDataSize) {\n return false; // Data too large\n }\n\n // Write length\n const lengthView = new DataView(\n this.buffer,\n this.getLengthOffset(slot.index),\n 4\n );\n lengthView.setUint32(0, data.length, true); // little-endian\n\n // Write data\n slot.dataView.set(data);\n\n // Signal data is ready (memory barrier via Atomics)\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(slot.index),\n SlotStatus.DATA_READY\n );\n // Wake up any waiting workers\n Atomics.notify(this.statusArray, this.getStatusOffset(slot.index));\n\n return true;\n }\n\n /**\n * Read data length from a slot.\n */\n getDataLength(slotIndex: number): number {\n const lengthView = new DataView(\n this.buffer,\n this.getLengthOffset(slotIndex),\n 4\n );\n return lengthView.getUint32(0, true);\n }\n\n /**\n * Get data view for a slot (for reading).\n */\n getDataView(slotIndex: number): Uint8Array {\n const length = this.getDataLength(slotIndex);\n const dataOffset = this.getDataOffset(slotIndex);\n return new Uint8Array(this.buffer, dataOffset, length);\n }\n\n /**\n * Get slot status.\n */\n getStatus(slotIndex: number): SlotStatus {\n return Atomics.load(this.statusArray, this.getStatusOffset(slotIndex));\n }\n\n /**\n * Wait for a specific status with timeout.\n * Returns the actual status (may differ if timeout occurred).\n */\n waitForStatus(\n slotIndex: number,\n expectedStatus: SlotStatus,\n timeoutMs: number = 5000\n ): SlotStatus {\n const statusOffset = this.getStatusOffset(slotIndex);\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const status = Atomics.load(this.statusArray, statusOffset);\n\n if (status === expectedStatus || status === SlotStatus.ERROR) {\n return status;\n }\n\n // Wait with timeout\n const remaining = deadline - Date.now();\n if (remaining > 0) {\n Atomics.wait(\n this.statusArray,\n statusOffset,\n status,\n Math.min(remaining, 100)\n );\n }\n }\n\n return Atomics.load(this.statusArray, statusOffset);\n }\n\n /**\n * Wait for result and read it.\n * Returns null on timeout or error.\n */\n waitForResult(slot: SharedSlot, timeoutMs: number = 5000): Uint8Array | null {\n const status = this.waitForStatus(\n slot.index,\n SlotStatus.RESULT_READY,\n timeoutMs\n );\n\n if (status === SlotStatus.RESULT_READY) {\n // Read length\n const length = this.getDataLength(slot.index);\n\n // Copy result (we need to copy because slot will be reused)\n const result = new Uint8Array(length);\n result.set(slot.dataView.subarray(0, length));\n\n return result;\n }\n\n return null; // Timeout or error\n }\n\n /**\n * Get the SharedArrayBuffer for passing to workers.\n */\n getBuffer(): SharedArrayBuffer {\n return this.buffer;\n }\n\n /**\n * Get configuration needed by workers.\n */\n getWorkerConfig(): SharedWorkerConfig {\n return {\n sharedBuffer: this.buffer,\n slotSize: this.slotSize,\n slotCount: this.slotCount,\n metadataSize: this.metadataSize,\n };\n }\n\n /**\n * Get statistics.\n */\n getStats(): SharedMemoryStats {\n return {\n totalSize: this.buffer.byteLength,\n slotCount: this.slotCount,\n slotSize: this.slotSize,\n allocatedSlots: this.allocatedCount,\n availableSlots: this.freeSlots.size,\n peakUsage: this.peakUsage,\n totalAllocations: this.totalAllocations,\n totalReleases: this.totalReleases,\n };\n }\n\n /**\n * Check if SharedArrayBuffer is available in current environment.\n */\n static isAvailable(): boolean {\n try {\n new SharedArrayBuffer(1);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Shutdown and release resources.\n * Resets all slots to FREE status.\n */\n shutdown(): void {\n // Reset all slots to FREE\n for (let i = 0; i < this.slotCount; i++) {\n Atomics.store(this.statusArray, this.getStatusOffset(i), SlotStatus.FREE);\n }\n this.freeSlots.clear();\n for (let i = 0; i < this.slotCount; i++) {\n this.freeSlots.add(i);\n }\n this.allocatedCount = 0;\n }\n}\n\n/**\n * Configuration passed to workers for shared memory access.\n */\nexport interface SharedWorkerConfig {\n sharedBuffer: SharedArrayBuffer;\n slotSize: number;\n slotCount: number;\n metadataSize: number;\n}\n","/**\n * TaskletScheduler — Cooperative multitasking for long-running operations.\n *\n * Inspired by Hazelcast's Tasklet pattern, this scheduler allows long operations\n * to yield control back to the event loop periodically, preventing event loop\n * blocking and maintaining responsiveness.\n *\n * Key concepts:\n * - Tasklet: A unit of work that can be paused and resumed\n * - Time budget: Maximum time a tasklet can run before yielding\n * - Cooperative scheduling: Tasklets voluntarily yield when time budget is exhausted\n */\n\n/**\n * Progress state returned by a tasklet after each execution step.\n */\nexport type ProgressState =\n | 'DONE' // Tasklet completed all work\n | 'MADE_PROGRESS' // Tasklet made progress but has more work\n | 'NO_PROGRESS'; // Tasklet couldn't make progress (e.g., waiting for I/O)\n\n/**\n * Interface for a tasklet that can be scheduled.\n */\nexport interface Tasklet<T = void> {\n /** Unique name for logging/metrics */\n readonly name: string;\n\n /**\n * Execute one chunk of work.\n * Should check time budget and return appropriate state.\n */\n call(): ProgressState;\n\n /**\n * Get the result after tasklet is DONE.\n * Only valid when call() returns 'DONE'.\n */\n getResult(): T;\n\n /**\n * Called when tasklet is cancelled before completion.\n */\n onCancel?(): void;\n}\n\n/**\n * Configuration for TaskletScheduler.\n */\nexport interface TaskletSchedulerConfig {\n /** Default time budget per tasklet execution in ms (default: 5) */\n defaultTimeBudgetMs?: number;\n\n /** Maximum concurrent tasklets (default: 10) */\n maxConcurrent?: number;\n\n /** Interval between scheduler ticks in ms (default: 1) */\n tickIntervalMs?: number;\n\n /** Enable metrics collection (default: true) */\n metricsEnabled?: boolean;\n}\n\n/**\n * Metrics for monitoring scheduler performance.\n */\nexport interface TaskletSchedulerStats {\n /** Total tasklets scheduled */\n totalScheduled: number;\n\n /** Tasklets currently running */\n activeTasklets: number;\n\n /** Tasklets completed successfully */\n completedTasklets: number;\n\n /** Tasklets cancelled */\n cancelledTasklets: number;\n\n /** Total iterations across all tasklets */\n totalIterations: number;\n\n /** Average iterations per tasklet */\n avgIterationsPerTasklet: number;\n\n /** Tasklets that completed in a single iteration */\n singleIterationCompletions: number;\n\n /** Time spent in tasklet execution (ms) */\n totalExecutionTimeMs: number;\n}\n\n/**\n * Internal state for a running tasklet.\n */\ninterface TaskletState<T> {\n tasklet: Tasklet<T>;\n resolve: (result: T) => void;\n reject: (error: Error) => void;\n iterations: number;\n startTime: number;\n lastProgressTime: number;\n}\n\nconst DEFAULT_CONFIG: Required<TaskletSchedulerConfig> = {\n defaultTimeBudgetMs: 5,\n maxConcurrent: 10,\n tickIntervalMs: 1,\n metricsEnabled: true,\n};\n\n/**\n * TaskletScheduler manages cooperative multitasking for long-running operations.\n *\n * Usage:\n * ```typescript\n * const scheduler = new TaskletScheduler();\n *\n * // Schedule a tasklet\n * const result = await scheduler.schedule(new QueryExecutionTasklet(records, query));\n *\n * // Cancel all running tasklets\n * scheduler.cancelAll();\n *\n * // Shutdown scheduler\n * scheduler.shutdown();\n * ```\n */\nexport class TaskletScheduler {\n private readonly config: Required<TaskletSchedulerConfig>;\n private readonly activeTasklets: Map<string, TaskletState<any>> = new Map();\n private tickTimer: NodeJS.Immediate | null = null;\n private isRunning = false;\n private isShuttingDown = false;\n\n // Metrics\n private totalScheduled = 0;\n private completedTasklets = 0;\n private cancelledTasklets = 0;\n private totalIterations = 0;\n private singleIterationCompletions = 0;\n private totalExecutionTimeMs = 0;\n\n constructor(config?: TaskletSchedulerConfig) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Schedule a tasklet for execution.\n * Returns a promise that resolves when the tasklet completes.\n */\n schedule<T>(tasklet: Tasklet<T>): Promise<T> {\n if (this.isShuttingDown) {\n return Promise.reject(new Error('Scheduler is shutting down'));\n }\n\n return new Promise<T>((resolve, reject) => {\n const taskletId = `${tasklet.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n // Check concurrent limit\n if (this.activeTasklets.size >= this.config.maxConcurrent) {\n reject(new Error(`Max concurrent tasklets (${this.config.maxConcurrent}) reached`));\n return;\n }\n\n const state: TaskletState<T> = {\n tasklet,\n resolve,\n reject,\n iterations: 0,\n startTime: Date.now(),\n lastProgressTime: Date.now(),\n };\n\n this.activeTasklets.set(taskletId, state);\n this.totalScheduled++;\n\n // Start scheduler if not running\n if (!this.isRunning) {\n this.startScheduler();\n }\n });\n }\n\n /**\n * Run a tasklet synchronously (blocking).\n * Useful for small operations or when cooperative scheduling isn't needed.\n */\n runSync<T>(tasklet: Tasklet<T>): T {\n let state: ProgressState;\n let iterations = 0;\n const startTime = Date.now();\n\n do {\n state = tasklet.call();\n iterations++;\n } while (state === 'MADE_PROGRESS');\n\n if (state === 'NO_PROGRESS') {\n throw new Error(`Tasklet ${tasklet.name} made no progress`);\n }\n\n if (this.config.metricsEnabled) {\n this.totalIterations += iterations;\n this.totalExecutionTimeMs += Date.now() - startTime;\n if (iterations === 1) {\n this.singleIterationCompletions++;\n }\n }\n\n return tasklet.getResult();\n }\n\n /**\n * Cancel a specific tasklet by name pattern.\n * Returns the number of tasklets cancelled.\n */\n cancel(namePattern: string | RegExp): number {\n let cancelled = 0;\n const pattern = typeof namePattern === 'string'\n ? new RegExp(`^${namePattern}`)\n : namePattern;\n\n for (const [id, state] of this.activeTasklets) {\n if (pattern.test(state.tasklet.name)) {\n this.cancelTasklet(id, state);\n cancelled++;\n }\n }\n\n return cancelled;\n }\n\n /**\n * Cancel all running tasklets.\n */\n cancelAll(): number {\n let cancelled = 0;\n\n for (const [id, state] of this.activeTasklets) {\n this.cancelTasklet(id, state);\n cancelled++;\n }\n\n return cancelled;\n }\n\n /**\n * Get scheduler statistics.\n */\n getStats(): TaskletSchedulerStats {\n return {\n totalScheduled: this.totalScheduled,\n activeTasklets: this.activeTasklets.size,\n completedTasklets: this.completedTasklets,\n cancelledTasklets: this.cancelledTasklets,\n totalIterations: this.totalIterations,\n avgIterationsPerTasklet: this.completedTasklets > 0\n ? this.totalIterations / this.completedTasklets\n : 0,\n singleIterationCompletions: this.singleIterationCompletions,\n totalExecutionTimeMs: this.totalExecutionTimeMs,\n };\n }\n\n /**\n * Reset statistics.\n */\n resetStats(): void {\n this.totalScheduled = 0;\n this.completedTasklets = 0;\n this.cancelledTasklets = 0;\n this.totalIterations = 0;\n this.singleIterationCompletions = 0;\n this.totalExecutionTimeMs = 0;\n }\n\n /**\n * Shutdown the scheduler.\n * Cancels all running tasklets and stops the tick timer.\n */\n shutdown(): void {\n this.isShuttingDown = true;\n this.cancelAll();\n this.stopScheduler();\n }\n\n /**\n * Check if scheduler is running.\n */\n get running(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get number of active tasklets.\n */\n get activeCount(): number {\n return this.activeTasklets.size;\n }\n\n private startScheduler(): void {\n if (this.isRunning) return;\n this.isRunning = true;\n this.scheduleTick();\n }\n\n private stopScheduler(): void {\n this.isRunning = false;\n if (this.tickTimer) {\n clearImmediate(this.tickTimer);\n this.tickTimer = null;\n }\n }\n\n private scheduleTick(): void {\n if (!this.isRunning) return;\n\n // Use setImmediate for minimal delay while allowing I/O\n this.tickTimer = setImmediate(() => {\n this.tick();\n });\n }\n\n private tick(): void {\n if (!this.isRunning || this.activeTasklets.size === 0) {\n this.stopScheduler();\n return;\n }\n\n const tickStart = Date.now();\n const taskletIds = Array.from(this.activeTasklets.keys());\n\n for (const id of taskletIds) {\n const state = this.activeTasklets.get(id);\n if (!state) continue;\n\n try {\n const iterationStart = Date.now();\n const result = state.tasklet.call();\n const iterationTime = Date.now() - iterationStart;\n\n state.iterations++;\n state.lastProgressTime = Date.now();\n\n if (this.config.metricsEnabled) {\n this.totalIterations++;\n this.totalExecutionTimeMs += iterationTime;\n }\n\n if (result === 'DONE') {\n this.completeTasklet(id, state);\n } else if (result === 'NO_PROGRESS') {\n // Tasklet couldn't make progress, will retry next tick\n }\n // MADE_PROGRESS: continue in next tick\n } catch (error) {\n this.failTasklet(id, state, error as Error);\n }\n\n // Check if we've exceeded our time budget for this tick\n if (Date.now() - tickStart > this.config.defaultTimeBudgetMs * 2) {\n break; // Process remaining tasklets in next tick\n }\n }\n\n // Schedule next tick if there are still active tasklets\n if (this.activeTasklets.size > 0) {\n this.scheduleTick();\n } else {\n this.stopScheduler();\n }\n }\n\n private completeTasklet<T>(id: string, state: TaskletState<T>): void {\n this.activeTasklets.delete(id);\n this.completedTasklets++;\n\n if (state.iterations === 1) {\n this.singleIterationCompletions++;\n }\n\n try {\n const result = state.tasklet.getResult();\n state.resolve(result);\n } catch (error) {\n state.reject(error as Error);\n }\n }\n\n private failTasklet<T>(id: string, state: TaskletState<T>, error: Error): void {\n this.activeTasklets.delete(id);\n state.reject(error);\n }\n\n private cancelTasklet<T>(id: string, state: TaskletState<T>): void {\n this.activeTasklets.delete(id);\n this.cancelledTasklets++;\n\n if (state.tasklet.onCancel) {\n try {\n state.tasklet.onCancel();\n } catch {\n // Ignore cancel errors\n }\n }\n\n state.reject(new Error(`Tasklet ${state.tasklet.name} was cancelled`));\n }\n}\n","/**\n * IteratorTasklet — Base class for tasklets that iterate over collections.\n *\n * Provides cooperative iteration with time-budgeted chunks.\n * Subclasses implement processItem() to handle each item.\n */\n\nimport { Tasklet, ProgressState } from '../TaskletScheduler';\n\n/**\n * Configuration for iterator tasklets.\n */\nexport interface IteratorTaskletConfig {\n /** Time budget per iteration in ms (default: 5) */\n timeBudgetMs?: number;\n\n /** Maximum items to process per iteration (default: 1000) */\n maxItemsPerIteration?: number;\n}\n\nconst DEFAULT_CONFIG: Required<IteratorTaskletConfig> = {\n timeBudgetMs: 5,\n maxItemsPerIteration: 1000,\n};\n\n/**\n * Abstract base class for iterator-based tasklets.\n *\n * Usage:\n * ```typescript\n * class MyTasklet extends IteratorTasklet<[string, Record], Record[]> {\n * constructor(map: Map<string, Record>) {\n * super('my-tasklet', map.entries());\n * }\n *\n * protected processItem([key, record]: [string, Record]): void {\n * if (matchesCriteria(record)) {\n * this.results.push(record);\n * }\n * }\n *\n * getResult(): Record[] {\n * return this.results;\n * }\n * }\n * ```\n */\nexport abstract class IteratorTasklet<TItem, TResult> implements Tasklet<TResult> {\n abstract readonly name: string;\n\n protected readonly config: Required<IteratorTaskletConfig>;\n protected readonly iterator: Iterator<TItem>;\n protected itemsProcessed = 0;\n protected isDone = false;\n\n constructor(\n iterator: Iterator<TItem>,\n config?: IteratorTaskletConfig\n ) {\n this.iterator = iterator;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Process a single item from the iterator.\n * Override this in subclasses.\n */\n protected abstract processItem(item: TItem): void;\n\n /**\n * Get the final result after iteration completes.\n */\n abstract getResult(): TResult;\n\n /**\n * Execute one chunk of iteration.\n */\n call(): ProgressState {\n if (this.isDone) {\n return 'DONE';\n }\n\n const deadline = Date.now() + this.config.timeBudgetMs;\n let processedThisIteration = 0;\n\n while (\n Date.now() < deadline &&\n processedThisIteration < this.config.maxItemsPerIteration\n ) {\n const { value, done } = this.iterator.next();\n\n if (done) {\n this.isDone = true;\n return 'DONE';\n }\n\n this.processItem(value);\n this.itemsProcessed++;\n processedThisIteration++;\n }\n\n return 'MADE_PROGRESS';\n }\n\n /**\n * Called when tasklet is cancelled.\n */\n onCancel(): void {\n // Subclasses can override for cleanup\n }\n\n /**\n * Get number of items processed so far.\n */\n get processed(): number {\n return this.itemsProcessed;\n }\n}\n\n/**\n * Simple iterator tasklet that collects items matching a predicate.\n */\nexport class FilterTasklet<T> extends IteratorTasklet<T, T[]> {\n readonly name: string;\n protected readonly results: T[] = [];\n private readonly predicate: (item: T) => boolean;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n predicate: (item: T) => boolean,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.predicate = predicate;\n }\n\n protected processItem(item: T): void {\n if (this.predicate(item)) {\n this.results.push(item);\n }\n }\n\n getResult(): T[] {\n return this.results;\n }\n}\n\n/**\n * Iterator tasklet that transforms items.\n */\nexport class MapTasklet<TIn, TOut> extends IteratorTasklet<TIn, TOut[]> {\n readonly name: string;\n protected readonly results: TOut[] = [];\n private readonly mapper: (item: TIn) => TOut;\n\n constructor(\n name: string,\n iterator: Iterator<TIn>,\n mapper: (item: TIn) => TOut,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.mapper = mapper;\n }\n\n protected processItem(item: TIn): void {\n this.results.push(this.mapper(item));\n }\n\n getResult(): TOut[] {\n return this.results;\n }\n}\n\n/**\n * Iterator tasklet that applies a function to each item (side effects).\n */\nexport class ForEachTasklet<T> extends IteratorTasklet<T, number> {\n readonly name: string;\n private readonly action: (item: T) => void;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n action: (item: T) => void,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.action = action;\n }\n\n protected processItem(item: T): void {\n this.action(item);\n }\n\n getResult(): number {\n return this.itemsProcessed;\n }\n}\n\n/**\n * Iterator tasklet that reduces items to a single value.\n */\nexport class ReduceTasklet<T, TAccum> extends IteratorTasklet<T, TAccum> {\n readonly name: string;\n private accumulator: TAccum;\n private readonly reducer: (acc: TAccum, item: T) => TAccum;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n initialValue: TAccum,\n reducer: (acc: TAccum, item: T) => TAccum,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n\n protected processItem(item: T): void {\n this.accumulator = this.reducer(this.accumulator, item);\n }\n\n getResult(): TAccum {\n return this.accumulator;\n }\n}\n","import { EventEmitter } from 'events';\nimport {\n WriteConcern,\n WriteResult,\n PendingWrite,\n DEFAULT_WRITE_CONCERN_TIMEOUT,\n isWriteConcernAchieved,\n getHighestWriteConcernLevel,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface WriteAckManagerConfig {\n /** Default timeout for write acknowledgments (ms) */\n defaultTimeout?: number;\n}\n\nexport interface WriteAckStats {\n /** Number of pending writes */\n pending: number;\n /** Pending writes by Write Concern level */\n byLevel: Record<WriteConcern, number>;\n}\n\n/**\n * Manages pending write acknowledgments for different Write Concern levels.\n *\n * Flow:\n * 1. Operation received → registerPending()\n * 2. Level achieved → notifyLevel()\n * 3. Target level reached → resolve promise, emit 'resolved'\n * 4. Timeout → resolve with partial success, emit 'timeout'\n *\n * @example\n * ```typescript\n * const ackManager = new WriteAckManager();\n *\n * // Register pending write\n * const promise = ackManager.registerPending(opId, WriteConcern.PERSISTED, 5000);\n *\n * // As processing progresses, notify levels\n * ackManager.notifyLevel(opId, WriteConcern.MEMORY); // Validated\n * ackManager.notifyLevel(opId, WriteConcern.APPLIED); // CRDT merged\n * ackManager.notifyLevel(opId, WriteConcern.REPLICATED); // Broadcast sent\n * ackManager.notifyLevel(opId, WriteConcern.PERSISTED); // Storage written\n *\n * // Promise resolves when target level is reached\n * const result = await promise;\n * ```\n */\nexport class WriteAckManager extends EventEmitter {\n private pending: Map<string, PendingWrite> = new Map();\n private readonly defaultTimeout: number;\n\n constructor(config?: WriteAckManagerConfig) {\n super();\n this.defaultTimeout = config?.defaultTimeout ?? DEFAULT_WRITE_CONCERN_TIMEOUT;\n }\n\n /**\n * Register a pending write operation.\n * Returns a promise that resolves when target Write Concern is achieved.\n *\n * @param opId - Operation ID\n * @param writeConcern - Target Write Concern level\n * @param timeout - Optional timeout in ms (defaults to config or 5000ms)\n * @returns Promise that resolves with WriteResult\n */\n registerPending(\n opId: string,\n writeConcern: WriteConcern,\n timeout?: number\n ): Promise<WriteResult> {\n // FIRE_AND_FORGET resolves immediately\n if (writeConcern === WriteConcern.FIRE_AND_FORGET) {\n return Promise.resolve({\n success: true,\n opId,\n achievedLevel: WriteConcern.FIRE_AND_FORGET,\n latencyMs: 0,\n });\n }\n\n return new Promise((resolve, reject) => {\n const effectiveTimeout = timeout ?? this.defaultTimeout;\n const timestamp = Date.now();\n\n const pendingWrite: PendingWrite = {\n opId,\n writeConcern,\n timestamp,\n timeout: effectiveTimeout,\n resolve,\n reject,\n achievedLevels: new Set([WriteConcern.FIRE_AND_FORGET]),\n };\n\n // Set timeout\n pendingWrite.timeoutHandle = setTimeout(() => {\n this.handleTimeout(opId);\n }, effectiveTimeout);\n\n this.pending.set(opId, pendingWrite);\n\n logger.debug(\n { opId, writeConcern, timeout: effectiveTimeout },\n 'Registered pending write'\n );\n\n // MEMORY level is achieved immediately after registration\n // (operation is validated and queued for processing)\n if (writeConcern === WriteConcern.MEMORY) {\n this.notifyLevel(opId, WriteConcern.MEMORY);\n }\n });\n }\n\n /**\n * Notify that a Write Concern level has been achieved for an operation.\n *\n * @param opId - Operation ID\n * @param level - Write Concern level that was achieved\n */\n notifyLevel(opId: string, level: WriteConcern): void {\n const pending = this.pending.get(opId);\n if (!pending) {\n // Operation not tracked (might be FIRE_AND_FORGET or already resolved)\n return;\n }\n\n pending.achievedLevels.add(level);\n\n logger.debug(\n { opId, level, target: pending.writeConcern },\n 'Write Concern level achieved'\n );\n\n // Check if target level is achieved\n if (isWriteConcernAchieved(pending.achievedLevels, pending.writeConcern)) {\n this.resolvePending(opId, level);\n }\n }\n\n /**\n * Notify multiple operations that a Write Concern level has been achieved.\n * Useful for batch operations.\n *\n * @param opIds - Array of operation IDs\n * @param level - Write Concern level that was achieved\n */\n notifyLevelBatch(opIds: string[], level: WriteConcern): void {\n for (const opId of opIds) {\n this.notifyLevel(opId, level);\n }\n }\n\n /**\n * Check if an operation is still pending.\n *\n * @param opId - Operation ID\n * @returns true if operation is pending\n */\n isPending(opId: string): boolean {\n return this.pending.has(opId);\n }\n\n /**\n * Get the target Write Concern level for a pending operation.\n *\n * @param opId - Operation ID\n * @returns Target Write Concern level or undefined if not pending\n */\n getTargetLevel(opId: string): WriteConcern | undefined {\n return this.pending.get(opId)?.writeConcern;\n }\n\n /**\n * Get the highest achieved level for a pending operation.\n *\n * @param opId - Operation ID\n * @returns Highest achieved level or undefined if not pending\n */\n getAchievedLevel(opId: string): WriteConcern | undefined {\n const pending = this.pending.get(opId);\n if (!pending) return undefined;\n return getHighestWriteConcernLevel(pending.achievedLevels);\n }\n\n /**\n * Resolve a pending write with success.\n */\n private resolvePending(opId: string, achievedLevel: WriteConcern): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n // Clear timeout\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const latencyMs = Date.now() - pending.timestamp;\n\n const result: WriteResult = {\n success: true,\n opId,\n achievedLevel,\n latencyMs,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.debug(\n { opId, achievedLevel, latencyMs },\n 'Write resolved successfully'\n );\n\n this.emit('resolved', result);\n }\n\n /**\n * Handle timeout for a pending write.\n */\n private handleTimeout(opId: string): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n const latencyMs = Date.now() - pending.timestamp;\n\n // Resolve with partial success (achieved lower level than requested)\n const result: WriteResult = {\n success: false,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error: `Timeout: achieved ${highestAchieved}, requested ${pending.writeConcern}`,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.warn(\n { opId, requested: pending.writeConcern, achieved: highestAchieved, latencyMs },\n 'Write timed out'\n );\n\n this.emit('timeout', {\n opId,\n requested: pending.writeConcern,\n achieved: highestAchieved,\n latencyMs,\n });\n }\n\n /**\n * Fail a pending write with an error.\n *\n * @param opId - Operation ID\n * @param error - Error message\n */\n failPending(opId: string, error: string): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n // Clear timeout\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const latencyMs = Date.now() - pending.timestamp;\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n\n const result: WriteResult = {\n success: false,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.error({ opId, error, latencyMs }, 'Write failed');\n\n this.emit('failed', result);\n }\n\n /**\n * Get pending writes statistics.\n */\n getStats(): WriteAckStats {\n const byLevel: Record<WriteConcern, number> = {\n [WriteConcern.FIRE_AND_FORGET]: 0,\n [WriteConcern.MEMORY]: 0,\n [WriteConcern.APPLIED]: 0,\n [WriteConcern.REPLICATED]: 0,\n [WriteConcern.PERSISTED]: 0,\n };\n\n for (const pending of this.pending.values()) {\n byLevel[pending.writeConcern]++;\n }\n\n return { pending: this.pending.size, byLevel };\n }\n\n /**\n * Get all pending operation IDs.\n */\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n /**\n * Clear all pending writes (for shutdown).\n * Rejects all pending promises with an error.\n */\n clear(): void {\n const count = this.pending.size;\n\n for (const pending of this.pending.values()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.reject(new Error('WriteAckManager cleared'));\n }\n\n this.pending.clear();\n\n if (count > 0) {\n logger.info({ count }, 'WriteAckManager cleared');\n }\n }\n\n /**\n * Graceful shutdown - resolves all pending writes with their current achieved level.\n */\n shutdown(): void {\n const count = this.pending.size;\n\n for (const [opId, pending] of this.pending.entries()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n const latencyMs = Date.now() - pending.timestamp;\n\n const result: WriteResult = {\n success: highestAchieved === pending.writeConcern,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error: highestAchieved !== pending.writeConcern\n ? `Shutdown: achieved ${highestAchieved}, requested ${pending.writeConcern}`\n : undefined,\n };\n\n pending.resolve(result);\n }\n\n this.pending.clear();\n\n if (count > 0) {\n logger.info({ count }, 'WriteAckManager shutdown');\n }\n }\n}\n","/**\n * ReplicationPipeline - Manages async replication with configurable consistency levels\n *\n * Phase 4 Task 04: Async Replication Pipeline\n *\n * Features:\n * - Three consistency levels: STRONG, QUORUM, EVENTUAL\n * - Async replication queue for high throughput\n * - Backpressure handling with queue limits\n * - Retry logic for failed replications\n * - Integration with LagTracker for monitoring\n * - Pluggable operation applier for storage integration\n */\n\nimport { EventEmitter } from 'events';\nimport {\n ConsistencyLevel,\n ReplicationConfig,\n ReplicationTask,\n ReplicationResult,\n ReplicationHealth,\n ReplicationLag,\n DEFAULT_REPLICATION_CONFIG,\n} from '@topgunbuild/core';\nimport { ClusterManager, ClusterMessage } from './ClusterManager';\nimport { PartitionService } from './PartitionService';\nimport { LagTracker } from './LagTracker';\nimport { logger } from '../utils/logger';\n\n/**\n * Callback to apply replicated operation to local storage\n * @param operation - The operation to apply\n * @param opId - Unique operation ID\n * @param sourceNode - Node that originated the operation\n * @returns Promise<boolean> - true if applied successfully\n */\nexport type OperationApplier = (\n operation: unknown,\n opId: string,\n sourceNode: string\n) => Promise<boolean>;\n\nexport interface PendingAck {\n opId: string;\n consistency: ConsistencyLevel;\n targetNodes: string[];\n ackedNodes: Set<string>;\n resolve: () => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n startTime: number;\n}\n\nexport interface ReplicationPipelineEvents {\n 'replicationComplete': (opId: string, ackedBy: string[]) => void;\n 'replicationFailed': (opId: string, error: Error) => void;\n 'queueOverflow': (nodeId: string) => void;\n 'error': (error: Error) => void;\n}\n\nexport class ReplicationTimeoutError extends Error {\n constructor(\n public readonly opId: string,\n public readonly targetNodes: string[],\n public readonly ackedNodes: string[]\n ) {\n super(\n `Replication timeout for operation ${opId}. Expected: ${targetNodes.join(', ')}, Acked: ${ackedNodes.join(', ')}`\n );\n this.name = 'ReplicationTimeoutError';\n }\n}\n\nexport class ReplicationPipeline extends EventEmitter {\n private readonly config: ReplicationConfig;\n private readonly clusterManager: ClusterManager;\n private readonly partitionService: PartitionService;\n private readonly lagTracker: LagTracker;\n private readonly nodeId: string;\n\n // Replication queues per node (for EVENTUAL mode)\n private replicationQueue: Map<string, ReplicationTask[]> = new Map();\n // Pending acknowledgments (for STRONG/QUORUM mode)\n private pendingAcks: Map<string, PendingAck> = new Map();\n // Queue processor timer\n private queueProcessorTimer: ReturnType<typeof setInterval> | null = null;\n // Operation applier callback (injected by ServerCoordinator)\n private operationApplier: OperationApplier | null = null;\n\n constructor(\n clusterManager: ClusterManager,\n partitionService: PartitionService,\n config: Partial<ReplicationConfig> = {}\n ) {\n super();\n this.clusterManager = clusterManager;\n this.partitionService = partitionService;\n this.nodeId = clusterManager.config.nodeId;\n this.config = {\n ...DEFAULT_REPLICATION_CONFIG,\n ...config,\n };\n this.lagTracker = new LagTracker();\n\n this.setupMessageHandlers();\n this.startQueueProcessor();\n }\n\n // ============================================\n // Configuration\n // ============================================\n\n /**\n * Set the operation applier callback\n * This is called when replicated operations are received from other nodes\n */\n public setOperationApplier(applier: OperationApplier): void {\n this.operationApplier = applier;\n }\n\n // ============================================\n // Replication API\n // ============================================\n\n /**\n * Replicate operation to backup nodes\n */\n public async replicate(\n operation: unknown,\n opId: string,\n key: string,\n options: { consistency?: ConsistencyLevel; timeout?: number } = {}\n ): Promise<ReplicationResult> {\n const consistency = options.consistency ?? this.config.defaultConsistency;\n const partitionId = this.partitionService.getPartitionId(key);\n const backups = this.partitionService.getBackups(partitionId);\n\n if (backups.length === 0) {\n // No replicas, always succeeds\n return { success: true, ackedBy: [this.nodeId] };\n }\n\n switch (consistency) {\n case ConsistencyLevel.STRONG:\n return this.replicateStrong(operation, opId, backups, options.timeout);\n\n case ConsistencyLevel.QUORUM:\n return this.replicateQuorum(operation, opId, backups, options.timeout);\n\n case ConsistencyLevel.EVENTUAL:\n return this.replicateEventual(operation, opId, backups);\n }\n }\n\n /**\n * STRONG: Wait for all replicas to acknowledge\n */\n private async replicateStrong(\n operation: unknown,\n opId: string,\n backups: string[],\n timeout?: number\n ): Promise<ReplicationResult> {\n const targetNodes = backups;\n\n return new Promise((resolve, reject) => {\n const pending: PendingAck = {\n opId,\n consistency: ConsistencyLevel.STRONG,\n targetNodes,\n ackedNodes: new Set(),\n resolve: () =>\n resolve({\n success: true,\n ackedBy: [this.nodeId, ...targetNodes],\n }),\n reject: (error) => reject(error),\n timeout: setTimeout(() => {\n this.pendingAcks.delete(opId);\n const ackedList = Array.from(pending.ackedNodes);\n reject(new ReplicationTimeoutError(opId, targetNodes, ackedList));\n }, timeout ?? this.config.ackTimeoutMs),\n startTime: Date.now(),\n };\n\n this.pendingAcks.set(opId, pending);\n\n // Track pending ops\n for (const nodeId of targetNodes) {\n this.lagTracker.incrementPending(nodeId);\n }\n\n // Send to all backups\n for (const nodeId of targetNodes) {\n this.sendReplication(nodeId, operation, opId, ConsistencyLevel.STRONG);\n }\n });\n }\n\n /**\n * QUORUM: Wait for majority of replicas\n */\n private async replicateQuorum(\n operation: unknown,\n opId: string,\n backups: string[],\n timeout?: number\n ): Promise<ReplicationResult> {\n const targetNodes = backups;\n const quorumSize = Math.floor(targetNodes.length / 2) + 1;\n\n return new Promise((resolve, reject) => {\n // Create pending ack with resolve that captures snapshot of ackedNodes\n const ackedNodes = new Set<string>();\n const pending: PendingAck = {\n opId,\n consistency: ConsistencyLevel.QUORUM,\n targetNodes,\n ackedNodes,\n resolve: () => {\n // Snapshot ackedNodes at resolve time to avoid race condition\n const ackedSnapshot = Array.from(ackedNodes);\n const ackedBy = [this.nodeId, ...ackedSnapshot];\n resolve({ success: true, ackedBy });\n },\n reject: (error) => reject(error),\n timeout: setTimeout(() => {\n this.pendingAcks.delete(opId);\n const ackedList = Array.from(ackedNodes);\n reject(new ReplicationTimeoutError(opId, targetNodes, ackedList));\n }, timeout ?? this.config.ackTimeoutMs),\n startTime: Date.now(),\n };\n\n this.pendingAcks.set(opId, pending);\n\n // Track pending ops\n for (const nodeId of targetNodes) {\n this.lagTracker.incrementPending(nodeId);\n }\n\n // Send to all backups\n for (const nodeId of targetNodes) {\n this.sendReplication(nodeId, operation, opId, ConsistencyLevel.QUORUM);\n }\n });\n }\n\n /**\n * EVENTUAL: Fire-and-forget with queue\n */\n private async replicateEventual(\n operation: unknown,\n opId: string,\n backups: string[]\n ): Promise<ReplicationResult> {\n // Add to replication queue for each backup\n for (const nodeId of backups) {\n this.enqueue(nodeId, {\n opId,\n operation,\n consistency: ConsistencyLevel.EVENTUAL,\n timestamp: Date.now(),\n retryCount: 0,\n });\n }\n\n // Return immediately\n return { success: true, ackedBy: [this.nodeId] };\n }\n\n // ============================================\n // Queue Management\n // ============================================\n\n /**\n * Add task to replication queue\n */\n private enqueue(nodeId: string, task: ReplicationTask): void {\n let queue = this.replicationQueue.get(nodeId);\n if (!queue) {\n queue = [];\n this.replicationQueue.set(nodeId, queue);\n }\n\n if (queue.length >= this.config.queueSizeLimit) {\n // Queue overflow - emit event and drop oldest\n this.emit('queueOverflow', nodeId);\n logger.warn({ nodeId, queueSize: queue.length }, 'Replication queue overflow, dropping oldest');\n queue.shift();\n }\n\n queue.push(task);\n this.lagTracker.incrementPending(nodeId);\n }\n\n /**\n * Start queue processor\n */\n private startQueueProcessor(): void {\n if (this.queueProcessorTimer) return;\n\n this.queueProcessorTimer = setInterval(() => {\n for (const nodeId of this.replicationQueue.keys()) {\n this.processQueue(nodeId).catch((err) => {\n logger.error({ nodeId, error: err }, 'Error processing replication queue');\n this.emit('error', err);\n });\n }\n }, this.config.batchIntervalMs);\n }\n\n /**\n * Stop queue processor\n */\n private stopQueueProcessor(): void {\n if (this.queueProcessorTimer) {\n clearInterval(this.queueProcessorTimer);\n this.queueProcessorTimer = null;\n }\n }\n\n /**\n * Process replication queue for a node\n */\n private async processQueue(nodeId: string): Promise<void> {\n const queue = this.replicationQueue.get(nodeId);\n if (!queue || queue.length === 0) return;\n\n // Batch up to config.batchSize operations\n const batch = queue.splice(0, this.config.batchSize);\n\n try {\n // Send batch to node\n this.clusterManager.send(nodeId, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_BATCH',\n payload: {\n operations: batch.map((t) => t.operation),\n opIds: batch.map((t) => t.opId),\n },\n },\n });\n\n // Update lag tracker with oldest timestamp in batch\n const oldestTimestamp = Math.min(...batch.map((t) => t.timestamp));\n this.lagTracker.update(nodeId, Date.now() - oldestTimestamp);\n\n logger.debug({ nodeId, batchSize: batch.length }, 'Sent replication batch');\n } catch (error) {\n // Requeue failed batch with retry increment\n for (const task of batch) {\n task.retryCount++;\n if (task.retryCount <= this.config.maxRetries) {\n queue.unshift(task); // Requeue at front\n } else {\n logger.warn({ nodeId, opId: task.opId, retries: task.retryCount }, 'Replication task exceeded max retries');\n this.emit('replicationFailed', task.opId, new Error('Max retries exceeded'));\n }\n }\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n /**\n * Send replication message to a node\n */\n private sendReplication(\n nodeId: string,\n operation: unknown,\n opId: string,\n consistency: ConsistencyLevel\n ): void {\n this.clusterManager.send(nodeId, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION',\n payload: {\n opId,\n operation,\n consistency,\n },\n },\n });\n }\n\n /**\n * Setup cluster message handlers\n */\n private setupMessageHandlers(): void {\n this.clusterManager.on('message', (msg: ClusterMessage) => {\n if (msg.payload?._replication) {\n const replication = msg.payload._replication;\n\n switch (replication.type) {\n case 'REPLICATION':\n this.handleReplication(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_BATCH':\n this.handleReplicationBatch(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_ACK':\n this.handleReplicationAck(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_BATCH_ACK':\n this.handleReplicationBatchAck(msg.senderId, replication.payload);\n break;\n }\n }\n });\n }\n\n /**\n * Handle incoming replication request (on backup node)\n */\n private async handleReplication(\n sourceNode: string,\n payload: { opId: string; operation: unknown; consistency: ConsistencyLevel }\n ): Promise<void> {\n const { opId, operation, consistency } = payload;\n\n logger.debug({ sourceNode, opId, consistency }, 'Received replication');\n\n // Apply operation to local storage\n let success = true;\n if (this.operationApplier) {\n try {\n success = await this.operationApplier(operation, opId, sourceNode);\n } catch (error) {\n logger.error({ sourceNode, opId, error }, 'Failed to apply replicated operation');\n success = false;\n }\n } else {\n logger.warn({ sourceNode, opId }, 'No operation applier set, operation not applied');\n }\n\n // For STRONG/QUORUM, send acknowledgment\n if (consistency === ConsistencyLevel.STRONG || consistency === ConsistencyLevel.QUORUM) {\n this.clusterManager.send(sourceNode, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_ACK',\n payload: {\n opId,\n success,\n timestamp: Date.now(),\n },\n },\n });\n }\n }\n\n /**\n * Handle incoming batch replication (on backup node)\n */\n private async handleReplicationBatch(\n sourceNode: string,\n payload: { operations: unknown[]; opIds: string[] }\n ): Promise<void> {\n const { operations, opIds } = payload;\n\n logger.debug({ sourceNode, count: operations.length }, 'Received replication batch');\n\n // Apply operations to local storage\n let allSuccess = true;\n if (this.operationApplier) {\n for (let i = 0; i < operations.length; i++) {\n try {\n const success = await this.operationApplier(operations[i], opIds[i], sourceNode);\n if (!success) {\n allSuccess = false;\n }\n } catch (error) {\n logger.error({ sourceNode, opId: opIds[i], error }, 'Failed to apply replicated operation in batch');\n allSuccess = false;\n }\n }\n } else {\n logger.warn({ sourceNode, count: operations.length }, 'No operation applier set, batch not applied');\n }\n\n // Send batch acknowledgment\n this.clusterManager.send(sourceNode, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_BATCH_ACK',\n payload: {\n opIds,\n success: allSuccess,\n timestamp: Date.now(),\n },\n },\n });\n }\n\n /**\n * Handle replication acknowledgment (on owner node)\n */\n private handleReplicationAck(\n sourceNode: string,\n payload: { opId: string; success: boolean; timestamp: number }\n ): void {\n const { opId, success } = payload;\n\n // Update lag tracker\n this.lagTracker.recordAck(sourceNode);\n\n const pending = this.pendingAcks.get(opId);\n if (!pending) return; // No pending ack or already resolved\n\n if (!success) {\n logger.warn({ sourceNode, opId }, 'Replication rejected by backup');\n return;\n }\n\n pending.ackedNodes.add(sourceNode);\n\n // Update lag with round-trip time\n const lag = Date.now() - pending.startTime;\n this.lagTracker.update(sourceNode, lag);\n\n // Check if we've met the consistency requirement\n const ackedCount = pending.ackedNodes.size;\n const targetCount = pending.targetNodes.length;\n\n switch (pending.consistency) {\n case ConsistencyLevel.STRONG:\n if (ackedCount === targetCount) {\n clearTimeout(pending.timeout);\n this.pendingAcks.delete(opId);\n pending.resolve();\n this.emit('replicationComplete', opId, [this.nodeId, ...pending.ackedNodes]);\n }\n break;\n\n case ConsistencyLevel.QUORUM:\n const quorumSize = Math.floor(targetCount / 2) + 1;\n if (ackedCount >= quorumSize) {\n clearTimeout(pending.timeout);\n this.pendingAcks.delete(opId);\n pending.resolve();\n this.emit('replicationComplete', opId, [this.nodeId, ...pending.ackedNodes]);\n }\n break;\n }\n }\n\n /**\n * Handle batch acknowledgment (on owner node)\n */\n private handleReplicationBatchAck(\n sourceNode: string,\n payload: { opIds: string[]; success: boolean; timestamp: number }\n ): void {\n const { success } = payload;\n\n // Update lag tracker\n this.lagTracker.recordAck(sourceNode);\n\n if (!success) {\n logger.warn({ sourceNode, count: payload.opIds.length }, 'Batch replication rejected');\n }\n }\n\n // ============================================\n // Status and Metrics\n // ============================================\n\n /**\n * Get replication lag for a specific node\n */\n public getLag(nodeId: string): ReplicationLag {\n return this.lagTracker.getLag(nodeId);\n }\n\n /**\n * Get overall replication health\n */\n public getHealth(): ReplicationHealth {\n return this.lagTracker.getHealth();\n }\n\n /**\n * Get queue size for a specific node\n */\n public getQueueSize(nodeId: string): number {\n return this.replicationQueue.get(nodeId)?.length ?? 0;\n }\n\n /**\n * Get total pending operations across all nodes\n */\n public getTotalPending(): number {\n let total = 0;\n for (const queue of this.replicationQueue.values()) {\n total += queue.length;\n }\n return total + this.pendingAcks.size;\n }\n\n /**\n * Check if a node is considered synced (low lag)\n */\n public isSynced(nodeId: string, maxLagMs: number = 1000): boolean {\n const lag = this.lagTracker.getLag(nodeId);\n return lag.current < maxLagMs;\n }\n\n /**\n * Get LagTracker for advanced monitoring\n */\n public getLagTracker(): LagTracker {\n return this.lagTracker;\n }\n\n /**\n * Export metrics in Prometheus format\n */\n public toPrometheusMetrics(): string {\n const lines: string[] = [];\n\n // Queue sizes\n lines.push('# HELP topgun_replication_queue_size Pending operations in replication queue');\n lines.push('# TYPE topgun_replication_queue_size gauge');\n for (const [nodeId, queue] of this.replicationQueue) {\n lines.push(`topgun_replication_queue_size{node=\"${nodeId}\"} ${queue.length}`);\n }\n\n // Pending acks\n lines.push('');\n lines.push('# HELP topgun_replication_pending_acks Pending synchronous acknowledgments');\n lines.push('# TYPE topgun_replication_pending_acks gauge');\n lines.push(`topgun_replication_pending_acks ${this.pendingAcks.size}`);\n\n // Lag tracker metrics\n lines.push('');\n lines.push(this.lagTracker.toPrometheusMetrics());\n\n return lines.join('\\n');\n }\n\n /**\n * Cleanup resources\n */\n public close(): void {\n this.stopQueueProcessor();\n\n // Reject all pending acks\n for (const [opId, pending] of this.pendingAcks) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('ReplicationPipeline closed'));\n }\n this.pendingAcks.clear();\n\n // Clear queues\n this.replicationQueue.clear();\n\n // Clear lag tracker\n this.lagTracker.clear();\n }\n}\n","/**\n * LagTracker - Monitors replication lag across cluster nodes\n *\n * Phase 4 Task 04: Async Replication Pipeline\n *\n * Features:\n * - Tracks replication lag per node\n * - Maintains historical lag data for percentile calculations\n * - Identifies unhealthy and laggy nodes\n * - Provides health metrics for monitoring\n */\n\nimport { ReplicationLag, ReplicationHealth } from '@topgunbuild/core';\n\nexport interface LagInfo {\n current: number;\n history: number[];\n lastUpdate: number;\n pendingOps: number;\n}\n\nexport interface LagTrackerConfig {\n /** Number of lag samples to keep in history (default: 100) */\n historySize: number;\n /** Threshold in ms for considering a node laggy (default: 5000) */\n laggyThresholdMs: number;\n /** Threshold in ms for considering a node unhealthy (default: 30000) */\n unhealthyThresholdMs: number;\n}\n\nexport const DEFAULT_LAG_TRACKER_CONFIG: LagTrackerConfig = {\n historySize: 100,\n laggyThresholdMs: 5000,\n unhealthyThresholdMs: 30000,\n};\n\nexport class LagTracker {\n private readonly config: LagTrackerConfig;\n private lagByNode: Map<string, LagInfo> = new Map();\n\n constructor(config: Partial<LagTrackerConfig> = {}) {\n this.config = {\n ...DEFAULT_LAG_TRACKER_CONFIG,\n ...config,\n };\n }\n\n /**\n * Update lag measurement for a node\n */\n public update(nodeId: string, lagMs: number): void {\n let info = this.lagByNode.get(nodeId);\n if (!info) {\n info = {\n current: 0,\n history: [],\n lastUpdate: Date.now(),\n pendingOps: 0,\n };\n this.lagByNode.set(nodeId, info);\n }\n\n info.current = lagMs;\n info.history.push(lagMs);\n\n // Trim history to configured size\n if (info.history.length > this.config.historySize) {\n info.history.shift();\n }\n\n info.lastUpdate = Date.now();\n }\n\n /**\n * Record acknowledgment from a node (lag effectively becomes 0)\n */\n public recordAck(nodeId: string): void {\n const info = this.lagByNode.get(nodeId);\n if (info) {\n info.current = 0;\n info.lastUpdate = Date.now();\n if (info.pendingOps > 0) {\n info.pendingOps--;\n }\n }\n }\n\n /**\n * Increment pending operations counter for a node\n */\n public incrementPending(nodeId: string): void {\n let info = this.lagByNode.get(nodeId);\n if (!info) {\n info = {\n current: 0,\n history: [],\n lastUpdate: Date.now(),\n pendingOps: 0,\n };\n this.lagByNode.set(nodeId, info);\n }\n info.pendingOps++;\n }\n\n /**\n * Get lag statistics for a specific node\n */\n public getLag(nodeId: string): ReplicationLag {\n const info = this.lagByNode.get(nodeId);\n if (!info || info.history.length === 0) {\n return { current: 0, avg: 0, max: 0, percentile99: 0 };\n }\n\n const sorted = [...info.history].sort((a, b) => a - b);\n const avg = sorted.reduce((a, b) => a + b, 0) / sorted.length;\n const max = sorted[sorted.length - 1] || 0;\n\n // Calculate 99th percentile\n const p99Index = Math.floor(sorted.length * 0.99);\n const percentile99 = sorted[p99Index] || max;\n\n return {\n current: info.current,\n avg: Math.round(avg * 100) / 100, // Round to 2 decimal places\n max,\n percentile99,\n };\n }\n\n /**\n * Get pending operations count for a node\n */\n public getPendingOps(nodeId: string): number {\n const info = this.lagByNode.get(nodeId);\n return info?.pendingOps ?? 0;\n }\n\n /**\n * Get overall replication health status\n */\n public getHealth(): ReplicationHealth {\n const unhealthyNodes: string[] = [];\n const laggyNodes: string[] = [];\n let totalLag = 0;\n let nodeCount = 0;\n\n const now = Date.now();\n\n for (const [nodeId, info] of this.lagByNode) {\n const timeSinceUpdate = now - info.lastUpdate;\n\n // Check if node is unhealthy (no updates for too long)\n if (timeSinceUpdate > this.config.unhealthyThresholdMs) {\n unhealthyNodes.push(nodeId);\n }\n // Check if node is laggy (current lag exceeds threshold)\n else if (info.current > this.config.laggyThresholdMs) {\n laggyNodes.push(nodeId);\n }\n\n totalLag += info.current;\n nodeCount++;\n }\n\n const avgLagMs = nodeCount > 0 ? totalLag / nodeCount : 0;\n\n return {\n healthy: unhealthyNodes.length === 0,\n unhealthyNodes,\n laggyNodes,\n avgLagMs: Math.round(avgLagMs * 100) / 100,\n };\n }\n\n /**\n * Get average lag across all tracked nodes\n */\n public getAverageLag(): number {\n let total = 0;\n let count = 0;\n\n for (const info of this.lagByNode.values()) {\n total += info.current;\n count++;\n }\n\n return count > 0 ? total / count : 0;\n }\n\n /**\n * Check if a specific node is considered healthy\n */\n public isNodeHealthy(nodeId: string): boolean {\n const info = this.lagByNode.get(nodeId);\n if (!info) return true; // Unknown nodes are considered healthy\n\n const timeSinceUpdate = Date.now() - info.lastUpdate;\n return timeSinceUpdate < this.config.unhealthyThresholdMs;\n }\n\n /**\n * Check if a specific node is considered laggy\n */\n public isNodeLaggy(nodeId: string): boolean {\n const info = this.lagByNode.get(nodeId);\n if (!info) return false;\n\n return info.current > this.config.laggyThresholdMs;\n }\n\n /**\n * Remove a node from tracking\n */\n public removeNode(nodeId: string): void {\n this.lagByNode.delete(nodeId);\n }\n\n /**\n * Get all tracked node IDs\n */\n public getTrackedNodes(): string[] {\n return Array.from(this.lagByNode.keys());\n }\n\n /**\n * Get raw lag info for a node (for advanced monitoring)\n */\n public getRawLagInfo(nodeId: string): LagInfo | undefined {\n return this.lagByNode.get(nodeId);\n }\n\n /**\n * Clear all tracking data\n */\n public clear(): void {\n this.lagByNode.clear();\n }\n\n /**\n * Export metrics in Prometheus format\n */\n public toPrometheusMetrics(): string {\n const lines: string[] = [\n '# HELP topgun_replication_lag_ms Current replication lag in milliseconds',\n '# TYPE topgun_replication_lag_ms gauge',\n ];\n\n for (const [nodeId, info] of this.lagByNode) {\n lines.push(`topgun_replication_lag_ms{node=\"${nodeId}\"} ${info.current}`);\n }\n\n lines.push('');\n lines.push('# HELP topgun_replication_pending_ops Pending replication operations');\n lines.push('# TYPE topgun_replication_pending_ops gauge');\n\n for (const [nodeId, info] of this.lagByNode) {\n lines.push(`topgun_replication_pending_ops{node=\"${nodeId}\"} ${info.pendingOps}`);\n }\n\n const health = this.getHealth();\n lines.push('');\n lines.push('# HELP topgun_replication_healthy Cluster replication health (1=healthy, 0=unhealthy)');\n lines.push('# TYPE topgun_replication_healthy gauge');\n lines.push(`topgun_replication_healthy ${health.healthy ? 1 : 0}`);\n\n lines.push('');\n lines.push('# HELP topgun_replication_avg_lag_ms Average replication lag across all nodes');\n lines.push('# TYPE topgun_replication_avg_lag_ms gauge');\n lines.push(`topgun_replication_avg_lag_ms ${health.avgLagMs}`);\n\n return lines.join('\\n');\n }\n}\n","import { Pool, PoolConfig } from 'pg';\nimport { LWWRecord } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue } from './IServerStorage';\n\nexport interface PostgresAdapterOptions {\n tableName?: string;\n}\n\nconst DEFAULT_TABLE_NAME = 'topgun_maps';\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction validateTableName(name: string): void {\n if (!TABLE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid table name \"${name}\". Table name must start with a letter or underscore and contain only alphanumeric characters and underscores.`\n );\n }\n}\n\nexport class PostgresAdapter implements IServerStorage {\n private pool: Pool;\n private tableName: string;\n\n constructor(configOrPool: PoolConfig | Pool, options?: PostgresAdapterOptions) {\n if (configOrPool instanceof Pool || (configOrPool as any).connect) {\n this.pool = configOrPool as Pool;\n } else {\n this.pool = new Pool(configOrPool as PoolConfig);\n }\n\n const tableName = options?.tableName ?? DEFAULT_TABLE_NAME;\n validateTableName(tableName);\n this.tableName = tableName;\n }\n\n async initialize(): Promise<void> {\n const client = await this.pool.connect();\n try {\n // Create a generic table for storing key-value pairs per map\n // schema: map_name (text), key (text), value (jsonb), timestamp_millis (bigint), timestamp_counter (int), node_id (text), is_deleted (boolean)\n await client.query(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n map_name TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSONB,\n ts_millis BIGINT NOT NULL,\n ts_counter INTEGER NOT NULL,\n ts_node_id TEXT NOT NULL,\n is_deleted BOOLEAN DEFAULT FALSE,\n PRIMARY KEY (map_name, key)\n );\n `);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n const res = await this.pool.query(\n `SELECT value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = $2`,\n [mapName, key]\n );\n\n if (res.rows.length === 0) return undefined;\n\n const row = res.rows[0];\n return this.mapRowToRecord(row);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const result = new Map<string, StorageValue<any>>();\n if (keys.length === 0) return result;\n\n const res = await this.pool.query(\n `SELECT key, value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = ANY($2)`,\n [mapName, keys]\n );\n\n for (const row of res.rows) {\n result.set(row.key, this.mapRowToRecord(row));\n }\n\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n const res = await this.pool.query(\n `SELECT key FROM ${this.tableName} WHERE map_name = $1`,\n [mapName]\n );\n return res.rows.map(row => row.key);\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n let value: any;\n let tsMillis: number;\n let tsCounter: number;\n let tsNodeId: string;\n let isDeleted: boolean;\n\n if (this.isORMapValue(record)) {\n // Store ORMap data\n // We use a special marker in ts_node_id to distinguish ORMap data from LWW data\n value = record;\n tsMillis = 0;\n tsCounter = 0;\n tsNodeId = '__ORMAP__';\n isDeleted = false;\n } else {\n // LWWRecord\n const lww = record as LWWRecord<any>;\n value = lww.value;\n tsMillis = lww.timestamp.millis;\n tsCounter = lww.timestamp.counter;\n tsNodeId = lww.timestamp.nodeId;\n isDeleted = lww.value === null;\n }\n\n await this.pool.query(\n `INSERT INTO ${this.tableName} (map_name, key, value, ts_millis, ts_counter, ts_node_id, is_deleted)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (map_name, key) DO UPDATE SET\n value = EXCLUDED.value,\n ts_millis = EXCLUDED.ts_millis,\n ts_counter = EXCLUDED.ts_counter,\n ts_node_id = EXCLUDED.ts_node_id,\n is_deleted = EXCLUDED.is_deleted`,\n [\n mapName,\n key,\n JSON.stringify(value),\n tsMillis,\n tsCounter,\n tsNodeId,\n isDeleted\n ]\n );\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n // Note: For high performance, this should use UNNEST or multi-row INSERT.\n // Keeping loop for simplicity in MVP alignment.\n for (const [key, record] of records) {\n await this.store(mapName, key, record); \n }\n await client.query('COMMIT');\n } catch (e) {\n await client.query('ROLLBACK');\n throw e;\n } finally {\n client.release();\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n await this.pool.query(`DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = $2`, [mapName, key]);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.pool.query(\n `DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = ANY($2)`, \n [mapName, keys]\n );\n }\n\n private mapRowToRecord(row: any): StorageValue<any> {\n if (row.ts_node_id === '__ORMAP__') {\n // It's an ORMap value (ORMapValue or ORMapTombstones)\n return row.value as StorageValue<any>;\n }\n\n // It's LWWRecord\n return {\n value: row.is_deleted ? null : row.value,\n timestamp: {\n millis: Number(row.ts_millis),\n counter: row.ts_counter,\n nodeId: row.ts_node_id\n }\n };\n }\n\n private isORMapValue(record: any): boolean {\n return (record && typeof record === 'object' && (record.type === 'OR' || record.type === 'OR_TOMBSTONES'));\n }\n}\n","import { IServerStorage, StorageValue } from './IServerStorage';\n\n/**\n * In-memory implementation of IServerStorage.\n * Useful for development, testing, and demos without requiring a database.\n *\n * Note: Data is lost when the server restarts.\n */\nexport class MemoryServerAdapter implements IServerStorage {\n // Map<mapName, Map<key, value>>\n private storage = new Map<string, Map<string, StorageValue<any>>>();\n\n async initialize(): Promise<void> {\n // No-op for in-memory storage\n console.log('[MemoryServerAdapter] Initialized in-memory storage');\n }\n\n async close(): Promise<void> {\n this.storage.clear();\n console.log('[MemoryServerAdapter] Storage cleared and closed');\n }\n\n private getMap(mapName: string): Map<string, StorageValue<any>> {\n let map = this.storage.get(mapName);\n if (!map) {\n map = new Map();\n this.storage.set(mapName, map);\n }\n return map;\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n return this.getMap(mapName).get(key);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const map = this.getMap(mapName);\n const result = new Map<string, StorageValue<any>>();\n for (const key of keys) {\n const value = map.get(key);\n if (value !== undefined) {\n result.set(key, value);\n }\n }\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n return Array.from(this.getMap(mapName).keys());\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n this.getMap(mapName).set(key, record);\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const map = this.getMap(mapName);\n for (const [key, value] of records) {\n map.set(key, value);\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n this.getMap(mapName).delete(key);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n const map = this.getMap(mapName);\n for (const key of keys) {\n map.delete(key);\n }\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\nexport class TimestampInterceptor implements IInterceptor {\n name = 'TimestampInterceptor';\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp> {\n // Only apply to PUT operations with LWW records\n if (op.opType === 'PUT' && op.record && op.record.value) {\n // Modifying the value to include server timestamp\n // This assumes value is an object where we can add properties\n if (typeof op.record.value === 'object' && op.record.value !== null && !Array.isArray(op.record.value)) {\n const newValue = {\n ...op.record.value,\n _serverTimestamp: Date.now()\n };\n logger.debug({ key: op.key, mapName: op.mapName, interceptor: this.name }, 'Added timestamp');\n return {\n ...op,\n record: {\n ...op.record,\n value: newValue\n }\n };\n }\n }\n return op;\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\ninterface RateLimitConfig {\n windowMs: number;\n maxOps: number;\n}\n\ninterface ClientLimit {\n count: number;\n resetTime: number;\n}\n\nexport class RateLimitInterceptor implements IInterceptor {\n name = 'RateLimitInterceptor';\n \n private limits = new Map<string, ClientLimit>();\n private config: RateLimitConfig;\n\n constructor(config: RateLimitConfig = { windowMs: 1000, maxOps: 50 }) {\n this.config = config;\n }\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp | null> {\n // Rate limit based on clientId\n const clientId = context.clientId;\n const now = Date.now();\n \n let limit = this.limits.get(clientId);\n \n if (!limit || now > limit.resetTime) {\n limit = {\n count: 0,\n resetTime: now + this.config.windowMs\n };\n this.limits.set(clientId, limit);\n }\n\n limit.count++;\n\n if (limit.count > this.config.maxOps) {\n logger.warn({ clientId, opId: op.id, count: limit.count }, 'Rate limit exceeded');\n throw new Error('Rate limit exceeded');\n }\n\n return op;\n }\n\n // Cleanup old entries periodically? \n // For now we rely on resetTime check, but map grows. \n // Simple cleanup on reset logic:\n // In a real system, we'd use Redis or a proper cache with TTL.\n // Here we can just prune occasionally or relying on connection disconnect?\n \n // Optimization: Cleanup on disconnect\n async onDisconnect(context: any) {\n this.limits.delete(context.clientId);\n }\n}\n\n","/**\n * Native Module Statistics\n *\n * Provides information about available native optimizations.\n *\n * Phase 3.05: Integration\n */\n\nimport { isUsingNativeHash } from '@topgunbuild/core';\nimport {\n SharedMemoryManager,\n type SharedMemoryStats,\n} from '../workers/SharedMemoryManager';\n\n/**\n * Native module availability status\n */\nexport interface NativeModuleStatus {\n /** Native xxHash64 is available and being used */\n nativeHash: boolean;\n /** SharedArrayBuffer is available */\n sharedArrayBuffer: boolean;\n}\n\n/**\n * Comprehensive native statistics\n */\nexport interface NativeStats {\n /** Module availability status */\n modules: NativeModuleStatus;\n /** Shared memory statistics (if enabled) */\n sharedMemory: SharedMemoryStats | null;\n /** Summary of what's being used */\n summary: string;\n}\n\n/**\n * Check which native modules are available.\n */\nexport function getNativeModuleStatus(): NativeModuleStatus {\n return {\n nativeHash: isUsingNativeHash(),\n sharedArrayBuffer: SharedMemoryManager.isAvailable(),\n };\n}\n\n/**\n * Get native statistics including shared memory.\n *\n * @param sharedMemoryManager - Optional SharedMemoryManager instance\n */\nexport function getNativeStats(\n sharedMemoryManager?: SharedMemoryManager\n): NativeStats {\n const modules = getNativeModuleStatus();\n\n const summaryParts: string[] = [];\n\n if (modules.nativeHash) {\n summaryParts.push('native xxHash64');\n } else {\n summaryParts.push('FNV-1a (JS fallback)');\n }\n\n if (modules.sharedArrayBuffer) {\n summaryParts.push('SharedArrayBuffer available');\n } else {\n summaryParts.push('SharedArrayBuffer unavailable');\n }\n\n return {\n modules,\n sharedMemory: sharedMemoryManager?.getStats() ?? null,\n summary: summaryParts.join(', '),\n };\n}\n\n/**\n * Log native module status to console.\n */\nexport function logNativeStatus(): void {\n const status = getNativeModuleStatus();\n\n console.log('[TopGun] Native Module Status:');\n console.log(` - Hash: ${status.nativeHash ? 'native xxHash64' : 'FNV-1a (JS)'}`);\n console.log(\n ` - SharedArrayBuffer: ${status.sharedArrayBuffer ? 'available' : 'unavailable'}`\n );\n}\n","/**\n * ClusterCoordinator - Unified cluster integration layer\n *\n * Phase 4 Task 06: System Integration\n *\n * Coordinates all cluster components:\n * - ClusterManager: P2P WebSocket mesh\n * - PartitionService: Consistent hashing & routing\n * - MigrationManager: Gradual rebalancing\n * - ReplicationPipeline: Async replication with consistency levels\n * - LagTracker: Replication health monitoring\n */\n\nimport { EventEmitter } from 'events';\nimport { ClusterManager, ClusterConfig } from './ClusterManager';\nimport { PartitionService, PartitionServiceConfig, PartitionDistribution } from './PartitionService';\nimport { MigrationManager } from './MigrationManager';\nimport { ReplicationPipeline } from './ReplicationPipeline';\nimport { LagTracker } from './LagTracker';\nimport {\n MigrationConfig,\n MigrationStatus,\n MigrationMetrics,\n ReplicationConfig,\n ReplicationHealth,\n ReplicationLag,\n ReplicationResult,\n ConsistencyLevel,\n PartitionMap,\n PartitionChange,\n DEFAULT_MIGRATION_CONFIG,\n DEFAULT_REPLICATION_CONFIG,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\n// ============================================\n// Unified Cluster Configuration\n// ============================================\n\nexport interface ClusterCoordinatorConfig {\n /** Cluster node configuration */\n cluster: ClusterConfig;\n\n /** Enable gradual partition rebalancing (default: true) */\n gradualRebalancing: boolean;\n\n /** Migration configuration for gradual rebalancing */\n migration: Partial<MigrationConfig>;\n\n /** Replication configuration */\n replication: Partial<ReplicationConfig>;\n\n /** Enable async replication pipeline (default: true) */\n replicationEnabled: boolean;\n\n /** Data collector callback for migrations */\n dataCollector?: (partitionId: number) => Promise<Uint8Array[]>;\n\n /** Data storer callback for incoming migrations */\n dataStorer?: (partitionId: number, data: Uint8Array[]) => Promise<void>;\n}\n\nexport const DEFAULT_CLUSTER_COORDINATOR_CONFIG: Omit<ClusterCoordinatorConfig, 'cluster'> = {\n gradualRebalancing: true,\n migration: DEFAULT_MIGRATION_CONFIG,\n replication: DEFAULT_REPLICATION_CONFIG,\n replicationEnabled: true,\n};\n\n// ============================================\n// Cluster Coordinator Events\n// ============================================\n\nexport interface ClusterCoordinatorEvents {\n 'started': () => void;\n 'stopped': () => void;\n 'member:joined': (nodeId: string) => void;\n 'member:left': (nodeId: string) => void;\n 'partition:rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;\n 'partition:moved': (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => void;\n 'migration:started': (partitionId: number, targetNode: string) => void;\n 'migration:completed': (partitionId: number) => void;\n 'migration:failed': (partitionId: number, error: Error) => void;\n 'replication:unhealthy': (nodeId: string) => void;\n 'replication:healthy': (nodeId: string) => void;\n 'error': (error: Error) => void;\n}\n\n// ============================================\n// Cluster Coordinator\n// ============================================\n\nexport class ClusterCoordinator extends EventEmitter {\n private readonly config: ClusterCoordinatorConfig;\n\n // Core components\n private clusterManager: ClusterManager;\n private partitionService: PartitionService;\n private replicationPipeline: ReplicationPipeline | null = null;\n private lagTracker: LagTracker;\n\n // State\n private started: boolean = false;\n private actualPort: number = 0;\n\n constructor(config: ClusterCoordinatorConfig) {\n super();\n this.config = {\n ...DEFAULT_CLUSTER_COORDINATOR_CONFIG,\n ...config,\n };\n\n // Initialize core components\n this.clusterManager = new ClusterManager(this.config.cluster);\n this.lagTracker = new LagTracker();\n\n // Initialize partition service with or without gradual rebalancing\n const partitionServiceConfig: Partial<PartitionServiceConfig> = {\n gradualRebalancing: this.config.gradualRebalancing,\n migration: this.config.migration,\n };\n this.partitionService = new PartitionService(this.clusterManager, partitionServiceConfig);\n\n // Initialize replication pipeline if enabled\n if (this.config.replicationEnabled) {\n this.replicationPipeline = new ReplicationPipeline(\n this.clusterManager,\n this.partitionService,\n this.config.replication\n );\n }\n\n this.setupEventHandlers();\n }\n\n // ============================================\n // Lifecycle Methods\n // ============================================\n\n /**\n * Start the cluster coordinator\n */\n public async start(): Promise<number> {\n if (this.started) {\n return this.actualPort;\n }\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'Starting ClusterCoordinator');\n\n // Start cluster manager\n this.actualPort = await this.clusterManager.start();\n\n // Configure migration data handlers if provided\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager && this.config.dataCollector) {\n migrationManager.setDataCollector(this.config.dataCollector);\n }\n if (migrationManager && this.config.dataStorer) {\n migrationManager.setDataStorer(this.config.dataStorer);\n }\n\n this.started = true;\n this.emit('started');\n\n logger.info({ nodeId: this.config.cluster.nodeId, port: this.actualPort }, 'ClusterCoordinator started');\n return this.actualPort;\n }\n\n /**\n * Stop the cluster coordinator\n */\n public async stop(): Promise<void> {\n if (!this.started) return;\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'Stopping ClusterCoordinator');\n\n // Cancel any active migrations\n await this.partitionService.cancelMigrations();\n\n // Close replication pipeline\n this.replicationPipeline?.close();\n\n // Stop cluster manager\n this.clusterManager.stop();\n\n this.started = false;\n this.emit('stopped');\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'ClusterCoordinator stopped');\n }\n\n // ============================================\n // Cluster Information\n // ============================================\n\n /**\n * Get local node ID\n */\n public getNodeId(): string {\n return this.config.cluster.nodeId;\n }\n\n /**\n * Get cluster port\n */\n public getPort(): number {\n return this.actualPort;\n }\n\n /**\n * Get all cluster members\n */\n public getMembers(): string[] {\n return this.clusterManager.getMembers();\n }\n\n /**\n * Check if this is the local node\n */\n public isLocal(nodeId: string): boolean {\n return this.clusterManager.isLocal(nodeId);\n }\n\n /**\n * Check if coordinator is started\n */\n public isStarted(): boolean {\n return this.started;\n }\n\n // ============================================\n // Partition Operations\n // ============================================\n\n /**\n * Get current partition map\n */\n public getPartitionMap(): PartitionMap {\n return this.partitionService.getPartitionMap();\n }\n\n /**\n * Get partition map version\n */\n public getPartitionMapVersion(): number {\n return this.partitionService.getMapVersion();\n }\n\n /**\n * Get partition ID for a key\n */\n public getPartitionId(key: string): number {\n return this.partitionService.getPartitionId(key);\n }\n\n /**\n * Get owner node for a key\n */\n public getOwner(key: string): string {\n return this.partitionService.getOwner(key);\n }\n\n /**\n * Check if this node owns the key\n */\n public isLocalOwner(key: string): boolean {\n return this.partitionService.isLocalOwner(key);\n }\n\n /**\n * Check if this node is a backup for the key\n */\n public isLocalBackup(key: string): boolean {\n return this.partitionService.isLocalBackup(key);\n }\n\n /**\n * Get backup nodes for a partition\n */\n public getBackups(partitionId: number): string[] {\n return this.partitionService.getBackups(partitionId);\n }\n\n /**\n * Check if partition is currently migrating\n */\n public isMigrating(partitionId: number): boolean {\n return this.partitionService.isMigrating(partitionId);\n }\n\n /**\n * Check if any rebalancing is in progress\n */\n public isRebalancing(): boolean {\n return this.partitionService.isRebalancing();\n }\n\n // ============================================\n // Migration Operations\n // ============================================\n\n /**\n * Get migration status\n */\n public getMigrationStatus(): MigrationStatus | null {\n return this.partitionService.getMigrationStatus();\n }\n\n /**\n * Get migration metrics\n */\n public getMigrationMetrics(): MigrationMetrics | null {\n return this.partitionService.getMigrationManager()?.getMetrics() ?? null;\n }\n\n /**\n * Cancel all active migrations\n */\n public async cancelMigrations(): Promise<void> {\n await this.partitionService.cancelMigrations();\n }\n\n /**\n * Set data collector for migrations\n */\n public setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void {\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.setDataCollector(collector);\n }\n }\n\n /**\n * Set data storer for incoming migrations\n */\n public setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void {\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.setDataStorer(storer);\n }\n }\n\n // ============================================\n // Replication Operations\n // ============================================\n\n /**\n * Replicate an operation to backup nodes\n */\n public async replicate(\n operation: unknown,\n opId: string,\n key: string,\n options: { consistency?: ConsistencyLevel; timeout?: number } = {}\n ): Promise<ReplicationResult> {\n if (!this.replicationPipeline) {\n return { success: true, ackedBy: [] };\n }\n return this.replicationPipeline.replicate(operation, opId, key, options);\n }\n\n /**\n * Get replication health status\n */\n public getReplicationHealth(): ReplicationHealth {\n return this.lagTracker.getHealth();\n }\n\n /**\n * Get replication lag for a specific node\n */\n public getReplicationLag(nodeId: string): ReplicationLag {\n return this.lagTracker.getLag(nodeId);\n }\n\n /**\n * Check if a node is healthy for replication\n */\n public isNodeHealthy(nodeId: string): boolean {\n return this.lagTracker.isNodeHealthy(nodeId);\n }\n\n /**\n * Check if a node is laggy\n */\n public isNodeLaggy(nodeId: string): boolean {\n return this.lagTracker.isNodeLaggy(nodeId);\n }\n\n // ============================================\n // Cluster Communication\n // ============================================\n\n /**\n * Send message to a specific node\n */\n public send(nodeId: string, message: unknown): void {\n this.clusterManager.sendToNode(nodeId, message);\n }\n\n /**\n * Broadcast message to all nodes\n */\n public broadcast(message: unknown): void {\n for (const nodeId of this.clusterManager.getMembers()) {\n if (!this.clusterManager.isLocal(nodeId)) {\n this.clusterManager.sendToNode(nodeId, message);\n }\n }\n }\n\n // ============================================\n // Component Access\n // ============================================\n\n /**\n * Get underlying ClusterManager\n */\n public getClusterManager(): ClusterManager {\n return this.clusterManager;\n }\n\n /**\n * Get underlying PartitionService\n */\n public getPartitionService(): PartitionService {\n return this.partitionService;\n }\n\n /**\n * Get underlying ReplicationPipeline\n */\n public getReplicationPipeline(): ReplicationPipeline | null {\n return this.replicationPipeline;\n }\n\n /**\n * Get underlying LagTracker\n */\n public getLagTracker(): LagTracker {\n return this.lagTracker;\n }\n\n // ============================================\n // Metrics Export\n // ============================================\n\n /**\n * Get all metrics in Prometheus format\n */\n public getPrometheusMetrics(): string {\n const lines: string[] = [];\n\n // Cluster info\n lines.push('# HELP topgun_cluster_members Number of cluster members');\n lines.push('# TYPE topgun_cluster_members gauge');\n lines.push(`topgun_cluster_members ${this.clusterManager.getMembers().length}`);\n\n lines.push('');\n lines.push('# HELP topgun_cluster_started Cluster started status (1=started, 0=stopped)');\n lines.push('# TYPE topgun_cluster_started gauge');\n lines.push(`topgun_cluster_started ${this.started ? 1 : 0}`);\n\n // Partition map info\n lines.push('');\n lines.push('# HELP topgun_partition_map_version Current partition map version');\n lines.push('# TYPE topgun_partition_map_version gauge');\n lines.push(`topgun_partition_map_version ${this.partitionService.getMapVersion()}`);\n\n // Migration metrics\n const migrationMetrics = this.getMigrationMetrics();\n if (migrationMetrics) {\n lines.push('');\n lines.push('# HELP topgun_migrations_started Total migrations started');\n lines.push('# TYPE topgun_migrations_started counter');\n lines.push(`topgun_migrations_started ${migrationMetrics.migrationsStarted}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_completed Total migrations completed');\n lines.push('# TYPE topgun_migrations_completed counter');\n lines.push(`topgun_migrations_completed ${migrationMetrics.migrationsCompleted}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_failed Total migrations failed');\n lines.push('# TYPE topgun_migrations_failed counter');\n lines.push(`topgun_migrations_failed ${migrationMetrics.migrationsFailed}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_active Currently active migrations');\n lines.push('# TYPE topgun_migrations_active gauge');\n lines.push(`topgun_migrations_active ${migrationMetrics.activeMigrations}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_queued Queued migrations');\n lines.push('# TYPE topgun_migrations_queued gauge');\n lines.push(`topgun_migrations_queued ${migrationMetrics.queuedMigrations}`);\n }\n\n // Replication metrics from LagTracker\n lines.push('');\n lines.push(this.lagTracker.toPrometheusMetrics());\n\n return lines.join('\\n');\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private setupEventHandlers(): void {\n // ClusterManager events\n this.clusterManager.on('memberJoined', (nodeId: string) => {\n logger.info({ nodeId }, 'Cluster member joined');\n this.emit('member:joined', nodeId);\n });\n\n this.clusterManager.on('memberLeft', (nodeId: string) => {\n logger.info({ nodeId }, 'Cluster member left');\n this.lagTracker.removeNode(nodeId);\n this.emit('member:left', nodeId);\n });\n\n // PartitionService events\n this.partitionService.on('rebalanced', (map: PartitionMap, changes: PartitionChange[]) => {\n logger.info({ version: map.version, changesCount: changes.length }, 'Partition map rebalanced');\n this.emit('partition:rebalanced', map, changes);\n });\n\n this.partitionService.on('partitionMoved', (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => {\n this.emit('partition:moved', info);\n });\n\n // MigrationManager events (if gradual rebalancing enabled)\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.on('migrationStarted', (partitionId: number, targetNode: string) => {\n this.emit('migration:started', partitionId, targetNode);\n });\n\n migrationManager.on('migrationComplete', (partitionId: number) => {\n this.emit('migration:completed', partitionId);\n });\n\n migrationManager.on('migrationFailed', (partitionId: number, error: Error) => {\n this.emit('migration:failed', partitionId, error);\n });\n }\n\n // ReplicationPipeline events\n if (this.replicationPipeline) {\n this.replicationPipeline.on('ackReceived', (nodeId: string) => {\n this.lagTracker.recordAck(nodeId);\n });\n\n this.replicationPipeline.on('replicationSent', (nodeId: string) => {\n this.lagTracker.incrementPending(nodeId);\n });\n }\n }\n}\n"],"mappings":"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","names":["import_fs","import_ws","import_core","import_core","pino","import_events","import_events","import_events","import_core","import_core","import_events","import_events","promises","import_ws","import_core","DEFAULT_CONFIG","DEFAULT_MAX_SIZE","DEFAULT_MAX_SIZE","DEFAULT_MAX_SIZE","DEFAULT_CONFIG","import_path","import_core","coreHashString","h","WORKER_THRESHOLD","taskIdCounter","generateTaskId","import_path","import_core","taskIdCounter","generateTaskId","coreSerialize","coreDeserialize","DEFAULT_CONFIG","DEFAULT_CONFIG","import_events","import_core","import_events","import_core","createHttpsServer","createHttpServer","import_core","import_events","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/ServerCoordinator.ts","../src/query/Matcher.ts","../src/query/QueryRegistry.ts","../src/utils/logger.ts","../src/topic/TopicManager.ts","../src/cluster/ClusterManager.ts","../src/cluster/FailureDetector.ts","../src/cluster/PartitionService.ts","../src/cluster/MigrationManager.ts","../src/cluster/LockManager.ts","../src/security/SecurityManager.ts","../src/monitoring/MetricsService.ts","../src/system/SystemManager.ts","../src/utils/BoundedEventQueue.ts","../src/utils/StripedEventExecutor.ts","../src/utils/BackpressureRegulator.ts","../src/utils/CoalescingWriter.ts","../src/memory/BufferPool.ts","../src/memory/ObjectPool.ts","../src/memory/pools/MessagePool.ts","../src/memory/pools/TimestampPool.ts","../src/memory/pools/RecordPool.ts","../src/utils/coalescingPresets.ts","../src/utils/ConnectionRateLimiter.ts","../src/workers/WorkerPool.ts","../src/workers/errors.ts","../src/workers/MerkleWorker.ts","../src/workers/CRDTMergeWorker.ts","../src/workers/SerializationWorker.ts","../src/workers/SharedMemoryManager.ts","../src/tasklet/TaskletScheduler.ts","../src/tasklet/tasklets/IteratorTasklet.ts","../src/ack/WriteAckManager.ts","../src/cluster/ReplicationPipeline.ts","../src/cluster/LagTracker.ts","../src/handlers/CounterHandler.ts","../src/handlers/EntryProcessorHandler.ts","../src/ProcessorSandbox.ts","../src/ConflictResolverService.ts","../src/handlers/ConflictResolverHandler.ts","../src/EventJournalService.ts","../src/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts","../src/utils/nativeStats.ts","../src/cluster/ClusterCoordinator.ts","../src/MapWithResolver.ts","../src/config/IndexConfig.ts","../src/config/MapFactory.ts"],"sourcesContent":["export * from './ServerCoordinator';\nexport * from './storage';\nexport * from './security/SecurityManager';\nexport * from './utils/logger';\nexport * from './utils/ConnectionRateLimiter';\nexport * from './utils/coalescingPresets';\nexport { CoalescingWriterOptions, CoalescingWriterMetrics } from './utils/CoalescingWriter';\nexport * from './memory';\nexport * from './tasklet';\nexport * from './interceptor/IInterceptor';\nexport { TimestampInterceptor } from './interceptor/TimestampInterceptor';\nexport { RateLimitInterceptor } from './interceptor/RateLimitInterceptor';\n\n// Native module utilities (Phase 3.05)\nexport {\n getNativeStats,\n getNativeModuleStatus,\n logNativeStatus,\n type NativeStats,\n type NativeModuleStatus,\n} from './utils/nativeStats';\n\n// Cluster module (Phase 4)\nexport * from './cluster';\n\n// Entry Processor (Phase 5.03)\nexport { ProcessorSandbox, ProcessorSandboxConfig, DEFAULT_SANDBOX_CONFIG } from './ProcessorSandbox';\nexport { EntryProcessorHandler, EntryProcessorHandlerConfig } from './handlers/EntryProcessorHandler';\n\n// Event Journal (Phase 5.04)\nexport {\n EventJournalService,\n EventJournalServiceConfig,\n DEFAULT_JOURNAL_SERVICE_CONFIG,\n ExportOptions,\n} from './EventJournalService';\n\n// Conflict Resolver (Phase 5.05)\nexport {\n ConflictResolverService,\n ConflictResolverServiceConfig,\n DEFAULT_CONFLICT_RESOLVER_CONFIG,\n} from './ConflictResolverService';\nexport {\n MapWithResolver,\n MapWithResolverConfig,\n SetWithResolverResult,\n} from './MapWithResolver';\nexport {\n ConflictResolverHandler,\n ConflictResolverHandlerConfig,\n MergeWithResolverResult,\n} from './handlers/ConflictResolverHandler';\n\n// Index Configuration (Phase 7.07)\nexport * from './config';\n","import { createServer as createHttpServer, Server as HttpServer } from 'http';\nimport { createServer as createHttpsServer, Server as HttpsServer, ServerOptions as HttpsServerOptions } from 'https';\nimport { readFileSync } from 'fs';\nimport * as net from 'net';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { HLC, LWWMap, ORMap, MerkleTree, serialize, deserialize, PermissionPolicy, Principal, PermissionType, Timestamp, LWWRecord, ORMapRecord, MessageSchema, WriteConcern, WriteConcernValue, ConsistencyLevel, ReplicationConfig, DEFAULT_REPLICATION_CONFIG, IndexedLWWMap, IndexedORMap, type QueryExpression as CoreQuery } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue, ORMapValue, ORMapTombstones } from './storage/IServerStorage';\nimport { IInterceptor, ServerOp, OpContext, ConnectionContext } from './interceptor/IInterceptor';\nimport * as jwt from 'jsonwebtoken';\nimport * as crypto from 'crypto';\nimport { QueryRegistry, Subscription } from './query/QueryRegistry';\n\nconst GC_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\nconst GC_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\nconst CLIENT_HEARTBEAT_TIMEOUT_MS = 20000; // 20 seconds - evict clients that haven't pinged\nconst CLIENT_HEARTBEAT_CHECK_INTERVAL_MS = 5000; // Check for dead clients every 5 seconds\nimport { TopicManager } from './topic/TopicManager';\nimport { ClusterManager } from './cluster/ClusterManager';\nimport { PartitionService } from './cluster/PartitionService';\nimport { LockManager } from './cluster/LockManager';\nimport { executeQuery, Query } from './query/Matcher';\nimport { SecurityManager } from './security/SecurityManager';\nimport { logger } from './utils/logger';\nimport { MetricsService } from './monitoring/MetricsService';\nimport { SystemManager } from './system/SystemManager';\nimport { TLSConfig, ClusterTLSConfig } from './types/TLSConfig';\nimport { StripedEventExecutor } from './utils/StripedEventExecutor';\nimport { BackpressureRegulator } from './utils/BackpressureRegulator';\nimport { CoalescingWriter, CoalescingWriterOptions } from './utils/CoalescingWriter';\nimport { coalescingPresets, CoalescingPreset } from './utils/coalescingPresets';\nimport { ConnectionRateLimiter } from './utils/ConnectionRateLimiter';\nimport { WorkerPool, MerkleWorker, CRDTMergeWorker, SerializationWorker, WorkerPoolConfig } from './workers';\nimport {\n ObjectPool,\n createEventPayloadPool,\n PooledEventPayload,\n} from './memory';\nimport { TaskletScheduler } from './tasklet';\nimport { WriteAckManager } from './ack/WriteAckManager';\nimport { ReplicationPipeline } from './cluster/ReplicationPipeline';\nimport { CounterHandler } from './handlers/CounterHandler';\nimport { EntryProcessorHandler } from './handlers/EntryProcessorHandler';\nimport { ConflictResolverHandler } from './handlers/ConflictResolverHandler';\nimport { EventJournalService, EventJournalServiceConfig } from './EventJournalService';\nimport type { JournalEvent, JournalEventType, MergeRejection, MergeContext } from '@topgunbuild/core';\n\ninterface ClientConnection {\n id: string;\n socket: WebSocket;\n writer: CoalescingWriter; // Per-connection write coalescing\n principal?: Principal; // Auth info\n isAuthenticated: boolean;\n subscriptions: Set<string>; // Set of Query IDs\n lastActiveHlc: Timestamp;\n lastPingReceived: number; // Date.now() of last PING received\n}\n\ninterface PendingClusterQuery {\n requestId: string;\n client: ClientConnection;\n queryId: string; // Client's Query ID\n mapName: string;\n query: Query;\n results: { key: string; value: any }[];\n expectedNodes: Set<string>;\n respondedNodes: Set<string>;\n timer: NodeJS.Timeout;\n}\n\nexport interface ServerCoordinatorConfig {\n port: number;\n nodeId: string;\n storage?: IServerStorage;\n jwtSecret?: string;\n host?: string;\n clusterPort?: number;\n peers?: string[];\n securityPolicies?: PermissionPolicy[];\n /** Callback to resolve dynamic peer addresses after ports are known */\n resolvePeers?: () => string[];\n interceptors?: IInterceptor[];\n metricsPort?: number;\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: TLSConfig;\n clusterTls?: ClusterTLSConfig;\n /** Total event queue capacity for bounded queue (default: 10000) */\n eventQueueCapacity?: number;\n /** Number of event queue stripes for parallel processing (default: 4) */\n eventStripeCount?: number;\n /** Enable/disable backpressure (default: true) */\n backpressureEnabled?: boolean;\n /** How often to force sync processing (default: 100 operations) */\n backpressureSyncFrequency?: number;\n /** Maximum pending async operations before blocking (default: 1000) */\n backpressureMaxPending?: number;\n /** Backoff timeout in ms when at capacity (default: 5000) */\n backpressureBackoffMs?: number;\n /** Enable/disable write coalescing (default: true) */\n writeCoalescingEnabled?: boolean;\n /** Coalescing preset: 'conservative', 'balanced', 'highThroughput', 'aggressive' (default: 'highThroughput') */\n writeCoalescingPreset?: CoalescingPreset;\n /** Maximum messages to batch before forcing flush (default: 500 for highThroughput) */\n writeCoalescingMaxBatch?: number;\n /** Maximum delay before flushing in ms (default: 10 for highThroughput) */\n writeCoalescingMaxDelayMs?: number;\n /** Maximum batch size in bytes (default: 262144/256KB for highThroughput) */\n writeCoalescingMaxBytes?: number;\n\n // === Connection Scaling Options ===\n /** WebSocket backlog for pending connections (default: 511) */\n wsBacklog?: number;\n /** Enable WebSocket per-message compression (default: false for CPU savings) */\n wsCompression?: boolean;\n /** Maximum WebSocket payload size in bytes (default: 64MB) */\n wsMaxPayload?: number;\n /** Maximum server connections (default: 10000) */\n maxConnections?: number;\n /** Server timeout in ms (default: 120000 = 2 min) */\n serverTimeout?: number;\n /** Keep-alive timeout in ms (default: 5000) */\n keepAliveTimeout?: number;\n /** Headers timeout in ms (default: 60000) */\n headersTimeout?: number;\n\n // === Rate Limiting Options ===\n /** Enable connection rate limiting (default: true) */\n rateLimitingEnabled?: boolean;\n /** Maximum new connections per second (default: 100) */\n maxConnectionsPerSecond?: number;\n /** Maximum pending connections (default: 1000) */\n maxPendingConnections?: number;\n\n // === Worker Pool Options ===\n /** Enable worker pool for CPU-bound operations (default: false) */\n workerPoolEnabled?: boolean;\n /** Worker pool configuration */\n workerPoolConfig?: Partial<WorkerPoolConfig>;\n\n // === Write Concern Options (Phase 5.01) ===\n /** Default timeout for Write Concern acknowledgments in ms (default: 5000) */\n writeAckTimeout?: number;\n\n // === Replication Options (Phase 4) ===\n /** Enable replication to backup nodes (default: true when cluster has peers) */\n replicationEnabled?: boolean;\n /** Default consistency level for replication (default: EVENTUAL) */\n defaultConsistency?: ConsistencyLevel;\n /** Replication configuration */\n replicationConfig?: Partial<ReplicationConfig>;\n\n // === Event Journal Options (Phase 5.04) ===\n /** Enable event journal for audit/CDC (default: false) */\n eventJournalEnabled?: boolean;\n /** Event journal configuration */\n eventJournalConfig?: Partial<Omit<EventJournalServiceConfig, 'pool'>>;\n}\n\nexport class ServerCoordinator {\n private httpServer: HttpServer | HttpsServer;\n private metricsServer?: HttpServer;\n private metricsService: MetricsService;\n private wss: WebSocketServer;\n private clients: Map<string, ClientConnection> = new Map();\n\n // Interceptors\n private interceptors: IInterceptor[] = [];\n\n // In-memory storage (partitioned later)\n private maps: Map<string, LWWMap<string, any> | ORMap<string, any>> = new Map();\n private hlc: HLC;\n private storage?: IServerStorage;\n private jwtSecret: string;\n private queryRegistry: QueryRegistry;\n\n private cluster!: ClusterManager;\n private partitionService!: PartitionService;\n private replicationPipeline?: ReplicationPipeline;\n private lockManager!: LockManager;\n private topicManager!: TopicManager;\n private securityManager: SecurityManager;\n private systemManager!: SystemManager;\n\n private pendingClusterQueries: Map<string, PendingClusterQuery> = new Map();\n private gcInterval?: NodeJS.Timeout;\n private heartbeatCheckInterval?: NodeJS.Timeout;\n\n // GC Consensus State\n private gcReports: Map<string, Timestamp> = new Map();\n\n // Track map loading state to avoid returning empty results during async load\n private mapLoadingPromises: Map<string, Promise<void>> = new Map();\n\n // Track pending batch operations for testing purposes\n private pendingBatchOperations: Set<Promise<void>> = new Set();\n\n // Bounded event queue executor for backpressure control\n private eventExecutor: StripedEventExecutor;\n\n // Backpressure regulator for periodic sync processing\n private backpressure: BackpressureRegulator;\n\n // Write coalescing options\n private writeCoalescingEnabled: boolean;\n private writeCoalescingOptions: Partial<CoalescingWriterOptions>;\n\n // Connection rate limiter\n private rateLimiter: ConnectionRateLimiter;\n private rateLimitingEnabled: boolean;\n\n // Worker pool for CPU-bound operations\n private workerPool?: WorkerPool;\n private merkleWorker?: MerkleWorker;\n private crdtMergeWorker?: CRDTMergeWorker;\n private serializationWorker?: SerializationWorker;\n\n // Memory pools for GC pressure reduction\n private eventPayloadPool: ObjectPool<PooledEventPayload>;\n\n // Tasklet scheduler for cooperative multitasking\n private taskletScheduler: TaskletScheduler;\n\n // Write Concern acknowledgment manager (Phase 5.01)\n private writeAckManager: WriteAckManager;\n\n // PN Counter handler (Phase 5.2)\n private counterHandler!: CounterHandler;\n\n // Entry Processor handler (Phase 5.03)\n private entryProcessorHandler!: EntryProcessorHandler;\n\n // Conflict Resolver handler (Phase 5.05)\n private conflictResolverHandler!: ConflictResolverHandler;\n\n // Event Journal (Phase 5.04)\n private eventJournalService?: EventJournalService;\n private journalSubscriptions: Map<string, { clientId: string; mapName?: string; types?: JournalEventType[] }> = new Map();\n\n private readonly _nodeId: string;\n\n private _actualPort: number = 0;\n private _actualClusterPort: number = 0;\n private _readyPromise: Promise<void>;\n private _readyResolve!: () => void;\n\n constructor(config: ServerCoordinatorConfig) {\n this._readyPromise = new Promise((resolve) => {\n this._readyResolve = resolve;\n });\n\n this._nodeId = config.nodeId;\n this.hlc = new HLC(config.nodeId);\n this.storage = config.storage;\n // Handle JWT_SECRET with escaped newlines (e.g., from Docker/Dokploy env vars)\n const rawSecret = config.jwtSecret || process.env.JWT_SECRET || 'topgun-secret-dev';\n this.jwtSecret = rawSecret.replace(/\\\\n/g, '\\n');\n this.queryRegistry = new QueryRegistry();\n this.securityManager = new SecurityManager(config.securityPolicies || []);\n this.interceptors = config.interceptors || [];\n this.metricsService = new MetricsService();\n\n // Initialize bounded event queue executor\n this.eventExecutor = new StripedEventExecutor({\n stripeCount: config.eventStripeCount ?? 4,\n queueCapacity: config.eventQueueCapacity ?? 10000,\n name: `${config.nodeId}-event-executor`,\n onReject: (task) => {\n logger.warn({ nodeId: config.nodeId, key: task.key }, 'Event task rejected due to queue capacity');\n this.metricsService.incEventQueueRejected();\n }\n });\n\n // Initialize backpressure regulator for periodic sync processing\n this.backpressure = new BackpressureRegulator({\n syncFrequency: config.backpressureSyncFrequency ?? 100,\n maxPendingOps: config.backpressureMaxPending ?? 1000,\n backoffTimeoutMs: config.backpressureBackoffMs ?? 5000,\n enabled: config.backpressureEnabled ?? true\n });\n\n // Initialize write coalescing options with preset support\n // Default preset changed from 'conservative' to 'highThroughput' for better performance\n this.writeCoalescingEnabled = config.writeCoalescingEnabled ?? true;\n const preset = coalescingPresets[config.writeCoalescingPreset ?? 'highThroughput'];\n this.writeCoalescingOptions = {\n maxBatchSize: config.writeCoalescingMaxBatch ?? preset.maxBatchSize,\n maxDelayMs: config.writeCoalescingMaxDelayMs ?? preset.maxDelayMs,\n maxBatchBytes: config.writeCoalescingMaxBytes ?? preset.maxBatchBytes,\n };\n\n // Initialize memory pools for GC pressure reduction\n this.eventPayloadPool = createEventPayloadPool({\n maxSize: 4096,\n initialSize: 128,\n });\n\n // Initialize tasklet scheduler for cooperative multitasking\n this.taskletScheduler = new TaskletScheduler({\n defaultTimeBudgetMs: 5,\n maxConcurrent: 20,\n });\n\n // Initialize Write Concern acknowledgment manager (Phase 5.01)\n this.writeAckManager = new WriteAckManager({\n defaultTimeout: config.writeAckTimeout ?? 5000,\n });\n\n // Initialize connection rate limiter\n this.rateLimitingEnabled = config.rateLimitingEnabled ?? true;\n this.rateLimiter = new ConnectionRateLimiter({\n maxConnectionsPerSecond: config.maxConnectionsPerSecond ?? 100,\n maxPendingConnections: config.maxPendingConnections ?? 1000,\n cooldownMs: 1000,\n });\n\n // Initialize worker pool for CPU-bound operations\n if (config.workerPoolEnabled) {\n this.workerPool = new WorkerPool({\n minWorkers: config.workerPoolConfig?.minWorkers ?? 2,\n maxWorkers: config.workerPoolConfig?.maxWorkers,\n taskTimeout: config.workerPoolConfig?.taskTimeout ?? 5000,\n idleTimeout: config.workerPoolConfig?.idleTimeout ?? 30000,\n autoRestart: config.workerPoolConfig?.autoRestart ?? true,\n });\n this.merkleWorker = new MerkleWorker(this.workerPool);\n this.crdtMergeWorker = new CRDTMergeWorker(this.workerPool);\n this.serializationWorker = new SerializationWorker(this.workerPool);\n logger.info({\n minWorkers: config.workerPoolConfig?.minWorkers ?? 2,\n maxWorkers: config.workerPoolConfig?.maxWorkers ?? 'auto'\n }, 'Worker pool initialized for CPU-bound operations');\n }\n\n // HTTP Server Setup first (to get actual port if port=0)\n if (config.tls?.enabled) {\n const tlsOptions = this.buildTLSOptions(config.tls);\n this.httpServer = createHttpsServer(tlsOptions, (_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running (Secure)');\n });\n logger.info('TLS enabled for client connections');\n } else {\n this.httpServer = createHttpServer((_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running');\n });\n\n if (process.env.NODE_ENV === 'production') {\n logger.warn('⚠️ TLS is disabled! Client connections are NOT encrypted.');\n }\n }\n\n const metricsPort = config.metricsPort !== undefined ? config.metricsPort : 9090;\n this.metricsServer = createHttpServer(async (req, res) => {\n if (req.url === '/metrics') {\n try {\n res.setHeader('Content-Type', this.metricsService.getContentType());\n res.end(await this.metricsService.getMetrics());\n } catch (err) {\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n } else {\n res.statusCode = 404;\n res.end();\n }\n });\n this.metricsServer.listen(metricsPort, () => {\n logger.info({ port: metricsPort }, 'Metrics server listening');\n });\n this.metricsServer.on('error', (err) => {\n logger.error({ err, port: metricsPort }, 'Metrics server failed to start');\n });\n\n // Configure WebSocketServer with optimal options for connection scaling\n this.wss = new WebSocketServer({\n server: this.httpServer,\n // Increase backlog for pending connections (default Linux is 128)\n backlog: config.wsBacklog ?? 511,\n // Disable per-message deflate by default (CPU overhead)\n perMessageDeflate: config.wsCompression ?? false,\n // Max payload size (64MB default)\n maxPayload: config.wsMaxPayload ?? 64 * 1024 * 1024,\n // Skip UTF-8 validation for binary messages (performance)\n skipUTF8Validation: true,\n });\n this.wss.on('connection', (ws) => this.handleConnection(ws));\n\n // Configure HTTP server limits for connection scaling\n this.httpServer.maxConnections = config.maxConnections ?? 10000;\n this.httpServer.timeout = config.serverTimeout ?? 120000; // 2 min\n this.httpServer.keepAliveTimeout = config.keepAliveTimeout ?? 5000;\n this.httpServer.headersTimeout = config.headersTimeout ?? 60000;\n\n // Configure socket options for all incoming connections\n this.httpServer.on('connection', (socket: net.Socket) => {\n // Disable Nagle's algorithm for lower latency\n socket.setNoDelay(true);\n // Enable keep-alive with 60s interval\n socket.setKeepAlive(true, 60000);\n });\n\n // Use port 0 to let OS assign a free port\n this.httpServer.listen(config.port, () => {\n const addr = this.httpServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : config.port;\n logger.info({ port: this._actualPort }, 'Server Coordinator listening');\n\n // Now setup cluster with actual/configured cluster port\n const clusterPort = config.clusterPort ?? 0;\n\n // Resolve peers dynamically if callback provided\n const peers = config.resolvePeers ? config.resolvePeers() : (config.peers || []);\n\n this.cluster = new ClusterManager({\n nodeId: config.nodeId,\n host: config.host || 'localhost',\n port: clusterPort,\n peers,\n discovery: config.discovery,\n serviceName: config.serviceName,\n discoveryInterval: config.discoveryInterval,\n tls: config.clusterTls\n });\n this.partitionService = new PartitionService(this.cluster);\n\n // Phase 4: Create ReplicationPipeline (Hazelcast pattern: always create, runtime check)\n // ReplicationPipeline checks cluster size at runtime - no replication for single node\n if (config.replicationEnabled !== false) {\n this.replicationPipeline = new ReplicationPipeline(\n this.cluster,\n this.partitionService,\n {\n ...DEFAULT_REPLICATION_CONFIG,\n defaultConsistency: config.defaultConsistency ?? ConsistencyLevel.EVENTUAL,\n ...config.replicationConfig,\n }\n );\n // Setup operation applier for incoming replications\n this.replicationPipeline.setOperationApplier(this.applyReplicatedOperation.bind(this));\n logger.info({ nodeId: config.nodeId }, 'ReplicationPipeline initialized');\n }\n\n // Phase 4: Listen for partition map changes and broadcast to clients\n this.partitionService.on('rebalanced', (partitionMap, changes) => {\n this.broadcastPartitionMap(partitionMap);\n });\n\n this.lockManager = new LockManager();\n this.lockManager.on('lockGranted', (evt) => this.handleLockGranted(evt));\n\n this.topicManager = new TopicManager({\n cluster: this.cluster,\n sendToClient: (clientId, message) => {\n const client = this.clients.get(clientId);\n if (client && client.socket.readyState === WebSocket.OPEN) {\n client.writer.write(message);\n }\n }\n });\n\n // PN Counter handler (Phase 5.2)\n this.counterHandler = new CounterHandler(this._nodeId);\n\n // Entry Processor handler (Phase 5.03)\n this.entryProcessorHandler = new EntryProcessorHandler({ hlc: this.hlc });\n\n // Conflict Resolver handler (Phase 5.05)\n this.conflictResolverHandler = new ConflictResolverHandler({ nodeId: this._nodeId });\n // Wire up rejection notifications to clients\n this.conflictResolverHandler.onRejection((rejection: MergeRejection) => {\n this.notifyMergeRejection(rejection);\n });\n\n // Event Journal (Phase 5.04) - requires PostgresAdapter with pool\n if (config.eventJournalEnabled && this.storage && 'pool' in (this.storage as any)) {\n const pool = (this.storage as any).pool;\n this.eventJournalService = new EventJournalService({\n capacity: 10000,\n ttlMs: 0,\n persistent: true,\n pool,\n ...config.eventJournalConfig,\n });\n this.eventJournalService.initialize().then(() => {\n logger.info('EventJournalService initialized');\n }).catch(err => {\n logger.error({ err }, 'Failed to initialize EventJournalService');\n });\n }\n\n this.systemManager = new SystemManager(\n this.cluster,\n this.metricsService,\n (name) => this.getMap(name) as LWWMap<string, any>\n );\n\n this.setupClusterListeners();\n this.cluster.start().then((actualClusterPort) => {\n this._actualClusterPort = actualClusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started');\n this.systemManager.start();\n this._readyResolve();\n }).catch((err) => {\n // Fallback for ClusterManager that doesn't return port\n this._actualClusterPort = clusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started (sync)');\n this.systemManager.start();\n this._readyResolve();\n });\n });\n\n if (this.storage) {\n this.storage.initialize().then(() => {\n logger.info('Storage adapter initialized');\n }).catch(err => {\n logger.error({ err }, 'Failed to initialize storage');\n });\n }\n\n this.startGarbageCollection();\n this.startHeartbeatCheck();\n }\n\n /** Wait for server to be fully ready (ports assigned) */\n public ready(): Promise<void> {\n return this._readyPromise;\n }\n\n /**\n * Wait for all pending batch operations to complete.\n * Useful for tests that need to verify state after OP_BATCH.\n */\n public async waitForPendingBatches(): Promise<void> {\n if (this.pendingBatchOperations.size === 0) return;\n await Promise.all(this.pendingBatchOperations);\n }\n\n /** Get the actual port the server is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n /** Get the actual cluster port */\n public get clusterPort(): number {\n return this._actualClusterPort;\n }\n\n /** Get event executor metrics for monitoring */\n public getEventExecutorMetrics() {\n return this.eventExecutor.getMetrics();\n }\n\n /** Get total event executor metrics across all stripes */\n public getEventExecutorTotalMetrics() {\n return this.eventExecutor.getTotalMetrics();\n }\n\n /** Get connection rate limiter stats for monitoring */\n public getRateLimiterStats() {\n return this.rateLimiter.getStats();\n }\n\n /** Get worker pool stats for monitoring */\n public getWorkerPoolStats() {\n return this.workerPool?.getStats() ?? null;\n }\n\n /** Check if worker pool is enabled */\n public get workerPoolEnabled(): boolean {\n return !!this.workerPool;\n }\n\n /** Get MerkleWorker for external use (null if worker pool disabled) */\n public getMerkleWorker(): MerkleWorker | null {\n return this.merkleWorker ?? null;\n }\n\n /** Get CRDTMergeWorker for external use (null if worker pool disabled) */\n public getCRDTMergeWorker(): CRDTMergeWorker | null {\n return this.crdtMergeWorker ?? null;\n }\n\n /** Get SerializationWorker for external use (null if worker pool disabled) */\n public getSerializationWorker(): SerializationWorker | null {\n return this.serializationWorker ?? null;\n }\n\n /** Get memory pool stats for monitoring GC pressure reduction */\n public getMemoryPoolStats() {\n return {\n eventPayloadPool: this.eventPayloadPool.getStats(),\n };\n }\n\n /** Get tasklet scheduler stats for monitoring cooperative multitasking */\n public getTaskletSchedulerStats() {\n return this.taskletScheduler.getStats();\n }\n\n /** Get tasklet scheduler for scheduling long-running operations */\n public getTaskletScheduler(): TaskletScheduler {\n return this.taskletScheduler;\n }\n\n public async shutdown() {\n logger.info('Shutting down Server Coordinator...');\n\n // 1. Stop accepting new connections\n this.httpServer.close();\n if (this.metricsServer) {\n this.metricsServer.close();\n }\n this.metricsService.destroy();\n this.wss.close();\n\n // 2. Notify and Close Clients\n logger.info(`Closing ${this.clients.size} client connections...`);\n const shutdownMsg = serialize({ type: 'SHUTDOWN_PENDING', retryAfter: 5000 });\n\n for (const client of this.clients.values()) {\n try {\n if (client.socket.readyState === WebSocket.OPEN) {\n // Send shutdown message directly to socket (bypass batching)\n // This ensures message is sent before socket.close()\n client.socket.send(shutdownMsg);\n if (client.writer) {\n client.writer.close();\n }\n client.socket.close(1001, 'Server Shutdown');\n }\n } catch (e) {\n logger.error({ err: e, clientId: client.id }, 'Error closing client connection');\n }\n }\n this.clients.clear();\n\n // 3. Shutdown event executor (wait for pending tasks)\n logger.info('Shutting down event executor...');\n await this.eventExecutor.shutdown(true);\n\n // 3.5. Shutdown worker pool\n if (this.workerPool) {\n logger.info('Shutting down worker pool...');\n await this.workerPool.shutdown(5000);\n logger.info('Worker pool shutdown complete.');\n }\n\n // 4. Close ReplicationPipeline\n if (this.replicationPipeline) {\n this.replicationPipeline.close();\n }\n\n // 5. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 6. Close Storage\n if (this.storage) {\n logger.info('Closing storage connection...');\n try {\n await this.storage.close();\n logger.info('Storage closed successfully.');\n } catch (err) {\n logger.error({ err }, 'Error closing storage');\n }\n }\n\n // 6. Cleanup\n if (this.gcInterval) {\n clearInterval(this.gcInterval);\n this.gcInterval = undefined;\n }\n\n if (this.heartbeatCheckInterval) {\n clearInterval(this.heartbeatCheckInterval);\n this.heartbeatCheckInterval = undefined;\n }\n\n // Stop LockManager\n if (this.lockManager) {\n this.lockManager.stop();\n }\n\n // Stop SystemManager\n if (this.systemManager) {\n this.systemManager.stop();\n }\n\n // Clear memory pools\n this.eventPayloadPool.clear();\n\n // Shutdown tasklet scheduler\n this.taskletScheduler.shutdown();\n\n // Shutdown Write Concern manager (Phase 5.01)\n this.writeAckManager.shutdown();\n\n // Dispose Entry Processor handler (Phase 5.03)\n this.entryProcessorHandler.dispose();\n\n // Dispose Event Journal (Phase 5.04)\n if (this.eventJournalService) {\n this.eventJournalService.dispose();\n }\n\n logger.info('Server Coordinator shutdown complete.');\n }\n\n private async handleConnection(ws: WebSocket) {\n // Check rate limit before accepting connection\n if (this.rateLimitingEnabled && !this.rateLimiter.shouldAccept()) {\n logger.warn('Connection rate limit exceeded, rejecting');\n this.rateLimiter.onConnectionRejected();\n this.metricsService.incConnectionsRejected();\n ws.close(1013, 'Server overloaded'); // 1013 = Try Again Later\n return;\n }\n\n // Register connection attempt\n if (this.rateLimitingEnabled) {\n this.rateLimiter.onConnectionAttempt();\n }\n this.metricsService.incConnectionsAccepted();\n\n // Client ID is temporary until auth\n const clientId = crypto.randomUUID();\n logger.info({ clientId }, 'Client connected (pending auth)');\n\n // Create CoalescingWriter if enabled, otherwise create a pass-through writer\n const writer = new CoalescingWriter(ws, this.writeCoalescingEnabled ? this.writeCoalescingOptions : {\n maxBatchSize: 1, // Disable batching by flushing immediately\n maxDelayMs: 0,\n maxBatchBytes: 0,\n });\n\n const connection: ClientConnection = {\n id: clientId,\n socket: ws,\n writer,\n isAuthenticated: false,\n subscriptions: new Set(),\n lastActiveHlc: this.hlc.now(), // Initialize with current time\n lastPingReceived: Date.now(), // Initialize heartbeat tracking\n };\n this.clients.set(clientId, connection);\n this.metricsService.setConnectedClients(this.clients.size);\n\n // Run onConnection interceptors\n try {\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onConnection) {\n await interceptor.onConnection(context);\n }\n }\n } catch (err) {\n logger.error({ clientId, err }, 'Interceptor rejected connection');\n ws.close(4000, 'Connection Rejected');\n this.clients.delete(clientId);\n return;\n }\n\n ws.on('message', (message) => {\n try {\n let data: any;\n let buf: Uint8Array;\n\n if (Buffer.isBuffer(message)) {\n buf = message;\n } else if (message instanceof ArrayBuffer) {\n buf = new Uint8Array(message);\n } else if (Array.isArray(message)) {\n buf = Buffer.concat(message);\n } else {\n // Fallback or unexpected type\n buf = Buffer.from(message as any);\n }\n\n try {\n data = deserialize(buf);\n } catch (e) {\n // If msgpack fails, try JSON (legacy support)\n try {\n // Use Buffer.toString() or TextDecoder\n const text = Buffer.isBuffer(buf) ? buf.toString() : new TextDecoder().decode(buf);\n data = JSON.parse(text);\n } catch (jsonErr) {\n // Original error likely relevant\n throw e;\n }\n }\n\n this.handleMessage(connection, data);\n } catch (err) {\n logger.error({ err }, 'Invalid message format');\n ws.close(1002, 'Protocol Error');\n }\n });\n\n ws.on('close', () => {\n logger.info({ clientId }, 'Client disconnected');\n\n // If connection was still pending (not authenticated), mark as failed\n if (this.rateLimitingEnabled && !connection.isAuthenticated) {\n this.rateLimiter.onPendingConnectionFailed();\n }\n\n // Close the CoalescingWriter to flush any pending messages\n connection.writer.close();\n\n // Run onDisconnect interceptors\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onDisconnect) {\n interceptor.onDisconnect(context).catch(err => {\n logger.error({ clientId, err }, 'Error in onDisconnect interceptor');\n });\n }\n }\n\n // Cleanup subscriptions\n for (const subId of connection.subscriptions) {\n this.queryRegistry.unregister(subId);\n }\n\n // Cleanup Locks (Local)\n this.lockManager.handleClientDisconnect(clientId);\n\n // Cleanup Topics (Local)\n this.topicManager.unsubscribeAll(clientId);\n\n // Cleanup Counters (Local)\n this.counterHandler.unsubscribeAll(clientId);\n\n // Notify Cluster to Cleanup Locks (Remote)\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_CLIENT_DISCONNECTED', {\n originNodeId: this.cluster.config.nodeId,\n clientId\n });\n }\n }\n\n this.clients.delete(clientId);\n this.metricsService.setConnectedClients(this.clients.size);\n });\n\n // Send Auth Challenge immediately\n ws.send(serialize({ type: 'AUTH_REQUIRED' }));\n }\n\n private async handleMessage(client: ClientConnection, rawMessage: any) {\n // Validation with Zod\n const parseResult = MessageSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n logger.error({ clientId: client.id, error: parseResult.error }, 'Invalid message format from client');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: 'Invalid message format', details: (parseResult.error as any).errors }\n }, true); // urgent\n return;\n }\n const message = parseResult.data;\n\n // Handle PING immediately (even before auth check for authenticated clients)\n if (message.type === 'PING') {\n this.handlePing(client, message.timestamp);\n return;\n }\n\n // Update client's last active HLC\n // Try to extract from payload if present, otherwise assume near current time but logically before next op\n this.updateClientHlc(client, message);\n\n // Handshake / Auth handling\n if (!client.isAuthenticated) {\n if (message.type === 'AUTH') {\n const token = message.token;\n try {\n // Verify JWT - support both HS256 (symmetric) and RS256 (asymmetric/Clerk)\n const isRSAKey = this.jwtSecret.includes('-----BEGIN');\n const verifyOptions: jwt.VerifyOptions = isRSAKey\n ? { algorithms: ['RS256'] }\n : { algorithms: ['HS256'] };\n const decoded = jwt.verify(token, this.jwtSecret, verifyOptions) as any;\n // Ensure roles exist\n if (!decoded.roles) {\n decoded.roles = ['USER']; // Default role\n }\n // Ensure userId exists (map from sub if needed)\n if (!decoded.userId && decoded.sub) {\n decoded.userId = decoded.sub;\n }\n\n client.principal = decoded;\n client.isAuthenticated = true;\n logger.info({ clientId: client.id, user: client.principal!.userId || 'anon' }, 'Client authenticated');\n\n // Mark connection as established (handshake complete)\n if (this.rateLimitingEnabled) {\n this.rateLimiter.onConnectionEstablished();\n }\n\n client.writer.write({ type: 'AUTH_ACK' }, true); // urgent: bypass batching\n return; // Stop processing this message\n } catch (e) {\n logger.error({ clientId: client.id, err: e }, 'Auth failed');\n client.writer.write({ type: 'AUTH_FAIL', error: 'Invalid token' }, true); // urgent\n client.socket.close(4001, 'Unauthorized');\n }\n } else {\n // Reject any other message before auth\n client.socket.close(4001, 'Auth required');\n }\n return;\n }\n\n // Standard Protocol Handling (Authenticated)\n switch (message.type) {\n case 'QUERY_SUB': {\n const { queryId, mapName, query } = message.payload;\n\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName }, 'Access Denied: QUERY_SUB');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${mapName}` }\n }, true);\n return;\n }\n\n logger.info({ clientId: client.id, mapName, query }, 'Client subscribed');\n this.metricsService.incOp('SUBSCRIBE', mapName);\n\n // Identify all relevant nodes\n const allMembers = this.cluster.getMembers();\n const remoteMembers = allMembers.filter(id => !this.cluster.isLocal(id));\n\n const requestId = crypto.randomUUID();\n\n const pending: PendingClusterQuery = {\n requestId,\n client,\n queryId,\n mapName,\n query,\n results: [], // Will populate with local results first\n expectedNodes: new Set(remoteMembers),\n respondedNodes: new Set(),\n timer: setTimeout(() => this.finalizeClusterQuery(requestId, true), 5000) // 5s timeout\n };\n\n this.pendingClusterQueries.set(requestId, pending);\n\n // Execute Locally (async - wait for map to load from storage)\n // [FIX] Using await ensures handleMessage completes only after query execution\n // This is important for:\n // 1. Tests that need to verify results immediately after handleMessage\n // 2. Ensuring storage is loaded before returning results\n try {\n const localResults = await this.executeLocalQuery(mapName, query);\n pending.results.push(...localResults);\n\n // Scatter: Send to other nodes\n if (remoteMembers.length > 0) {\n for (const nodeId of remoteMembers) {\n this.cluster.send(nodeId, 'CLUSTER_QUERY_EXEC', {\n requestId,\n mapName,\n query\n });\n }\n } else {\n // Single node cluster: finalize immediately\n this.finalizeClusterQuery(requestId);\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to execute local query');\n // Finalize with empty results on error\n this.finalizeClusterQuery(requestId);\n }\n break;\n }\n\n case 'QUERY_UNSUB': {\n const { queryId: unsubId } = message.payload;\n this.queryRegistry.unregister(unsubId);\n client.subscriptions.delete(unsubId);\n break;\n }\n\n case 'CLIENT_OP': {\n const op = message.payload;\n\n // Determine action type\n // LWW: op.record.value === null -> REMOVE\n // OR: OR_REMOVE or OR_ADD -> PUT (effectively)\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n this.metricsService.incOp(isRemove ? 'DELETE' : 'PUT', op.mapName);\n\n // Check Permission\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied: Client OP');\n client.writer.write({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: 'Access Denied' }\n });\n return;\n }\n\n logger.info({ clientId: client.id, opType: op.opType, key: op.key, mapName: op.mapName }, 'Received op');\n\n if (this.partitionService.isLocalOwner(op.key)) {\n this.processLocalOp(op, false, client.id).catch(err => {\n logger.error({ clientId: client.id, err }, 'Op failed');\n client.writer.write({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: err.message || 'Internal Error' }\n });\n });\n } else {\n const owner = this.partitionService.getOwner(op.key);\n logger.info({ key: op.key, owner }, 'Forwarding op');\n this.cluster.sendToNode(owner, op);\n }\n break;\n }\n\n case 'OP_BATCH': {\n const ops = message.payload.ops;\n // Extract batch-level Write Concern (Phase 5.01)\n const batchWriteConcern = (message.payload as any).writeConcern as WriteConcernValue | undefined;\n const batchTimeout = (message.payload as any).timeout as number | undefined;\n\n logger.info({ clientId: client.id, count: ops.length, writeConcern: batchWriteConcern }, 'Received batch');\n\n // === OPTIMIZATION 1: Early ACK ===\n // Fast validation pass - check permissions without processing\n const validOps: typeof ops = [];\n let rejectedCount = 0;\n let lastValidId: string | null = null;\n\n // Categorize ops by Write Concern for different ACK handling\n const memoryOps: typeof ops = []; // Ops that need immediate ACK (MEMORY or FIRE_AND_FORGET)\n const deferredOps: typeof ops = []; // Ops that need deferred ACK (APPLIED, REPLICATED, PERSISTED)\n\n for (const op of ops) {\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n rejectedCount++;\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied (Batch)');\n continue;\n }\n\n validOps.push(op);\n if (op.id) {\n lastValidId = op.id;\n }\n\n // Determine effective Write Concern for this operation\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n\n // Categorize by Write Concern level\n if (effectiveWriteConcern === 'FIRE_AND_FORGET' || effectiveWriteConcern === 'MEMORY' || !effectiveWriteConcern) {\n memoryOps.push(op);\n } else {\n deferredOps.push(op);\n }\n }\n\n // Send Early ACK for MEMORY/FIRE_AND_FORGET ops (backwards compatible)\n if (memoryOps.length > 0) {\n const lastMemoryId = memoryOps[memoryOps.length - 1].id;\n if (lastMemoryId) {\n client.writer.write({\n type: 'OP_ACK',\n payload: {\n lastId: lastMemoryId,\n achievedLevel: 'MEMORY'\n }\n });\n }\n }\n\n // Send rejection error if any ops were denied\n if (rejectedCount > 0) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Partial batch failure: ${rejectedCount} ops denied` }\n }, true);\n }\n\n // Register deferred ops with WriteAckManager for tracking\n for (const op of deferredOps) {\n if (op.id) {\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n const effectiveTimeout = op.timeout ?? batchTimeout;\n const wcLevel = this.stringToWriteConcern(effectiveWriteConcern);\n\n // Register and handle the promise\n this.writeAckManager.registerPending(op.id, wcLevel, effectiveTimeout)\n .then((result) => {\n // Send ACK when Write Concern is achieved\n client.writer.write({\n type: 'OP_ACK',\n payload: {\n lastId: op.id!,\n achievedLevel: result.achievedLevel,\n results: [{\n opId: op.id!,\n success: result.success,\n achievedLevel: result.achievedLevel,\n error: result.error\n }]\n }\n });\n })\n .catch((err) => {\n logger.error({ opId: op.id, err }, 'Write concern tracking failed');\n });\n }\n }\n\n // Process valid ops asynchronously (non-blocking)\n if (validOps.length > 0) {\n const batchPromise = new Promise<void>((resolve) => {\n setImmediate(() => {\n this.processBatchAsyncWithWriteConcern(validOps, client.id, batchWriteConcern, batchTimeout)\n .catch(err => {\n logger.error({ clientId: client.id, err }, 'Batch processing failed');\n })\n .finally(() => {\n this.pendingBatchOperations.delete(batchPromise);\n resolve();\n });\n });\n });\n this.pendingBatchOperations.add(batchPromise);\n }\n break;\n }\n\n case 'SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName: message.mapName }, 'Access Denied: SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }, true);\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'Client too old, sending SYNC_RESET_REQUIRED');\n client.writer.write({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n });\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested sync');\n this.metricsService.incOp('GET', message.mapName);\n\n // [FIX] Wait for map to be fully loaded from storage before sending rootHash\n // This prevents sending rootHash=0 for maps that are still loading from PostgreSQL\n try {\n const mapForSync = await this.getMapAsync(message.mapName);\n if (mapForSync instanceof LWWMap) {\n // Use the incremental Merkle Tree from LWWMap\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.writer.write({\n type: 'SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n });\n } else {\n // ORMap sync not implemented via Merkle Tree yet\n logger.warn({ mapName: message.mapName }, 'SYNC_INIT requested for ORMap - Not Implemented');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 501, message: `Merkle Sync not supported for ORMap ${message.mapName}` }\n }, true);\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }, true);\n }\n break;\n }\n\n case 'MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName, path } = message.payload;\n\n // [FIX] Wait for map to be fully loaded before accessing Merkle tree\n try {\n const mapForBucket = await this.getMapAsync(mapName);\n if (mapForBucket instanceof LWWMap) {\n const treeForBucket = mapForBucket.getMerkleTree();\n const buckets = treeForBucket.getBuckets(path);\n const node = treeForBucket.getNode(path);\n if (node && node.entries && node.entries.size > 0) {\n const diffRecords = [];\n for (const key of node.entries.keys()) {\n diffRecords.push({ key, record: mapForBucket.getRecord(key) });\n }\n client.writer.write({\n type: 'SYNC_RESP_LEAF',\n payload: { mapName, path, records: diffRecords }\n });\n } else {\n client.writer.write({\n type: 'SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n });\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'LOCK_REQUEST': {\n const { requestId, name, ttl } = message.payload;\n\n // 1. Access Control\n // Define a convention: lock names are resources.\n // Check if user has 'WRITE' permission on \"locks\" map or specific lock name.\n // Since locks are ephemeral, we might treat them as a special resource \"sys:locks\".\n // Or just check against the lock name itself.\n // Let's use `sys:lock:${name}` pattern or just `${name}`.\n // If we use just name, it might conflict with map names if policies are strict.\n // Assuming for now that lock name represents the resource being protected.\n if (!this.securityManager.checkPermission(client.principal!, name, 'PUT')) {\n client.writer.write({\n // We don't have LOCK_DENIED type in schema yet?\n // Using LOCK_RELEASED with success=false as a hack or ERROR.\n // Ideally ERROR.\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for lock ${name}` }\n }, true);\n return;\n }\n\n if (this.partitionService.isLocalOwner(name)) {\n const result = this.lockManager.acquire(name, client.id, requestId, ttl || 10000);\n if (result.granted) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken: result.fencingToken }\n });\n }\n // If not granted, it is queued. Response sent later via event.\n } else {\n const owner = this.partitionService.getOwner(name);\n // 2. Cluster Reliability Check\n if (!this.cluster.getMembers().includes(owner)) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 503, message: `Lock owner ${owner} is unavailable` }\n }, true);\n return;\n }\n\n this.cluster.send(owner, 'CLUSTER_LOCK_REQ', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n ttl\n });\n }\n break;\n }\n\n case 'LOCK_RELEASE': {\n const { requestId, name, fencingToken } = message.payload;\n\n if (this.partitionService.isLocalOwner(name)) {\n const success = this.lockManager.release(name, client.id, fencingToken);\n client.writer.write({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n });\n } else {\n const owner = this.partitionService.getOwner(name);\n this.cluster.send(owner, 'CLUSTER_LOCK_RELEASE', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n fencingToken\n });\n }\n break;\n }\n\n case 'TOPIC_SUB': {\n const { topic } = message.payload;\n\n // C1: Access Control\n // We treat topics as resources.\n // Policy check: action 'READ' on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'READ')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_SUB');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }, true);\n return;\n }\n\n try {\n this.topicManager.subscribe(client.id, topic);\n } catch (e: any) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }, true);\n }\n break;\n }\n\n case 'TOPIC_UNSUB': {\n const { topic } = message.payload;\n this.topicManager.unsubscribe(client.id, topic);\n break;\n }\n\n case 'TOPIC_PUB': {\n const { topic, data } = message.payload;\n\n // C1: Access Control\n // Policy check: action 'PUT' (publish) on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'PUT')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_PUB');\n // No error sent back? Fire and forget usually implies silent drop or async error.\n // But for security violations, an error is useful during dev.\n // Spec says fire-and-forget delivery, but security rejection should ideally notify.\n // Let's send error.\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }, true);\n return;\n }\n\n try {\n this.topicManager.publish(topic, data, client.id);\n } catch (e: any) {\n // Invalid topic name etc\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }, true);\n }\n break;\n }\n\n // ============ Phase 5.2: PN Counter Handlers ============\n\n case 'COUNTER_REQUEST': {\n const { name } = message.payload;\n const response = this.counterHandler.handleCounterRequest(client.id, name);\n client.writer.write(response);\n logger.debug({ clientId: client.id, name }, 'Counter request handled');\n break;\n }\n\n case 'COUNTER_SYNC': {\n const { name, state } = message.payload;\n const result = this.counterHandler.handleCounterSync(client.id, name, state);\n\n // Send response to the syncing client\n client.writer.write(result.response);\n\n // Broadcast to other subscribed clients\n for (const targetClientId of result.broadcastTo) {\n const targetClient = this.clients.get(targetClientId);\n if (targetClient && targetClient.socket.readyState === WebSocket.OPEN) {\n targetClient.writer.write(result.broadcastMessage);\n }\n }\n logger.debug({ clientId: client.id, name, broadcastCount: result.broadcastTo.length }, 'Counter sync handled');\n break;\n }\n\n // ============ Phase 5.03: Entry Processor Handlers ============\n\n case 'ENTRY_PROCESS': {\n const { requestId, mapName, key, processor } = message;\n\n // Check PUT permission (entry processor modifies data)\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'PUT')) {\n client.writer.write({\n type: 'ENTRY_PROCESS_RESPONSE',\n requestId,\n success: false,\n error: `Access Denied for map ${mapName}`,\n }, true);\n break;\n }\n\n // Get or create the map\n const entryMap = this.getMap(mapName) as LWWMap<string, any>;\n\n // Execute the processor\n const { result, timestamp } = await this.entryProcessorHandler.executeOnKey(\n entryMap,\n key,\n processor,\n );\n\n // Send response to client\n client.writer.write({\n type: 'ENTRY_PROCESS_RESPONSE',\n requestId,\n success: result.success,\n result: result.result,\n newValue: result.newValue,\n error: result.error,\n });\n\n // If successful and value changed, notify query subscribers\n if (result.success && timestamp) {\n const record = entryMap.getRecord(key);\n if (record) {\n this.queryRegistry.processChange(mapName, entryMap, key, record, undefined);\n }\n }\n\n logger.debug({\n clientId: client.id,\n mapName,\n key,\n processor: processor.name,\n success: result.success,\n }, 'Entry processor executed');\n break;\n }\n\n case 'ENTRY_PROCESS_BATCH': {\n const { requestId, mapName, keys, processor } = message;\n\n // Check PUT permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'PUT')) {\n const errorResults: Record<string, { success: boolean; error: string }> = {};\n for (const key of keys) {\n errorResults[key] = {\n success: false,\n error: `Access Denied for map ${mapName}`,\n };\n }\n client.writer.write({\n type: 'ENTRY_PROCESS_BATCH_RESPONSE',\n requestId,\n results: errorResults,\n }, true);\n break;\n }\n\n // Get or create the map\n const batchMap = this.getMap(mapName) as LWWMap<string, any>;\n\n // Execute the processor on all keys\n const { results, timestamps } = await this.entryProcessorHandler.executeOnKeys(\n batchMap,\n keys,\n processor,\n );\n\n // Convert Map to Record for serialization\n const resultsRecord: Record<string, {\n success: boolean;\n result?: unknown;\n newValue?: unknown;\n error?: string;\n }> = {};\n\n for (const [key, keyResult] of results) {\n resultsRecord[key] = {\n success: keyResult.success,\n result: keyResult.result,\n newValue: keyResult.newValue,\n error: keyResult.error,\n };\n }\n\n // Send batch response to client\n client.writer.write({\n type: 'ENTRY_PROCESS_BATCH_RESPONSE',\n requestId,\n results: resultsRecord,\n });\n\n // Notify query subscribers about changes\n for (const [key] of timestamps) {\n const record = batchMap.getRecord(key);\n if (record) {\n this.queryRegistry.processChange(mapName, batchMap, key, record, undefined);\n }\n }\n\n logger.debug({\n clientId: client.id,\n mapName,\n keyCount: keys.length,\n processor: processor.name,\n successCount: Array.from(results.values()).filter(r => r.success).length,\n }, 'Entry processor batch executed');\n break;\n }\n\n // ============ Phase 5.05: Conflict Resolver Handlers ============\n\n case 'REGISTER_RESOLVER': {\n const { requestId, mapName, resolver } = message;\n\n // Check PUT permission (resolver registration is a privileged operation)\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'PUT')) {\n client.writer.write({\n type: 'REGISTER_RESOLVER_RESPONSE',\n requestId,\n success: false,\n error: `Access Denied for map ${mapName}`,\n }, true);\n break;\n }\n\n try {\n this.conflictResolverHandler.registerResolver(\n mapName,\n {\n name: resolver.name,\n code: resolver.code,\n priority: resolver.priority,\n keyPattern: resolver.keyPattern,\n },\n client.id,\n );\n\n client.writer.write({\n type: 'REGISTER_RESOLVER_RESPONSE',\n requestId,\n success: true,\n });\n\n logger.info({\n clientId: client.id,\n mapName,\n resolverName: resolver.name,\n priority: resolver.priority,\n }, 'Conflict resolver registered');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n client.writer.write({\n type: 'REGISTER_RESOLVER_RESPONSE',\n requestId,\n success: false,\n error: errorMessage,\n }, true);\n logger.warn({\n clientId: client.id,\n mapName,\n error: errorMessage,\n }, 'Failed to register conflict resolver');\n }\n break;\n }\n\n case 'UNREGISTER_RESOLVER': {\n const { requestId, mapName, resolverName } = message;\n\n // Check PUT permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'PUT')) {\n client.writer.write({\n type: 'UNREGISTER_RESOLVER_RESPONSE',\n requestId,\n success: false,\n error: `Access Denied for map ${mapName}`,\n }, true);\n break;\n }\n\n const removed = this.conflictResolverHandler.unregisterResolver(\n mapName,\n resolverName,\n client.id,\n );\n\n client.writer.write({\n type: 'UNREGISTER_RESOLVER_RESPONSE',\n requestId,\n success: removed,\n error: removed ? undefined : 'Resolver not found or not owned by this client',\n });\n\n if (removed) {\n logger.info({\n clientId: client.id,\n mapName,\n resolverName,\n }, 'Conflict resolver unregistered');\n }\n break;\n }\n\n case 'LIST_RESOLVERS': {\n const { requestId, mapName } = message;\n\n // Check READ permission if mapName specified\n if (mapName && !this.securityManager.checkPermission(client.principal!, mapName, 'READ')) {\n client.writer.write({\n type: 'LIST_RESOLVERS_RESPONSE',\n requestId,\n resolvers: [],\n });\n break;\n }\n\n const resolvers = this.conflictResolverHandler.listResolvers(mapName);\n\n client.writer.write({\n type: 'LIST_RESOLVERS_RESPONSE',\n requestId,\n resolvers,\n });\n break;\n }\n\n // ============ Phase 4: Partition Map Request Handler ============\n\n case 'PARTITION_MAP_REQUEST': {\n // Client is requesting the current partition map\n // This is used for cluster-aware routing\n const clientVersion = message.payload?.currentVersion ?? 0;\n const currentMap = this.partitionService.getPartitionMap();\n\n // Only send if client has stale version or no version\n if (clientVersion < currentMap.version) {\n client.writer.write({\n type: 'PARTITION_MAP',\n payload: currentMap\n });\n logger.debug({\n clientId: client.id,\n clientVersion,\n serverVersion: currentMap.version\n }, 'Sent partition map to client');\n }\n break;\n }\n\n // ============ ORMap Sync Message Handlers ============\n\n case 'ORMAP_SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }, true);\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'ORMap client too old, sending SYNC_RESET_REQUIRED');\n client.writer.write({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n });\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested ORMap sync');\n this.metricsService.incOp('GET', message.mapName);\n\n try {\n const mapForSync = await this.getMapAsync(message.mapName, 'OR');\n if (mapForSync instanceof ORMap) {\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n });\n } else {\n // It's actually an LWWMap, client should use SYNC_INIT\n client.writer.write({\n type: 'ERROR',\n payload: { code: 400, message: `Map ${message.mapName} is not an ORMap` }\n }, true);\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for ORMAP_SYNC_INIT');\n client.writer.write({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }, true);\n }\n break;\n }\n\n case 'ORMAP_MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName, path } = message.payload;\n\n try {\n const mapForBucket = await this.getMapAsync(mapName, 'OR');\n if (mapForBucket instanceof ORMap) {\n const tree = mapForBucket.getMerkleTree();\n const buckets = tree.getBuckets(path);\n const isLeaf = tree.isLeaf(path);\n\n if (isLeaf) {\n // This is a leaf node - send actual records\n const keys = tree.getKeysInBucket(path);\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n\n for (const key of keys) {\n const recordsMap = mapForBucket.getRecordsMap(key);\n if (recordsMap && recordsMap.size > 0) {\n entries.push({\n key,\n records: Array.from(recordsMap.values()),\n tombstones: mapForBucket.getTombstones()\n });\n }\n }\n\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_LEAF',\n payload: { mapName, path, entries }\n });\n } else {\n // Not a leaf - send bucket hashes\n client.writer.write({\n type: 'ORMAP_SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n });\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for ORMAP_MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'ORMAP_DIFF_REQUEST': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName: diffMapName, keys } = message.payload;\n\n try {\n const mapForDiff = await this.getMapAsync(diffMapName, 'OR');\n if (mapForDiff instanceof ORMap) {\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n const allTombstones = mapForDiff.getTombstones();\n\n for (const key of keys) {\n const recordsMap = mapForDiff.getRecordsMap(key);\n entries.push({\n key,\n records: recordsMap ? Array.from(recordsMap.values()) : [],\n tombstones: allTombstones\n });\n }\n\n client.writer.write({\n type: 'ORMAP_DIFF_RESPONSE',\n payload: { mapName: diffMapName, entries }\n });\n }\n } catch (err) {\n logger.error({ err, mapName: diffMapName }, 'Failed to load map for ORMAP_DIFF_REQUEST');\n }\n break;\n }\n\n case 'ORMAP_PUSH_DIFF': {\n // Check WRITE permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'PUT')) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }, true);\n return;\n }\n\n const { mapName: pushMapName, entries: pushEntries } = message.payload;\n\n try {\n const mapForPush = await this.getMapAsync(pushMapName, 'OR');\n if (mapForPush instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of pushEntries) {\n const { key, records, tombstones } = entry;\n const result = mapForPush.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName: pushMapName, added: totalAdded, updated: totalUpdated, clientId: client.id }, 'Merged ORMap diff from client');\n\n // Broadcast changes to other clients\n for (const entry of pushEntries) {\n for (const record of entry.records) {\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: {\n mapName: pushMapName,\n eventType: 'OR_ADD',\n key: entry.key,\n orRecord: record\n }\n }, client.id);\n }\n }\n\n // Persist to storage\n if (this.storage) {\n for (const entry of pushEntries) {\n const recordsMap = mapForPush.getRecordsMap(entry.key);\n if (recordsMap && recordsMap.size > 0) {\n await this.storage.store(pushMapName, entry.key, {\n type: 'OR',\n records: Array.from(recordsMap.values())\n });\n }\n }\n }\n }\n }\n } catch (err) {\n logger.error({ err, mapName: pushMapName }, 'Failed to process ORMAP_PUSH_DIFF');\n }\n break;\n }\n\n // === Event Journal Messages (Phase 5.04) ===\n\n case 'JOURNAL_SUBSCRIBE': {\n if (!this.eventJournalService) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 503, message: 'Event journal not enabled' }\n }, true);\n break;\n }\n\n const { requestId, fromSequence, mapName, types } = message;\n const subscriptionId = requestId;\n\n // Store subscription metadata\n this.journalSubscriptions.set(subscriptionId, {\n clientId: client.id,\n mapName,\n types,\n });\n\n // Subscribe to journal events\n const unsubscribe = this.eventJournalService.subscribe(\n (event) => {\n // Apply filters\n if (mapName && event.mapName !== mapName) return;\n if (types && types.length > 0 && !types.includes(event.type)) return;\n\n // Check if client still connected\n const clientConn = this.clients.get(client.id);\n if (!clientConn) {\n unsubscribe();\n this.journalSubscriptions.delete(subscriptionId);\n return;\n }\n\n // Send event to client\n clientConn.writer.write({\n type: 'JOURNAL_EVENT',\n event: {\n sequence: event.sequence.toString(),\n type: event.type,\n mapName: event.mapName,\n key: event.key,\n value: event.value,\n previousValue: event.previousValue,\n timestamp: event.timestamp,\n nodeId: event.nodeId,\n metadata: event.metadata,\n },\n });\n },\n fromSequence ? BigInt(fromSequence) : undefined\n );\n\n logger.info({ clientId: client.id, subscriptionId, mapName }, 'Journal subscription created');\n break;\n }\n\n case 'JOURNAL_UNSUBSCRIBE': {\n const { subscriptionId } = message;\n this.journalSubscriptions.delete(subscriptionId);\n logger.info({ clientId: client.id, subscriptionId }, 'Journal subscription removed');\n break;\n }\n\n case 'JOURNAL_READ': {\n if (!this.eventJournalService) {\n client.writer.write({\n type: 'ERROR',\n payload: { code: 503, message: 'Event journal not enabled' }\n }, true);\n break;\n }\n\n const { requestId: readReqId, fromSequence: readFromSeq, limit, mapName: readMapName } = message;\n const startSeq = BigInt(readFromSeq);\n const eventLimit = limit ?? 100;\n\n let events = this.eventJournalService.readFrom(startSeq, eventLimit);\n\n // Filter by map name if provided\n if (readMapName) {\n events = events.filter(e => e.mapName === readMapName);\n }\n\n // Serialize events\n const serializedEvents = events.map(e => ({\n sequence: e.sequence.toString(),\n type: e.type,\n mapName: e.mapName,\n key: e.key,\n value: e.value,\n previousValue: e.previousValue,\n timestamp: e.timestamp,\n nodeId: e.nodeId,\n metadata: e.metadata,\n }));\n\n client.writer.write({\n type: 'JOURNAL_READ_RESPONSE',\n requestId: readReqId,\n events: serializedEvents,\n hasMore: events.length === eventLimit,\n });\n break;\n }\n\n default:\n logger.warn({ type: message.type }, 'Unknown message type');\n }\n }\n\n private updateClientHlc(client: ClientConnection, message: any) {\n // Try to extract timestamp from message if available\n // This is heuristic based on typical message structure\n let ts: Timestamp | undefined;\n\n if (message.type === 'CLIENT_OP') {\n const op = message.payload;\n if (op.record && op.record.timestamp) {\n ts = op.record.timestamp;\n } else if (op.orRecord && op.orRecord.timestamp) {\n // orRecord usually has entries which have timestamps, or value itself is decorated?\n // Depends on implementation.\n } else if (op.orTag) {\n try {\n ts = HLC.parse(op.orTag);\n } catch (e) { }\n }\n }\n\n if (ts) {\n // Client sent an explicit timestamp, update their HLC\n this.hlc.update(ts); // Also update server clock\n // Client HLC is at least this\n client.lastActiveHlc = ts;\n } else {\n // Just bump to current server time if no explicit TS\n // This assumes client is \"alive\" at this moment.\n client.lastActiveHlc = this.hlc.now();\n }\n }\n\n // ============ Phase 4: Partition Map Broadcast ============\n\n /**\n * Broadcast partition map to all connected and authenticated clients.\n * Called when partition topology changes (node join/leave/failover).\n */\n private broadcastPartitionMap(partitionMap: any): void {\n const message = {\n type: 'PARTITION_MAP',\n payload: partitionMap\n };\n\n let broadcastCount = 0;\n for (const client of this.clients.values()) {\n if (client.isAuthenticated && client.socket.readyState === WebSocket.OPEN) {\n client.writer.write(message);\n broadcastCount++;\n }\n }\n\n logger.info({\n version: partitionMap.version,\n clientCount: broadcastCount\n }, 'Broadcast partition map to clients');\n }\n\n /**\n * Notify a client about a merge rejection (Phase 5.05).\n * Finds the client by node ID and sends MERGE_REJECTED message.\n */\n private notifyMergeRejection(rejection: MergeRejection): void {\n // Find client by node ID\n // Node ID format: \"client-{uuid}\" - we need to find matching client\n for (const [clientId, client] of this.clients) {\n // Check if this client sent the rejected operation\n // The nodeId in rejection matches the remoteNodeId from the operation\n if (clientId === rejection.nodeId || rejection.nodeId.includes(clientId)) {\n client.writer.write({\n type: 'MERGE_REJECTED',\n mapName: rejection.mapName,\n key: rejection.key,\n attemptedValue: rejection.attemptedValue,\n reason: rejection.reason,\n timestamp: rejection.timestamp,\n }, true); // urgent - bypass batching\n return;\n }\n }\n\n // If no matching client found, broadcast to all clients subscribed to this map\n const subscribedClientIds = this.queryRegistry.getSubscribedClientIds(rejection.mapName);\n for (const clientId of subscribedClientIds) {\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'MERGE_REJECTED',\n mapName: rejection.mapName,\n key: rejection.key,\n attemptedValue: rejection.attemptedValue,\n reason: rejection.reason,\n timestamp: rejection.timestamp,\n });\n }\n }\n }\n\n private broadcast(message: any, excludeClientId?: string) {\n const isServerEvent = message.type === 'SERVER_EVENT';\n\n if (isServerEvent) {\n const payload = message.payload;\n const mapName = payload.mapName;\n\n // === SUBSCRIPTION-BASED ROUTING ===\n // Only send to clients that have active subscriptions for this map\n const subscribedClientIds = this.queryRegistry.getSubscribedClientIds(mapName);\n\n // Track metrics\n this.metricsService.incEventsRouted();\n\n if (subscribedClientIds.size === 0) {\n // Early exit - no subscribers for this map!\n this.metricsService.incEventsFilteredBySubscription();\n return;\n }\n\n // Track average subscribers per event\n this.metricsService.recordSubscribersPerEvent(subscribedClientIds.size);\n\n // Send only to subscribed clients with FLS filtering\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n // Shallow clone payload for FLS filtering\n const newPayload = { ...payload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(newPayload.record.value, client.principal, mapName);\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(newPayload.orRecord.value, client.principal, mapName);\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n client.writer.write({ ...message, payload: newPayload });\n }\n } else {\n // Non-event messages (GC_PRUNE, SHUTDOWN_PENDING) still go to all clients\n const msgData = serialize(message);\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1) { // 1 = OPEN\n client.writer.writeRaw(msgData);\n }\n }\n }\n }\n\n /**\n * === OPTIMIZATION 2 & 3: Batched Broadcast with Serialization Caching ===\n * Groups clients by their permission roles and serializes once per group.\n * Also batches multiple events into a single SERVER_BATCH_EVENT message.\n * === OPTIMIZATION 4: Subscription-based Routing ===\n * Only sends events to clients with active subscriptions for affected maps.\n */\n private broadcastBatch(events: any[], excludeClientId?: string): void {\n if (events.length === 0) return;\n\n // === SUBSCRIPTION-BASED ROUTING ===\n // Get unique map names from events\n const affectedMaps = new Set<string>();\n for (const event of events) {\n if (event.mapName) {\n affectedMaps.add(event.mapName);\n }\n }\n\n // Get all subscribed client IDs across all affected maps\n const subscribedClientIds = new Set<string>();\n for (const mapName of affectedMaps) {\n const mapSubscribers = this.queryRegistry.getSubscribedClientIds(mapName);\n for (const clientId of mapSubscribers) {\n subscribedClientIds.add(clientId);\n }\n }\n\n // Track metrics\n this.metricsService.incEventsRouted();\n\n if (subscribedClientIds.size === 0) {\n // Early exit - no subscribers for any of the affected maps!\n this.metricsService.incEventsFilteredBySubscription();\n return;\n }\n\n this.metricsService.recordSubscribersPerEvent(subscribedClientIds.size);\n\n // Group subscribed clients by their role signature for serialization caching\n const clientsByRoleSignature = new Map<string, ClientConnection[]>();\n\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n // Create a role signature for grouping (sorted roles joined)\n const roleSignature = (client.principal.roles || ['USER']).sort().join(',');\n\n if (!clientsByRoleSignature.has(roleSignature)) {\n clientsByRoleSignature.set(roleSignature, []);\n }\n clientsByRoleSignature.get(roleSignature)!.push(client);\n }\n\n // For each role group, filter events once and serialize once\n for (const [, clients] of clientsByRoleSignature) {\n if (clients.length === 0) continue;\n\n // Use first client as representative for filtering (same roles = same permissions)\n const representativeClient = clients[0];\n\n // Filter all events for this role group\n const filteredEvents = events.map(eventPayload => {\n const mapName = eventPayload.mapName;\n const newPayload = { ...eventPayload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(\n newPayload.record.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(\n newPayload.orRecord.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n return newPayload;\n });\n\n // Serialize ONCE for this entire group\n const batchMessage = {\n type: 'SERVER_BATCH_EVENT',\n payload: { events: filteredEvents },\n timestamp: this.hlc.now()\n };\n const serializedBatch = serialize(batchMessage);\n\n // Send to all clients in this role group\n for (const client of clients) {\n try {\n client.writer.writeRaw(serializedBatch);\n } catch (err) {\n logger.error({ clientId: client.id, err }, 'Failed to send batch to client');\n }\n }\n }\n }\n\n /**\n * Helper method to get role signature for a client (for caching key)\n */\n private getClientRoleSignature(client: ClientConnection): string {\n if (!client.principal || !client.principal.roles) {\n return 'USER';\n }\n return client.principal.roles.sort().join(',');\n }\n\n /**\n * === BACKPRESSURE: Synchronous Broadcast ===\n * Same as broadcastBatch but waits for all sends to complete.\n * Used when backpressure forces sync processing to drain the pipeline.\n */\n private async broadcastBatchSync(events: any[], excludeClientId?: string): Promise<void> {\n if (events.length === 0) return;\n\n // Get unique map names from events\n const affectedMaps = new Set<string>();\n for (const event of events) {\n if (event.mapName) {\n affectedMaps.add(event.mapName);\n }\n }\n\n // Get all subscribed client IDs across all affected maps\n const subscribedClientIds = new Set<string>();\n for (const mapName of affectedMaps) {\n const mapSubscribers = this.queryRegistry.getSubscribedClientIds(mapName);\n for (const clientId of mapSubscribers) {\n subscribedClientIds.add(clientId);\n }\n }\n\n if (subscribedClientIds.size === 0) {\n return;\n }\n\n // Group subscribed clients by their role signature\n const clientsByRoleSignature = new Map<string, ClientConnection[]>();\n\n for (const clientId of subscribedClientIds) {\n if (clientId === excludeClientId) continue;\n\n const client = this.clients.get(clientId);\n if (!client || client.socket.readyState !== 1 || !client.isAuthenticated || !client.principal) {\n continue;\n }\n\n const roleSignature = (client.principal.roles || ['USER']).sort().join(',');\n\n if (!clientsByRoleSignature.has(roleSignature)) {\n clientsByRoleSignature.set(roleSignature, []);\n }\n clientsByRoleSignature.get(roleSignature)!.push(client);\n }\n\n // Collect all send promises\n const sendPromises: Promise<void>[] = [];\n\n for (const [, clients] of clientsByRoleSignature) {\n if (clients.length === 0) continue;\n\n const representativeClient = clients[0];\n\n // Filter all events for this role group\n const filteredEvents = events.map(eventPayload => {\n const mapName = eventPayload.mapName;\n const newPayload = { ...eventPayload };\n\n if (newPayload.record) {\n const newVal = this.securityManager.filterObject(\n newPayload.record.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) {\n const newVal = this.securityManager.filterObject(\n newPayload.orRecord.value,\n representativeClient.principal!,\n mapName\n );\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n return newPayload;\n });\n\n const batchMessage = {\n type: 'SERVER_BATCH_EVENT',\n payload: { events: filteredEvents },\n timestamp: this.hlc.now()\n };\n const serializedBatch = serialize(batchMessage);\n\n // Send to all clients and collect promises\n for (const client of clients) {\n sendPromises.push(new Promise<void>((resolve, reject) => {\n try {\n client.socket.send(serializedBatch, (err) => {\n if (err) {\n logger.error({ clientId: client.id, err }, 'Failed to send sync batch to client');\n reject(err);\n } else {\n resolve();\n }\n });\n } catch (err) {\n logger.error({ clientId: client.id, err }, 'Exception sending sync batch to client');\n reject(err);\n }\n }));\n }\n }\n\n // Wait for all sends to complete (ignore individual failures)\n await Promise.allSettled(sendPromises);\n }\n\n private setupClusterListeners() {\n this.cluster.on('memberJoined', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n this.cluster.on('memberLeft', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n\n this.cluster.on('message', (msg) => {\n switch (msg.type) {\n case 'OP_FORWARD':\n logger.info({ senderId: msg.senderId }, 'Received forwarded op');\n if (this.partitionService.isLocalOwner(msg.payload.key)) {\n this.processLocalOp(msg.payload, true, msg.senderId).catch(err => {\n logger.error({ err, senderId: msg.senderId }, 'Forwarded op failed');\n });\n } else {\n logger.warn({ key: msg.payload.key }, 'Received OP_FORWARD but not owner. Dropping.');\n }\n break;\n case 'CLUSTER_EVENT':\n this.handleClusterEvent(msg.payload);\n break;\n\n case 'CLUSTER_QUERY_EXEC': {\n const { requestId, mapName, query } = msg.payload;\n this.executeLocalQuery(mapName, query).then(results => {\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results\n });\n }).catch(err => {\n logger.error({ err, mapName }, 'Failed to execute cluster query');\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results: []\n });\n });\n break;\n }\n\n case 'CLUSTER_QUERY_RESP': {\n const { requestId: reqId, results: remoteResults } = msg.payload;\n const pendingQuery = this.pendingClusterQueries.get(reqId);\n if (pendingQuery) {\n pendingQuery.results.push(...remoteResults);\n pendingQuery.respondedNodes.add(msg.senderId);\n\n if (pendingQuery.respondedNodes.size === pendingQuery.expectedNodes.size) {\n this.finalizeClusterQuery(reqId);\n }\n }\n break;\n }\n\n case 'CLUSTER_GC_REPORT': {\n this.handleGcReport(msg.senderId, msg.payload.minHlc);\n break;\n }\n\n case 'CLUSTER_GC_COMMIT': {\n this.performGarbageCollection(msg.payload.safeTimestamp);\n break;\n }\n\n case 'CLUSTER_LOCK_REQ': {\n const { originNodeId, clientId, requestId, name, ttl } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const result = this.lockManager.acquire(name, compositeId, requestId, ttl || 10000);\n if (result.granted) {\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId,\n requestId,\n name,\n fencingToken: result.fencingToken\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASE': {\n const { originNodeId, clientId, requestId, name, fencingToken } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const success = this.lockManager.release(name, compositeId, fencingToken);\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_RELEASED', {\n clientId, requestId, name, success\n });\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASED': {\n const { clientId, requestId, name, success } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_GRANTED': {\n const { clientId, requestId, name, fencingToken } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n });\n }\n break;\n }\n\n case 'CLUSTER_CLIENT_DISCONNECTED': {\n const { clientId, originNodeId } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n this.lockManager.handleClientDisconnect(compositeId);\n break;\n }\n\n case 'CLUSTER_TOPIC_PUB': {\n const { topic, data, originalSenderId } = msg.payload;\n this.topicManager.publish(topic, data, originalSenderId, true);\n break;\n }\n }\n });\n }\n\n private async executeLocalQuery(mapName: string, query: Query) {\n // Wait for map to be fully loaded from storage before querying\n const map = await this.getMapAsync(mapName);\n\n // Fix: Do not apply offset/limit locally for cluster queries.\n // They will be applied in finalizeClusterQuery after aggregation.\n const localQuery = { ...query };\n delete localQuery.offset;\n delete localQuery.limit;\n\n // Use indexed query execution if available (O(1) to O(log N))\n if (map instanceof IndexedLWWMap) {\n // Convert Query to core query format for indexed execution\n const coreQuery = this.convertToCoreQuery(localQuery);\n if (coreQuery) {\n const entries = map.queryEntries(coreQuery);\n return entries.map(([key, value]) => {\n const record = map.getRecord(key);\n return { key, value, timestamp: record?.timestamp };\n });\n }\n }\n\n if (map instanceof IndexedORMap) {\n const coreQuery = this.convertToCoreQuery(localQuery);\n if (coreQuery) {\n const results = map.query(coreQuery);\n return results.map(({ key, value }) => ({ key, value }));\n }\n }\n\n // Fallback to full scan for non-indexed maps\n const records = new Map<string, any>();\n\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const rec = map.getRecord(key);\n if (rec && rec.value !== null) {\n records.set(key, rec);\n }\n }\n } else if (map instanceof ORMap) {\n // For ORMap, we flatten values. A key matches if ANY of its values match?\n // Or we expose the array of values?\n // For now, we expose { key, value: [v1, v2, ...] }\n const items = (map as any).items as Map<string, any>;\n for (const key of items.keys()) {\n const values = map.get(key);\n if (values.length > 0) {\n records.set(key, { value: values });\n }\n }\n }\n\n return executeQuery(records, localQuery);\n }\n\n /**\n * Convert server Query format to core Query format for indexed execution.\n * Returns null if conversion is not possible (complex queries).\n */\n private convertToCoreQuery(query: Query): CoreQuery | null {\n // Handle predicate-based queries (core format)\n if (query.predicate) {\n return this.predicateToCoreQuery(query.predicate);\n }\n\n // Handle where-based queries (server format)\n if (query.where) {\n const conditions: CoreQuery[] = [];\n\n for (const [attribute, condition] of Object.entries(query.where)) {\n if (typeof condition !== 'object' || condition === null) {\n // Simple equality: { status: 'active' }\n conditions.push({ type: 'eq', attribute, value: condition });\n } else {\n // Operator-based: { age: { $gte: 18 } }\n for (const [op, value] of Object.entries(condition)) {\n const coreOp = this.convertOperator(op);\n if (coreOp) {\n conditions.push({ type: coreOp, attribute, value } as CoreQuery);\n }\n }\n }\n }\n\n if (conditions.length === 0) return null;\n if (conditions.length === 1) return conditions[0];\n return { type: 'and', children: conditions };\n }\n\n return null;\n }\n\n /**\n * Convert predicate node to core Query format.\n */\n private predicateToCoreQuery(predicate: any): CoreQuery | null {\n if (!predicate || !predicate.op) return null;\n\n switch (predicate.op) {\n case 'eq':\n case 'neq':\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte':\n return {\n type: predicate.op,\n attribute: predicate.attribute,\n value: predicate.value,\n } as CoreQuery;\n\n case 'and':\n case 'or':\n if (predicate.children && Array.isArray(predicate.children)) {\n const children = predicate.children\n .map((c: any) => this.predicateToCoreQuery(c))\n .filter((c: any): c is CoreQuery => c !== null);\n if (children.length === 0) return null;\n if (children.length === 1) return children[0];\n return { type: predicate.op, children };\n }\n return null;\n\n case 'not':\n if (predicate.children && predicate.children[0]) {\n const child = this.predicateToCoreQuery(predicate.children[0]);\n if (child) {\n return { type: 'not', child } as CoreQuery;\n }\n }\n return null;\n\n default:\n return null;\n }\n }\n\n /**\n * Convert server operator to core query type.\n */\n private convertOperator(op: string): 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | null {\n const mapping: Record<string, 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte'> = {\n '$eq': 'eq',\n '$ne': 'neq',\n '$neq': 'neq',\n '$gt': 'gt',\n '$gte': 'gte',\n '$lt': 'lt',\n '$lte': 'lte',\n };\n return mapping[op] || null;\n }\n\n private finalizeClusterQuery(requestId: string, timeout = false) {\n const pending = this.pendingClusterQueries.get(requestId);\n if (!pending) return;\n\n if (timeout) {\n logger.warn({ requestId, responded: pending.respondedNodes.size, expected: pending.expectedNodes.size }, 'Query timed out. Returning partial results.');\n }\n\n clearTimeout(pending.timer);\n this.pendingClusterQueries.delete(requestId);\n\n const { client, queryId, mapName, query, results } = pending;\n\n // Deduplicate results (if backups responded or multiple nodes have same key)\n const uniqueResults = new Map<string, any>();\n for (const res of results) {\n uniqueResults.set(res.key, res);\n }\n const finalResults = Array.from(uniqueResults.values());\n\n // Re-Apply Sort (Global)\n if (query.sort) {\n finalResults.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n // Handle ORMap array values vs LWW single values?\n // Assuming LWW for sort logic or array comparison.\n const valA = a.value[field];\n const valB = b.value[field];\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n const slicedResults = (query.offset || query.limit)\n ? finalResults.slice(query.offset || 0, (query.offset || 0) + (query.limit || finalResults.length))\n : finalResults;\n\n // Register Subscription\n const resultKeys = new Set(slicedResults.map(r => r.key));\n const sub: Subscription = {\n id: queryId,\n clientId: client.id,\n mapName,\n query,\n socket: client.socket,\n previousResultKeys: resultKeys,\n interestedFields: 'ALL'\n };\n\n this.queryRegistry.register(sub);\n client.subscriptions.add(queryId);\n\n // Apply Field Level Security\n const filteredResults = slicedResults.map(res => {\n const filteredValue = this.securityManager.filterObject(res.value, client.principal!, mapName);\n return { ...res, value: filteredValue };\n });\n\n client.writer.write({\n type: 'QUERY_RESP',\n payload: { queryId, results: filteredResults }\n });\n }\n\n /**\n * Core operation application logic shared between processLocalOp and processLocalOpForBatch.\n * Handles map merge, storage persistence, query evaluation, and event generation.\n *\n * @returns Event payload for broadcasting (or null if operation failed)\n */\n private async applyOpToMap(op: any, remoteNodeId?: string): Promise<{ eventPayload: any; oldRecord: any; rejected?: boolean }> {\n // Determine type hint from op\n const typeHint = (op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE') ? 'OR' : 'LWW';\n const map = this.getMap(op.mapName, typeHint);\n\n // Check compatibility\n if (typeHint === 'OR' && map instanceof LWWMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: LWWMap but received OR op');\n throw new Error('Map type mismatch: LWWMap but received OR op');\n }\n if (typeHint === 'LWW' && map instanceof ORMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: ORMap but received LWW op');\n throw new Error('Map type mismatch: ORMap but received LWW op');\n }\n\n let oldRecord: any;\n let recordToStore: StorageValue<any> | undefined;\n let tombstonesToStore: StorageValue<any> | undefined;\n\n const eventPayload: any = {\n mapName: op.mapName,\n key: op.key,\n };\n\n if (map instanceof LWWMap) {\n oldRecord = map.getRecord(op.key);\n\n // Use conflict resolver if registered (Phase 5.05)\n if (this.conflictResolverHandler.hasResolvers(op.mapName)) {\n const mergeResult = await this.conflictResolverHandler.mergeWithResolver(\n map,\n op.mapName,\n op.key,\n op.record,\n remoteNodeId || this._nodeId,\n );\n\n if (!mergeResult.applied) {\n // Operation was rejected or local value kept\n if (mergeResult.rejection) {\n logger.debug(\n { mapName: op.mapName, key: op.key, reason: mergeResult.rejection.reason },\n 'Merge rejected by resolver'\n );\n }\n return { eventPayload: null, oldRecord, rejected: true };\n }\n\n // Use the resolved record\n recordToStore = mergeResult.record;\n eventPayload.eventType = 'UPDATED';\n eventPayload.record = mergeResult.record;\n } else {\n // Standard merge without resolver\n map.merge(op.key, op.record);\n recordToStore = op.record;\n eventPayload.eventType = 'UPDATED';\n eventPayload.record = op.record;\n }\n } else if (map instanceof ORMap) {\n oldRecord = map.getRecords(op.key);\n\n if (op.opType === 'OR_ADD') {\n map.apply(op.key, op.orRecord);\n eventPayload.eventType = 'OR_ADD';\n eventPayload.orRecord = op.orRecord;\n recordToStore = { type: 'OR', records: map.getRecords(op.key) };\n } else if (op.opType === 'OR_REMOVE') {\n map.applyTombstone(op.orTag);\n eventPayload.eventType = 'OR_REMOVE';\n eventPayload.orTag = op.orTag;\n recordToStore = { type: 'OR', records: map.getRecords(op.key) };\n tombstonesToStore = { type: 'OR_TOMBSTONES', tags: map.getTombstones() };\n }\n }\n\n // Live Query Evaluation\n this.queryRegistry.processChange(op.mapName, map, op.key, op.record || op.orRecord, oldRecord);\n\n // Update metrics\n const mapSize = (map instanceof ORMap) ? map.totalRecords : map.size;\n this.metricsService.setMapSize(op.mapName, mapSize);\n\n // Persist to storage (async, don't wait)\n if (this.storage) {\n if (recordToStore) {\n this.storage.store(op.mapName, op.key, recordToStore).catch(err => {\n logger.error({ mapName: op.mapName, key: op.key, err }, 'Failed to persist op');\n });\n }\n if (tombstonesToStore) {\n this.storage.store(op.mapName, '__tombstones__', tombstonesToStore).catch(err => {\n logger.error({ mapName: op.mapName, err }, 'Failed to persist tombstones');\n });\n }\n }\n\n // Append to Event Journal (Phase 5.04)\n if (this.eventJournalService) {\n const isDelete = op.opType === 'REMOVE' || op.opType === 'OR_REMOVE' ||\n (op.record && op.record.value === null);\n const isNew = !oldRecord || (Array.isArray(oldRecord) && oldRecord.length === 0);\n const journalEventType: JournalEventType = isDelete ? 'DELETE' : (isNew ? 'PUT' : 'UPDATE');\n\n const timestamp = op.record?.timestamp || op.orRecord?.timestamp || this.hlc.now();\n\n this.eventJournalService.append({\n type: journalEventType,\n mapName: op.mapName,\n key: op.key,\n value: op.record?.value ?? op.orRecord?.value,\n previousValue: oldRecord?.value ?? (Array.isArray(oldRecord) ? oldRecord[0]?.value : undefined),\n timestamp,\n nodeId: this._nodeId,\n });\n }\n\n return { eventPayload, oldRecord };\n }\n\n /**\n * Broadcast event to cluster members (excluding self).\n */\n private broadcastToCluster(eventPayload: any): void {\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n /**\n * Apply replicated operation from another node (callback for ReplicationPipeline)\n * This is called when we receive a replicated operation as a backup node\n */\n private async applyReplicatedOperation(\n operation: unknown,\n opId: string,\n sourceNode: string\n ): Promise<boolean> {\n try {\n const op = operation as any;\n logger.debug({ sourceNode, opId, mapName: op.mapName, key: op.key }, 'Applying replicated operation');\n\n // Apply operation to local map (as backup)\n const { eventPayload, rejected } = await this.applyOpToMap(op, sourceNode);\n\n // Skip broadcast if operation was rejected by resolver\n if (rejected || !eventPayload) {\n return true; // Still return true - rejection is not an error\n }\n\n // Broadcast event to local clients subscribed to this data\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n return true;\n } catch (error) {\n logger.error({ sourceNode, opId, error }, 'Failed to apply replicated operation');\n return false;\n }\n }\n\n /**\n * Build OpContext for interceptors.\n */\n private buildOpContext(clientId: string, fromCluster: boolean): OpContext {\n let context: OpContext = {\n clientId,\n isAuthenticated: false,\n fromCluster,\n originalSenderId: clientId\n };\n\n if (!fromCluster) {\n const client = this.clients.get(clientId);\n if (client) {\n context = {\n clientId: client.id,\n socket: client.socket,\n isAuthenticated: client.isAuthenticated,\n principal: client.principal,\n fromCluster,\n originalSenderId: clientId\n };\n }\n }\n\n return context;\n }\n\n /**\n * Run onBeforeOp interceptors. Returns modified op or null if dropped.\n */\n private async runBeforeInterceptors(op: any, context: OpContext): Promise<any | null> {\n let currentOp: ServerOp | null = op;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.onBeforeOp && currentOp) {\n currentOp = await interceptor.onBeforeOp(currentOp, context);\n if (!currentOp) {\n logger.debug({ interceptor: interceptor.name, opId: op.id }, 'Interceptor silently dropped op');\n return null;\n }\n }\n }\n\n return currentOp;\n }\n\n /**\n * Run onAfterOp interceptors (fire-and-forget).\n */\n private runAfterInterceptors(op: any, context: OpContext): void {\n for (const interceptor of this.interceptors) {\n if (interceptor.onAfterOp) {\n interceptor.onAfterOp(op, context).catch(err => {\n logger.error({ err }, 'Error in onAfterOp');\n });\n }\n }\n }\n\n private handleLockGranted({ clientId, requestId, name, fencingToken }: { clientId: string, requestId: string, name: string, fencingToken: number }) {\n // Check if local client\n const client = this.clients.get(clientId);\n if (client) {\n client.writer.write({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n });\n return;\n }\n\n // Check if remote client (composite ID: \"nodeId:realClientId\")\n const parts = clientId.split(':');\n if (parts.length === 2) {\n const [nodeId, realClientId] = parts;\n // Verify nodeId is not self (loopback check, though split should handle it)\n if (nodeId !== this.cluster.config.nodeId) {\n this.cluster.send(nodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId: realClientId,\n requestId,\n name,\n fencingToken\n });\n return;\n }\n }\n\n logger.warn({ clientId, name }, 'Lock granted to unknown client');\n }\n\n private async processLocalOp(op: any, fromCluster: boolean, originalSenderId?: string) {\n // 1. Build context for interceptors\n const context = this.buildOpContext(originalSenderId || 'unknown', fromCluster);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) return; // Silently dropped by interceptor\n op = processedOp;\n } catch (err) {\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op');\n throw err;\n }\n\n // 3. Apply operation to map (shared logic)\n const { eventPayload, rejected } = await this.applyOpToMap(op, originalSenderId);\n\n // Skip further processing if operation was rejected by conflict resolver\n if (rejected || !eventPayload) {\n return;\n }\n\n // 4. Replicate to backup nodes (Hazelcast pattern: after local merge)\n // Note: Only replicate if we are the owner and operation is not from cluster\n // Incoming cluster ops are already replicated by the owner\n if (this.replicationPipeline && !fromCluster) {\n const opId = op.id || `${op.mapName}:${op.key}:${Date.now()}`;\n // Fire-and-forget for EVENTUAL, or await for STRONG/QUORUM\n this.replicationPipeline.replicate(op, opId, op.key).catch(err => {\n logger.warn({ opId, key: op.key, err }, 'Replication failed (non-fatal)');\n });\n }\n\n // 5. Broadcast EVENT to other clients\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 6. Broadcast to cluster (for subscribers on other nodes)\n // Note: This is different from replication - this notifies subscribers\n this.broadcastToCluster(eventPayload);\n\n // 7. Run onAfterOp interceptors\n this.runAfterInterceptors(op, context);\n }\n\n /**\n * === OPTIMIZATION 1: Async Batch Processing with Backpressure ===\n * Processes validated operations asynchronously after ACK has been sent.\n * Uses BackpressureRegulator to periodically force sync processing and\n * prevent unbounded accumulation of async work.\n */\n private async processBatchAsync(ops: any[], clientId: string): Promise<void> {\n // === BACKPRESSURE: Check if we should force sync processing ===\n if (this.backpressure.shouldForceSync()) {\n this.metricsService.incBackpressureSyncForced();\n await this.processBatchSync(ops, clientId);\n return;\n }\n\n // === BACKPRESSURE: Check and wait for capacity ===\n if (!this.backpressure.registerPending()) {\n this.metricsService.incBackpressureWaits();\n try {\n await this.backpressure.waitForCapacity();\n this.backpressure.registerPending();\n } catch (err) {\n this.metricsService.incBackpressureTimeouts();\n logger.warn({ clientId, pendingOps: ops.length }, 'Backpressure timeout - rejecting batch');\n throw new Error('Server overloaded');\n }\n }\n\n // Update pending ops metric\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n\n try {\n // === OPTIMIZATION 3: Batch Broadcast ===\n // Collect all events for a single batched broadcast at the end\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n // Process without immediate broadcast (we'll batch them)\n await this.processLocalOpForBatch(op, clientId, batchedEvents);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in async batch');\n }\n } else {\n // Forward to owner\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n }\n }\n\n // Send batched broadcast if we have events\n if (batchedEvents.length > 0) {\n this.broadcastBatch(batchedEvents, clientId);\n }\n } finally {\n this.backpressure.completePending();\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n }\n }\n\n /**\n * === BACKPRESSURE: Synchronous Batch Processing ===\n * Processes operations synchronously, waiting for broadcast completion.\n * Used when backpressure forces sync to drain the pipeline.\n */\n private async processBatchSync(ops: any[], clientId: string): Promise<void> {\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOpForBatch(op, clientId, batchedEvents);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in sync batch');\n }\n } else {\n // Forward to owner and wait for acknowledgment\n const owner = this.partitionService.getOwner(op.key);\n await this.forwardOpAndWait(op, owner);\n }\n }\n\n // Send batched broadcast SYNCHRONOUSLY - wait for all sends to complete\n if (batchedEvents.length > 0) {\n await this.broadcastBatchSync(batchedEvents, clientId);\n }\n }\n\n /**\n * Forward operation to owner node and wait for completion.\n * Used in sync processing mode.\n */\n private async forwardOpAndWait(op: any, owner: string): Promise<void> {\n return new Promise<void>((resolve) => {\n // Fire and forget for now - cluster forwarding doesn't have ack mechanism\n // In a full implementation, this would wait for cluster ACK\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n // Resolve immediately since cluster doesn't support sync ACK yet\n resolve();\n });\n }\n\n /**\n * Process a single operation for batch processing.\n * Uses shared applyOpToMap but collects events instead of broadcasting immediately.\n */\n private async processLocalOpForBatch(op: any, clientId: string, batchedEvents: any[]): Promise<void> {\n // 1. Build context for interceptors\n const context = this.buildOpContext(clientId, false);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) return; // Silently dropped by interceptor\n op = processedOp;\n } catch (err) {\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op in batch');\n throw err;\n }\n\n // 3. Apply operation to map (shared logic)\n const { eventPayload, rejected } = await this.applyOpToMap(op, clientId);\n\n // Skip further processing if operation was rejected by conflict resolver\n if (rejected || !eventPayload) {\n return;\n }\n\n // 4. Replicate to backup nodes (Hazelcast pattern: after local merge)\n if (this.replicationPipeline) {\n const opId = op.id || `${op.mapName}:${op.key}:${Date.now()}`;\n // Fire-and-forget for batch operations (EVENTUAL by default)\n this.replicationPipeline.replicate(op, opId, op.key).catch(err => {\n logger.warn({ opId, key: op.key, err }, 'Batch replication failed (non-fatal)');\n });\n }\n\n // 5. Collect event for batched broadcast (instead of immediate broadcast)\n batchedEvents.push(eventPayload);\n\n // 6. Broadcast to cluster (for subscribers)\n this.broadcastToCluster(eventPayload);\n\n // 7. Run onAfterOp interceptors\n this.runAfterInterceptors(op, context);\n }\n\n private handleClusterEvent(payload: any) {\n // 1. Replication Logic: Am I a Backup?\n const { mapName, key, eventType } = payload;\n const map = this.getMap(mapName, (eventType === 'OR_ADD' || eventType === 'OR_REMOVE') ? 'OR' : 'LWW');\n const oldRecord = (map instanceof LWWMap) ? map.getRecord(key) : null;\n\n // Only store if we are Owner (shouldn't receive event unless forwarded) or Backup\n if (this.partitionService.isRelated(key)) {\n if (map instanceof LWWMap && payload.record) {\n map.merge(key, payload.record);\n } else if (map instanceof ORMap) {\n if (eventType === 'OR_ADD' && payload.orRecord) {\n map.apply(key, payload.orRecord);\n } else if (eventType === 'OR_REMOVE' && payload.orTag) {\n map.applyTombstone(payload.orTag);\n }\n }\n }\n\n // 2. Notify Query Subscriptions\n this.queryRegistry.processChange(mapName, map, key, payload.record || payload.orRecord, oldRecord);\n\n // 3. Broadcast to local clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: payload,\n timestamp: this.hlc.now()\n });\n }\n\n public getMap(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): LWWMap<string, any> | ORMap<string, any> {\n if (!this.maps.has(name)) {\n let map: LWWMap<string, any> | ORMap<string, any>;\n\n if (typeHint === 'OR') {\n map = new ORMap(this.hlc);\n } else {\n map = new LWWMap(this.hlc);\n }\n\n this.maps.set(name, map);\n\n // Lazy load from storage - track the promise for getMapAsync\n if (this.storage) {\n logger.info({ mapName: name }, 'Loading map from storage...');\n const loadPromise = this.loadMapFromStorage(name, typeHint);\n this.mapLoadingPromises.set(name, loadPromise);\n loadPromise.finally(() => {\n this.mapLoadingPromises.delete(name);\n });\n }\n }\n return this.maps.get(name)!;\n }\n\n /**\n * Returns map after ensuring it's fully loaded from storage.\n * Use this for queries to avoid returning empty results during initial load.\n */\n public async getMapAsync(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): Promise<LWWMap<string, any> | ORMap<string, any>> {\n const mapExisted = this.maps.has(name);\n\n // First ensure map exists (this triggers loading if needed)\n this.getMap(name, typeHint);\n\n // Wait for loading to complete if in progress\n const loadingPromise = this.mapLoadingPromises.get(name);\n\n // [DEBUG] Log state for troubleshooting sync issues\n const map = this.maps.get(name);\n const mapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({\n mapName: name,\n mapExisted,\n hasLoadingPromise: !!loadingPromise,\n currentMapSize: mapSize\n }, '[getMapAsync] State check');\n\n if (loadingPromise) {\n logger.info({ mapName: name }, '[getMapAsync] Waiting for loadMapFromStorage...');\n await loadingPromise;\n const newMapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({ mapName: name, mapSizeAfterLoad: newMapSize }, '[getMapAsync] Load completed');\n }\n\n return this.maps.get(name)!;\n }\n\n private async loadMapFromStorage(name: string, typeHint: 'LWW' | 'OR'): Promise<void> {\n try {\n const keys = await this.storage!.loadAllKeys(name);\n if (keys.length === 0) return;\n\n // Check for ORMap markers in keys\n const hasTombstones = keys.includes('__tombstones__');\n\n const relatedKeys = keys.filter(k => this.partitionService.isRelated(k));\n if (relatedKeys.length === 0) return;\n\n const records = await this.storage!.loadAll(name, relatedKeys);\n let count = 0;\n\n // Check for Type Mismatch and Replace Map if needed\n let isOR = hasTombstones;\n if (!isOR) {\n // Check first record\n for (const [k, v] of records) {\n if (k !== '__tombstones__' && (v as any).type === 'OR') {\n isOR = true;\n break;\n }\n }\n }\n\n // If we created LWW but it's OR, replace it.\n // If we created OR but it's LWW, replace it? (Less likely if hint was OR, but possible if hint was wrong?)\n const currentMap = this.maps.get(name);\n if (!currentMap) return;\n let targetMap = currentMap;\n\n if (isOR && currentMap instanceof LWWMap) {\n logger.info({ mapName: name }, 'Map auto-detected as ORMap. Switching type.');\n targetMap = new ORMap(this.hlc);\n this.maps.set(name, targetMap);\n } else if (!isOR && currentMap instanceof ORMap && typeHint !== 'OR') {\n // Only switch back to LWW if hint wasn't explicit OR\n logger.info({ mapName: name }, 'Map auto-detected as LWWMap. Switching type.');\n targetMap = new LWWMap(this.hlc);\n this.maps.set(name, targetMap);\n }\n\n if (targetMap instanceof ORMap) {\n for (const [key, record] of records) {\n if (key === '__tombstones__') {\n const t = record as ORMapTombstones;\n if (t && t.tags) t.tags.forEach(tag => targetMap.applyTombstone(tag));\n } else {\n const orVal = record as ORMapValue<any>;\n if (orVal && orVal.records) {\n orVal.records.forEach(r => targetMap.apply(key, r));\n count++;\n }\n }\n }\n } else if (targetMap instanceof LWWMap) {\n for (const [key, record] of records) {\n // Expect LWWRecord\n // If record is actually ORMapValue (mismatch), we skip or error?\n // If !isOR, we assume LWWRecord.\n if (!(record as any).type) { // LWWRecord doesn't have type property in my impl\n targetMap.merge(key, record as LWWRecord<any>);\n count++;\n }\n }\n }\n\n if (count > 0) {\n logger.info({ mapName: name, count }, 'Loaded records for map');\n this.queryRegistry.refreshSubscriptions(name, targetMap);\n const mapSize = (targetMap instanceof ORMap) ? targetMap.totalRecords : targetMap.size;\n this.metricsService.setMapSize(name, mapSize);\n }\n } catch (err) {\n logger.error({ mapName: name, err }, 'Failed to load map');\n }\n }\n\n private startGarbageCollection() {\n this.gcInterval = setInterval(() => {\n this.reportLocalHlc();\n }, GC_INTERVAL_MS);\n }\n\n // ============ Heartbeat Methods ============\n\n /**\n * Starts the periodic check for dead clients (those that haven't sent PING).\n */\n private startHeartbeatCheck() {\n this.heartbeatCheckInterval = setInterval(() => {\n this.evictDeadClients();\n }, CLIENT_HEARTBEAT_CHECK_INTERVAL_MS);\n }\n\n /**\n * Handles incoming PING message from client.\n * Responds with PONG immediately.\n */\n private handlePing(client: ClientConnection, clientTimestamp: number): void {\n client.lastPingReceived = Date.now();\n\n const pongMessage = {\n type: 'PONG',\n timestamp: clientTimestamp,\n serverTime: Date.now(),\n };\n\n // PONG is urgent - bypass batching for accurate RTT measurement\n client.writer.write(pongMessage, true);\n }\n\n /**\n * Checks if a client is still alive based on heartbeat.\n */\n public isClientAlive(clientId: string): boolean {\n const client = this.clients.get(clientId);\n if (!client) return false;\n\n const idleTime = Date.now() - client.lastPingReceived;\n return idleTime < CLIENT_HEARTBEAT_TIMEOUT_MS;\n }\n\n /**\n * Returns how long the client has been idle (no PING received).\n */\n public getClientIdleTime(clientId: string): number {\n const client = this.clients.get(clientId);\n if (!client) return Infinity;\n\n return Date.now() - client.lastPingReceived;\n }\n\n /**\n * Evicts clients that haven't sent a PING within the timeout period.\n */\n private evictDeadClients(): void {\n const now = Date.now();\n const deadClients: string[] = [];\n\n for (const [clientId, client] of this.clients) {\n // Only check authenticated clients (unauthenticated ones will timeout via auth mechanism)\n if (client.isAuthenticated) {\n const idleTime = now - client.lastPingReceived;\n if (idleTime > CLIENT_HEARTBEAT_TIMEOUT_MS) {\n deadClients.push(clientId);\n }\n }\n }\n\n for (const clientId of deadClients) {\n const client = this.clients.get(clientId);\n if (client) {\n logger.warn({\n clientId,\n idleTime: now - client.lastPingReceived,\n timeoutMs: CLIENT_HEARTBEAT_TIMEOUT_MS,\n }, 'Evicting dead client (heartbeat timeout)');\n\n // Close the connection\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.close(4002, 'Heartbeat timeout');\n }\n }\n }\n }\n\n private reportLocalHlc() {\n // 1. Calculate Local Min HLC\n let minHlc = this.hlc.now();\n\n for (const client of this.clients.values()) {\n if (HLC.compare(client.lastActiveHlc, minHlc) < 0) {\n minHlc = client.lastActiveHlc;\n }\n }\n\n const members = this.cluster.getMembers().sort();\n const leaderId = members[0];\n const myId = this.cluster.config.nodeId;\n\n if (leaderId === myId) {\n // I am Leader\n this.handleGcReport(myId, minHlc);\n } else {\n // Send to Leader\n this.cluster.send(leaderId, 'CLUSTER_GC_REPORT', { minHlc });\n }\n }\n\n private handleGcReport(nodeId: string, minHlc: Timestamp) {\n this.gcReports.set(nodeId, minHlc);\n\n const members = this.cluster.getMembers();\n\n // Check if we have reports from ALL members\n // (Including self, which is inserted directly)\n const allReported = members.every(m => this.gcReports.has(m));\n\n if (allReported) {\n // Calculate Global Safe Timestamp\n let globalSafe = this.hlc.now(); // Start high\n let initialized = false;\n\n for (const ts of this.gcReports.values()) {\n if (!initialized || HLC.compare(ts, globalSafe) < 0) {\n globalSafe = ts;\n initialized = true;\n }\n }\n\n // Add safety buffer (e.g. GC_AGE)\n // prune(timestamp) removes items OLDER than timestamp.\n // We want to remove items OLDER than (GlobalMin - GC_AGE).\n\n const olderThanMillis = globalSafe.millis - GC_AGE_MS;\n const safeTimestamp: Timestamp = {\n millis: olderThanMillis,\n counter: 0,\n nodeId: globalSafe.nodeId // Doesn't matter much for comparison if millis match, but best effort\n };\n\n logger.info({\n globalMinHlc: globalSafe.millis,\n safeGcTimestamp: olderThanMillis,\n reportsCount: this.gcReports.size\n }, 'GC Consensus Reached. Broadcasting Commit.');\n\n // Broadcast Commit\n const commitMsg = {\n type: 'CLUSTER_GC_COMMIT', // Handled by cluster listener\n payload: { safeTimestamp }\n };\n\n // Send to others\n for (const member of members) {\n if (!this.cluster.isLocal(member)) {\n this.cluster.send(member, 'CLUSTER_GC_COMMIT', { safeTimestamp });\n }\n }\n\n // Execute Locally\n this.performGarbageCollection(safeTimestamp);\n\n // Clear reports for next round?\n // Or keep them and overwrite?\n // Overwriting is better for partial updates, but clearing ensures freshness.\n // Since we run interval based, clearing is safer to ensure active participation next time.\n this.gcReports.clear();\n }\n }\n\n private performGarbageCollection(olderThan: Timestamp) {\n logger.info({ olderThanMillis: olderThan.millis }, 'Performing Garbage Collection');\n const now = Date.now();\n\n for (const [name, map] of this.maps) {\n // 1. Check for active expired records (TTL)\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const record = map.getRecord(key);\n if (record && record.value !== null && record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n logger.info({ mapName: name, key }, 'Record expired (TTL). Converting to tombstone.');\n\n // Create Tombstone at expiration time to handle \"Resurrection\" correctly\n const tombstoneTimestamp: Timestamp = {\n millis: expirationTime,\n counter: 0, // Reset counter for expiration time\n nodeId: this.hlc.getNodeId // Use our ID\n };\n\n const tombstone: LWWRecord<any> = { value: null, timestamp: tombstoneTimestamp };\n\n // Apply locally\n const changed = map.merge(key, tombstone);\n\n if (changed) {\n // Persist and Broadcast\n // Construct an artificial op to reuse pipeline logic or do manual steps\n // Manual steps are safer here as we don't have a client op context\n\n if (this.storage) {\n this.storage.store(name, key, tombstone).catch(err =>\n logger.error({ mapName: name, key, err }, 'Failed to persist expired tombstone')\n );\n }\n\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'UPDATED',\n record: tombstone\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedKeys = map.prune(olderThan);\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned records from LWW map');\n if (this.storage) {\n this.storage.deleteAll(name, removedKeys).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to delete pruned keys from storage');\n });\n }\n }\n } else if (map instanceof ORMap) {\n // ORMap Expiration\n // We need to check all active records in the ORMap\n const items = (map as any).items as Map<string, Map<string, ORMapRecord<any>>>;\n const tombstonesSet = (map as any).tombstones as Set<string>;\n\n const tagsToExpire: { key: string; tag: string }[] = [];\n\n for (const [key, keyMap] of items) {\n for (const [tag, record] of keyMap) {\n if (!tombstonesSet.has(tag)) {\n if (record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n tagsToExpire.push({ key, tag });\n }\n }\n }\n }\n }\n\n for (const { key, tag } of tagsToExpire) {\n logger.info({ mapName: name, key, tag }, 'ORMap Record expired (TTL). Removing.');\n\n // Remove by adding tag to tombstones\n map.applyTombstone(tag);\n\n // Persist change\n if (this.storage) {\n // We need to update the key's record list and tombstones\n // Optimally, we should batch these updates\n const records = map.getRecords(key);\n if (records.length > 0) {\n this.storage.store(name, key, { type: 'OR', records });\n } else {\n this.storage.delete(name, key);\n }\n\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n });\n }\n\n // Broadcast\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'OR_REMOVE',\n orTag: tag\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from OR map');\n // We need to update __tombstones__ in storage\n if (this.storage) {\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n }).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to update tombstones');\n });\n }\n }\n }\n }\n\n // Broadcast to clients\n this.broadcast({\n type: 'GC_PRUNE',\n payload: {\n olderThan\n }\n });\n }\n private buildTLSOptions(config: TLSConfig): HttpsServerOptions {\n const options: HttpsServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.ciphers) {\n options.ciphers = config.ciphers;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n\n // ============ Write Concern Methods (Phase 5.01) ============\n\n /**\n * Get effective Write Concern level for an operation.\n * Per-op writeConcern overrides batch-level.\n */\n private getEffectiveWriteConcern(\n opWriteConcern: WriteConcernValue | undefined,\n batchWriteConcern: WriteConcernValue | undefined\n ): WriteConcernValue | undefined {\n return opWriteConcern ?? batchWriteConcern;\n }\n\n /**\n * Convert string WriteConcern value to enum.\n */\n private stringToWriteConcern(value: WriteConcernValue | undefined): WriteConcern {\n switch (value) {\n case 'FIRE_AND_FORGET':\n return WriteConcern.FIRE_AND_FORGET;\n case 'MEMORY':\n return WriteConcern.MEMORY;\n case 'APPLIED':\n return WriteConcern.APPLIED;\n case 'REPLICATED':\n return WriteConcern.REPLICATED;\n case 'PERSISTED':\n return WriteConcern.PERSISTED;\n default:\n return WriteConcern.MEMORY;\n }\n }\n\n /**\n * Process batch with Write Concern tracking.\n * Notifies WriteAckManager at each stage of processing.\n */\n private async processBatchAsyncWithWriteConcern(\n ops: any[],\n clientId: string,\n batchWriteConcern?: WriteConcernValue,\n batchTimeout?: number\n ): Promise<void> {\n // === BACKPRESSURE: Check if we should force sync processing ===\n if (this.backpressure.shouldForceSync()) {\n this.metricsService.incBackpressureSyncForced();\n await this.processBatchSyncWithWriteConcern(ops, clientId, batchWriteConcern, batchTimeout);\n return;\n }\n\n // === BACKPRESSURE: Check and wait for capacity ===\n if (!this.backpressure.registerPending()) {\n this.metricsService.incBackpressureWaits();\n try {\n await this.backpressure.waitForCapacity();\n this.backpressure.registerPending();\n } catch (err) {\n this.metricsService.incBackpressureTimeouts();\n logger.warn({ clientId, pendingOps: ops.length }, 'Backpressure timeout - rejecting batch');\n // Fail all pending operations\n for (const op of ops) {\n if (op.id) {\n this.writeAckManager.failPending(op.id, 'Server overloaded');\n }\n }\n throw new Error('Server overloaded');\n }\n }\n\n // Update pending ops metric\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n\n try {\n // === OPTIMIZATION 3: Batch Broadcast ===\n // Collect all events for a single batched broadcast at the end\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n // Process operation with Write Concern tracking\n await this.processLocalOpWithWriteConcern(op, clientId, batchedEvents, batchWriteConcern);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in async batch');\n // Fail the pending write\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n }\n } else {\n // Forward to owner\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType,\n writeConcern: op.writeConcern ?? batchWriteConcern,\n }\n });\n // For forwarded ops, we mark REPLICATED immediately since it's sent to cluster\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n\n // Send batched broadcast if we have events\n if (batchedEvents.length > 0) {\n this.broadcastBatch(batchedEvents, clientId);\n // Notify REPLICATED for all ops that were broadcast\n for (const op of ops) {\n if (op.id && this.partitionService.isLocalOwner(op.key)) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n } finally {\n this.backpressure.completePending();\n this.metricsService.setBackpressurePendingOps(this.backpressure.getPendingOps());\n }\n }\n\n /**\n * Synchronous batch processing with Write Concern.\n */\n private async processBatchSyncWithWriteConcern(\n ops: any[],\n clientId: string,\n batchWriteConcern?: WriteConcernValue,\n batchTimeout?: number\n ): Promise<void> {\n const batchedEvents: any[] = [];\n\n for (const op of ops) {\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOpWithWriteConcern(op, clientId, batchedEvents, batchWriteConcern);\n } catch (err) {\n logger.warn({ clientId, mapName: op.mapName, key: op.key, err }, 'Op failed in sync batch');\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n }\n } else {\n // Forward to owner and wait for acknowledgment\n const owner = this.partitionService.getOwner(op.key);\n await this.forwardOpAndWait(op, owner);\n // Mark REPLICATED after forwarding\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n\n // Send batched broadcast SYNCHRONOUSLY - wait for all sends to complete\n if (batchedEvents.length > 0) {\n await this.broadcastBatchSync(batchedEvents, clientId);\n // Notify REPLICATED for all local ops\n for (const op of ops) {\n if (op.id && this.partitionService.isLocalOwner(op.key)) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.REPLICATED);\n }\n }\n }\n }\n\n /**\n * Process a single operation with Write Concern level notifications.\n */\n private async processLocalOpWithWriteConcern(\n op: any,\n clientId: string,\n batchedEvents: any[],\n batchWriteConcern?: WriteConcernValue\n ): Promise<void> {\n // 1. Build context for interceptors\n const context = this.buildOpContext(clientId, false);\n\n // 2. Run onBeforeOp interceptors\n try {\n const processedOp = await this.runBeforeInterceptors(op, context);\n if (!processedOp) {\n // Silently dropped by interceptor - fail the pending write\n if (op.id) {\n this.writeAckManager.failPending(op.id, 'Dropped by interceptor');\n }\n return;\n }\n op = processedOp;\n } catch (err) {\n logger.warn({ opId: op.id, err }, 'Interceptor rejected op');\n if (op.id) {\n this.writeAckManager.failPending(op.id, String(err));\n }\n return;\n }\n\n // 3. Apply operation to map\n const { eventPayload, rejected } = await this.applyOpToMap(op, clientId);\n\n // If rejected by conflict resolver, fail the pending write\n if (rejected) {\n if (op.id) {\n this.writeAckManager.failPending(op.id, 'Rejected by conflict resolver');\n }\n return;\n }\n\n // 4. Notify APPLIED level (CRDT merged)\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.APPLIED);\n }\n\n // 5. Collect event for batched broadcast\n if (eventPayload) {\n batchedEvents.push({\n mapName: op.mapName,\n key: op.key,\n ...eventPayload\n });\n }\n\n // 6. Handle PERSISTED Write Concern\n const effectiveWriteConcern = this.getEffectiveWriteConcern(op.writeConcern, batchWriteConcern);\n if (effectiveWriteConcern === 'PERSISTED' && this.storage) {\n try {\n // Wait for storage write to complete\n await this.persistOpSync(op);\n if (op.id) {\n this.writeAckManager.notifyLevel(op.id, WriteConcern.PERSISTED);\n }\n } catch (err) {\n logger.error({ opId: op.id, err }, 'Persistence failed');\n if (op.id) {\n this.writeAckManager.failPending(op.id, `Persistence failed: ${err}`);\n }\n }\n } else if (this.storage && op.id) {\n // Fire-and-forget persistence for non-PERSISTED writes\n this.persistOpAsync(op).catch(err => {\n logger.error({ opId: op.id, err }, 'Async persistence failed');\n });\n }\n\n // 7. Run onAfterOp interceptors\n try {\n const serverOp: ServerOp = {\n mapName: op.mapName,\n key: op.key,\n opType: op.opType || (op.record?.value === null ? 'REMOVE' : 'PUT'),\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n };\n await this.runAfterInterceptors(serverOp, context);\n } catch (err) {\n logger.warn({ opId: op.id, err }, 'onAfterOp interceptor failed');\n }\n }\n\n /**\n * Persist operation synchronously (blocking).\n * Used for PERSISTED Write Concern.\n */\n private async persistOpSync(op: any): Promise<void> {\n if (!this.storage) return;\n\n const isORMapOp = op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE' || op.orRecord || op.orTag;\n\n if (isORMapOp) {\n const orMap = this.getMap(op.mapName, 'OR') as ORMap<string, any>;\n const records = orMap.getRecords(op.key);\n const tombstones = orMap.getTombstones();\n\n if (records.length > 0) {\n await this.storage.store(op.mapName, op.key, { type: 'OR', records } as ORMapValue<any>);\n } else {\n await this.storage.delete(op.mapName, op.key);\n }\n\n if (tombstones.length > 0) {\n await this.storage.store(op.mapName, '__tombstones__', { type: 'OR_TOMBSTONES', tags: tombstones } as ORMapTombstones);\n }\n } else {\n const lwwMap = this.getMap(op.mapName, 'LWW') as LWWMap<string, any>;\n const record = lwwMap.getRecord(op.key);\n if (record) {\n await this.storage.store(op.mapName, op.key, record);\n }\n }\n }\n\n /**\n * Persist operation asynchronously (fire-and-forget).\n * Used for non-PERSISTED Write Concern levels.\n */\n private async persistOpAsync(op: any): Promise<void> {\n return this.persistOpSync(op);\n }\n}\n","import { LWWRecord, PredicateNode, evaluatePredicate } from '@topgunbuild/core';\n\nexport interface Query {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Checks if a record matches a query.\n * Supports simple exact match for now.\n */\nexport function matchesQuery(record: LWWRecord<any>, query: Query): boolean {\n const data = record.value;\n if (!data) return false; \n\n // Check TTL\n if (record.ttlMs) {\n const now = Date.now();\n if (record.timestamp.millis + record.ttlMs < now) {\n return false; // Expired\n }\n }\n\n // 1. New Predicate API\n if (query.predicate) {\n return evaluatePredicate(query.predicate, data);\n }\n\n // 2. Legacy 'where' clause\n if (!query.where) return true; // Empty query matches everything\n\n for (const [field, expected] of Object.entries(query.where)) {\n const actual = data[field];\n\n // Operator matching (e.g. { age: { $gt: 18 } })\n if (typeof expected === 'object' && expected !== null && !Array.isArray(expected)) {\n for (const [op, opValueRaw] of Object.entries(expected)) {\n const opValue = opValueRaw as any; // Cast for comparison\n switch (op) {\n case '$gt':\n if (!(actual > opValue)) return false;\n break;\n case '$gte':\n if (!(actual >= opValue)) return false;\n break;\n case '$lt':\n if (!(actual < opValue)) return false;\n break;\n case '$lte':\n if (!(actual <= opValue)) return false;\n break;\n case '$ne':\n if (!(actual !== opValue)) return false;\n break;\n // Add more operators as needed ($in, etc.)\n default:\n // Unknown operator, treating as exact match (or should we fail?)\n // For now, ignore unknown operators or treat as mismatch?\n // Let's treat unknown operators as false to be safe.\n return false; \n }\n }\n } else {\n // Simple exact match\n if (actual !== expected) {\n return false;\n }\n }\n }\n \n return true;\n}\n\nexport function executeQuery(records: Map<string, LWWRecord<any>> | LWWRecord<any>[], query: Query): { key: string; value: any }[] {\n // Handle null/undefined query\n if (!query) {\n query = {};\n }\n\n let results: { key: string; record: LWWRecord<any> }[] = [];\n\n // 1. Filter\n if (records instanceof Map) {\n for (const [key, record] of records) {\n if (matchesQuery(record, query)) {\n results.push({ key, record });\n }\n }\n } else {\n // If array, we might not have keys easily unless they are in the record or we iterate\n // For now assume Map input primarily for ServerCoordinator\n // But if input is array of records?\n for (const record of records) {\n // Assuming key is not readily available if just array of records, \n // but usually we pass Map from ServerCoordinator.\n // If we really need key, we need it in the input.\n // Let's stick to Map input for now as that's what ServerCoordinator has.\n // But wait, the signature I defined allows array.\n if (matchesQuery(record, query)) {\n results.push({ key: '?', record }); \n }\n }\n }\n\n // 2. Sort\n if (query.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n const valA = a.record.value[field];\n const valB = b.record.value[field];\n \n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // 3. Limit & Offset\n if (query.offset || query.limit) {\n const offset = query.offset || 0;\n const limit = query.limit || results.length;\n results = results.slice(offset, offset + limit);\n }\n\n return results.map(r => ({ key: r.key, value: r.record.value }));\n}\n","import { Query, matchesQuery, executeQuery } from './Matcher';\nimport { LWWRecord, LWWMap, ORMap, serialize, PredicateNode, ORMapRecord, IndexedLWWMap, IndexedORMap, StandingQueryRegistry as CoreStandingQueryRegistry, type QueryExpression as CoreQuery, type StandingQueryChange } from '@topgunbuild/core';\nimport { WebSocket } from 'ws';\nimport { logger } from '../utils/logger';\n\nexport interface Subscription {\n id: string; // queryId\n clientId: string;\n mapName: string;\n query: Query;\n socket: WebSocket;\n previousResultKeys: Set<string>;\n interestedFields?: Set<string> | 'ALL';\n _cleanup?: () => void; // For Reverse Index cleanup\n}\n\nclass ReverseQueryIndex {\n // field -> value -> Set<Subscription>\n private equality = new Map<string, Map<any, Set<Subscription>>>();\n // field -> Set<Subscription>\n private interest = new Map<string, Set<Subscription>>();\n // catch-all\n private wildcard = new Set<Subscription>();\n\n public add(sub: Subscription) {\n const query = sub.query;\n let indexed = false;\n const cleanupFns: (() => void)[] = [];\n\n // 1. Where\n if (query.where) {\n for (const [field, value] of Object.entries(query.where)) {\n if (typeof value !== 'object') {\n // Exact match\n this.addEquality(field, value, sub);\n cleanupFns.push(() => this.removeEquality(field, value, sub));\n indexed = true;\n } else {\n // Operator - add to interest\n this.addInterest(field, sub);\n cleanupFns.push(() => this.removeInterest(field, sub));\n indexed = true;\n }\n }\n }\n \n // 2. Predicate\n if (query.predicate) {\n const visit = (node: PredicateNode) => {\n if (node.op === 'eq' && node.attribute && node.value !== undefined) {\n this.addEquality(node.attribute, node.value, sub);\n cleanupFns.push(() => this.removeEquality(node.attribute!, node.value, sub));\n indexed = true;\n } else if (node.attribute) {\n // Any other op on attribute\n this.addInterest(node.attribute, sub);\n cleanupFns.push(() => this.removeInterest(node.attribute!, sub));\n indexed = true;\n }\n \n if (node.children) {\n node.children.forEach(visit);\n }\n };\n visit(query.predicate);\n }\n \n // 3. Sort\n if (query.sort) {\n Object.keys(query.sort).forEach(k => {\n this.addInterest(k, sub);\n cleanupFns.push(() => this.removeInterest(k, sub));\n indexed = true;\n });\n }\n\n if (!indexed) {\n this.wildcard.add(sub);\n cleanupFns.push(() => this.wildcard.delete(sub));\n }\n \n sub._cleanup = () => cleanupFns.forEach(fn => fn());\n }\n\n public remove(sub: Subscription) {\n if (sub._cleanup) {\n sub._cleanup();\n sub._cleanup = undefined;\n }\n }\n\n public getCandidates(changedFields: Set<string> | 'ALL', oldVal: any, newVal: any): Set<Subscription> {\n const candidates = new Set<Subscription>(this.wildcard);\n\n if (changedFields === 'ALL') {\n // Return all possible candidates (inefficient but safe)\n // We collect from all indexes? Or just return all subs?\n // To match \"wildcard\" behavior, we should probably iterate all.\n // But we don't track all subs in index easily.\n // We can iterate this.interest and this.equality.\n for (const set of this.interest.values()) {\n for (const s of set) candidates.add(s);\n }\n for (const map of this.equality.values()) {\n for (const set of map.values()) {\n for (const s of set) candidates.add(s);\n }\n }\n return candidates;\n }\n\n // If no changes detected (shouldn't happen if called correctly), just return wildcard\n if (changedFields.size === 0) return candidates;\n\n for (const field of changedFields) {\n // 1. Interest (General)\n if (this.interest.has(field)) {\n for (const sub of this.interest.get(field)!) {\n candidates.add(sub);\n }\n }\n\n // 2. Equality\n if (this.equality.has(field)) {\n const valMap = this.equality.get(field)!;\n \n // Check New Value queries\n if (newVal && newVal[field] !== undefined && valMap.has(newVal[field])) {\n for (const sub of valMap.get(newVal[field])!) {\n candidates.add(sub);\n }\n }\n \n // Check Old Value queries\n if (oldVal && oldVal[field] !== undefined && valMap.has(oldVal[field])) {\n for (const sub of valMap.get(oldVal[field])!) {\n candidates.add(sub);\n }\n }\n }\n }\n \n return candidates;\n }\n\n private addEquality(field: string, value: any, sub: Subscription) {\n if (!this.equality.has(field)) this.equality.set(field, new Map());\n const valMap = this.equality.get(field)!;\n if (!valMap.has(value)) valMap.set(value, new Set());\n valMap.get(value)!.add(sub);\n }\n\n private removeEquality(field: string, value: any, sub: Subscription) {\n const valMap = this.equality.get(field);\n if (valMap) {\n const set = valMap.get(value);\n if (set) {\n set.delete(sub);\n if (set.size === 0) valMap.delete(value);\n }\n if (valMap.size === 0) this.equality.delete(field);\n }\n }\n\n private addInterest(field: string, sub: Subscription) {\n if (!this.interest.has(field)) this.interest.set(field, new Set());\n this.interest.get(field)!.add(sub);\n }\n\n private removeInterest(field: string, sub: Subscription) {\n const set = this.interest.get(field);\n if (set) {\n set.delete(sub);\n if (set.size === 0) this.interest.delete(field);\n }\n }\n}\n\nexport class QueryRegistry {\n // MapName -> Set of Subscriptions (Legacy/Backup)\n private subscriptions: Map<string, Set<Subscription>> = new Map();\n \n // MapName -> Reverse Index\n private indexes: Map<string, ReverseQueryIndex> = new Map();\n\n public register(sub: Subscription) {\n if (!this.subscriptions.has(sub.mapName)) {\n this.subscriptions.set(sub.mapName, new Set());\n this.indexes.set(sub.mapName, new ReverseQueryIndex());\n }\n \n const interestedFields = this.analyzeQueryFields(sub.query);\n sub.interestedFields = interestedFields;\n\n this.subscriptions.get(sub.mapName)!.add(sub);\n this.indexes.get(sub.mapName)!.add(sub);\n \n logger.info({ clientId: sub.clientId, mapName: sub.mapName, query: sub.query }, 'Client subscribed');\n }\n\n public unregister(queryId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.id === queryId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n return; \n }\n }\n }\n }\n\n public unsubscribeAll(clientId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.clientId === clientId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n }\n }\n }\n }\n\n /**\n * Returns all active subscriptions for a specific map.\n * Used for subscription-based event routing to avoid broadcasting to all clients.\n */\n public getSubscriptionsForMap(mapName: string): Subscription[] {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) {\n return [];\n }\n return Array.from(subs);\n }\n\n /**\n * Returns unique client IDs that have subscriptions for a specific map.\n * Useful for efficient routing when a client has multiple queries on the same map.\n */\n public getSubscribedClientIds(mapName: string): Set<string> {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) {\n return new Set();\n }\n const clientIds = new Set<string>();\n for (const sub of subs) {\n clientIds.add(sub.clientId);\n }\n return clientIds;\n }\n\n /**\n * Refreshes all subscriptions for a given map.\n * Useful when the map is bulk-loaded from storage.\n */\n public refreshSubscriptions(mapName: string, map: LWWMap<string, any> | ORMap<string, any>) {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) return;\n\n const allRecords = this.getMapRecords(map);\n\n for (const sub of subs) {\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n // Send update for all currently matching records\n // We assume value might have changed or it is new\n this.sendUpdate(sub, res.key, res.value, 'UPDATE');\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private getMapRecords(map: LWWMap<string, any> | ORMap<string, any>): Map<string, any> {\n const recordsMap = new Map<string, any>();\n\n // Use duck-typing to support mocks and proxies\n const mapAny = map as any;\n\n // LWWMap-like: has allKeys() and getRecord()\n if (typeof mapAny.allKeys === 'function' && typeof mapAny.getRecord === 'function') {\n for (const key of mapAny.allKeys()) {\n const rec = mapAny.getRecord(key);\n if (rec) {\n recordsMap.set(key, rec);\n }\n }\n }\n // ORMap-like: has items Map and get() returns array\n else if (mapAny.items instanceof Map && typeof mapAny.get === 'function') {\n const items = mapAny.items as Map<string, any>;\n for (const key of items.keys()) {\n const values = mapAny.get(key);\n if (values.length > 0) {\n recordsMap.set(key, { value: values });\n }\n }\n }\n return recordsMap;\n }\n\n /**\n * Processes a record change for all relevant subscriptions.\n * Calculates diffs and sends updates.\n *\n * For IndexedLWWMap: Uses StandingQueryRegistry for O(1) affected query detection.\n * For regular maps: Falls back to ReverseQueryIndex.\n */\n public processChange(\n mapName: string,\n map: LWWMap<string, any> | ORMap<string, any>,\n changeKey: string,\n changeRecord: any, // LWWRecord | ORMapRecord | ORMapRecord[]\n oldRecord?: any // LWWRecord | ORMapRecord[]\n ) {\n const index = this.indexes.get(mapName);\n if (!index) return;\n\n // Extract Values\n const newVal = this.extractValue(changeRecord);\n const oldVal = this.extractValue(oldRecord);\n\n // Use StandingQueryRegistry for IndexedLWWMap (O(1) query matching)\n if (map instanceof IndexedLWWMap) {\n this.processChangeWithStandingQuery(mapName, map, changeKey, newVal, oldVal);\n return;\n }\n\n // Fallback to ReverseQueryIndex for regular maps\n this.processChangeWithReverseIndex(mapName, map, changeKey, newVal, oldVal, index);\n }\n\n /**\n * Process change using IndexedLWWMap's StandingQueryRegistry.\n * O(1) detection of affected queries.\n */\n private processChangeWithStandingQuery(\n mapName: string,\n map: IndexedLWWMap<string, any>,\n changeKey: string,\n newVal: any,\n oldVal: any\n ) {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) return;\n\n // Build a map of queryId -> subscription for quick lookup\n const subsByQueryId = new Map<string, Subscription>();\n for (const sub of subs) {\n subsByQueryId.set(sub.id, sub);\n }\n\n // Get standing query registry from the map\n const standingRegistry = map.getStandingQueryRegistry();\n\n // Determine changes via StandingQueryRegistry\n let changes: Map<string, StandingQueryChange>;\n if (oldVal === null || oldVal === undefined) {\n // New record added\n if (newVal !== null && newVal !== undefined) {\n changes = standingRegistry.onRecordAdded(changeKey, newVal);\n } else {\n return; // No actual change\n }\n } else if (newVal === null || newVal === undefined) {\n // Record removed\n changes = standingRegistry.onRecordRemoved(changeKey, oldVal);\n } else {\n // Record updated\n changes = standingRegistry.onRecordUpdated(changeKey, oldVal, newVal);\n }\n\n // Process affected subscriptions\n for (const sub of subs) {\n // Check if this subscription's query was affected\n const coreQuery = this.convertToCoreQuery(sub.query);\n if (!coreQuery) {\n // Can't convert query, use fallback\n this.processSubscriptionFallback(sub, map, changeKey, newVal);\n continue;\n }\n\n const queryHash = this.hashCoreQuery(coreQuery);\n const change = changes.get(queryHash);\n\n if (change === 'added') {\n sub.previousResultKeys.add(changeKey);\n this.sendUpdate(sub, changeKey, newVal, 'UPDATE');\n } else if (change === 'removed') {\n sub.previousResultKeys.delete(changeKey);\n this.sendUpdate(sub, changeKey, null, 'REMOVE');\n } else if (change === 'updated') {\n this.sendUpdate(sub, changeKey, newVal, 'UPDATE');\n }\n // 'unchanged' - no action needed\n }\n }\n\n /**\n * Process change using legacy ReverseQueryIndex.\n */\n private processChangeWithReverseIndex(\n mapName: string,\n map: LWWMap<string, any> | ORMap<string, any>,\n changeKey: string,\n newVal: any,\n oldVal: any,\n index: ReverseQueryIndex\n ) {\n // 0. Calculate Changed Fields\n const changedFields = this.getChangedFields(oldVal, newVal);\n\n if (changedFields !== 'ALL' && changedFields.size === 0 && oldVal && newVal) {\n return;\n }\n\n const candidates = index.getCandidates(changedFields, oldVal, newVal);\n\n if (candidates.size === 0) return;\n\n // Helper to get all records as a Map for executeQuery\n let recordsMap: Map<string, any> | null = null;\n const getRecordsMap = () => {\n if (recordsMap) return recordsMap;\n recordsMap = this.getMapRecords(map);\n return recordsMap;\n };\n\n for (const sub of candidates) {\n const dummyRecord: LWWRecord<any> = {\n value: newVal,\n timestamp: { millis: 0, counter: 0, nodeId: '' } // Dummy timestamp for matchesQuery\n };\n const isMatch = matchesQuery(dummyRecord, sub.query); // Approximate match check\n const wasInResult = sub.previousResultKeys.has(changeKey);\n\n if (!isMatch && !wasInResult) {\n continue;\n }\n\n // Re-evaluate query\n const allRecords = getRecordsMap();\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // Determine changes\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n const key = res.key;\n const isNew = !sub.previousResultKeys.has(key);\n\n if (key === changeKey) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n } else if (isNew) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n }\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n /**\n * Fallback processing for subscriptions that can't use StandingQueryRegistry.\n */\n private processSubscriptionFallback(\n sub: Subscription,\n map: IndexedLWWMap<string, any>,\n changeKey: string,\n newVal: any\n ) {\n const dummyRecord: LWWRecord<any> = {\n value: newVal,\n timestamp: { millis: 0, counter: 0, nodeId: '' }\n };\n const isMatch = newVal !== null && matchesQuery(dummyRecord, sub.query);\n const wasInResult = sub.previousResultKeys.has(changeKey);\n\n if (isMatch && !wasInResult) {\n sub.previousResultKeys.add(changeKey);\n this.sendUpdate(sub, changeKey, newVal, 'UPDATE');\n } else if (!isMatch && wasInResult) {\n sub.previousResultKeys.delete(changeKey);\n this.sendUpdate(sub, changeKey, null, 'REMOVE');\n } else if (isMatch && wasInResult) {\n this.sendUpdate(sub, changeKey, newVal, 'UPDATE');\n }\n }\n\n /**\n * Convert server Query format to core Query format.\n */\n private convertToCoreQuery(query: Query): CoreQuery | null {\n if (query.predicate) {\n return this.predicateToCoreQuery(query.predicate);\n }\n\n if (query.where) {\n const conditions: CoreQuery[] = [];\n for (const [attribute, condition] of Object.entries(query.where)) {\n if (typeof condition !== 'object' || condition === null) {\n conditions.push({ type: 'eq', attribute, value: condition });\n } else {\n for (const [op, value] of Object.entries(condition)) {\n const coreOp = this.convertOperator(op);\n if (coreOp) {\n conditions.push({ type: coreOp, attribute, value } as CoreQuery);\n }\n }\n }\n }\n if (conditions.length === 0) return null;\n if (conditions.length === 1) return conditions[0];\n return { type: 'and', children: conditions };\n }\n\n return null;\n }\n\n private predicateToCoreQuery(predicate: any): CoreQuery | null {\n if (!predicate || !predicate.op) return null;\n\n switch (predicate.op) {\n case 'eq':\n case 'neq':\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte':\n return {\n type: predicate.op,\n attribute: predicate.attribute,\n value: predicate.value,\n } as CoreQuery;\n\n case 'and':\n case 'or':\n if (predicate.children && Array.isArray(predicate.children)) {\n const children = predicate.children\n .map((c: any) => this.predicateToCoreQuery(c))\n .filter((c: any): c is CoreQuery => c !== null);\n if (children.length === 0) return null;\n if (children.length === 1) return children[0];\n return { type: predicate.op, children };\n }\n return null;\n\n case 'not':\n if (predicate.children && predicate.children[0]) {\n const child = this.predicateToCoreQuery(predicate.children[0]);\n if (child) {\n return { type: 'not', child } as CoreQuery;\n }\n }\n return null;\n\n default:\n return null;\n }\n }\n\n private convertOperator(op: string): 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | null {\n const mapping: Record<string, 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte'> = {\n '$eq': 'eq',\n '$ne': 'neq',\n '$neq': 'neq',\n '$gt': 'gt',\n '$gte': 'gte',\n '$lt': 'lt',\n '$lte': 'lte',\n };\n return mapping[op] || null;\n }\n\n private hashCoreQuery(query: CoreQuery): string {\n return JSON.stringify(query);\n }\n\n private extractValue(record: any): any {\n if (!record) return null;\n if (Array.isArray(record)) {\n // ORMapRecord[]\n return record.map(r => r.value);\n }\n // LWWRecord or ORMapRecord\n return record.value;\n }\n\n private sendUpdate(sub: Subscription, key: string, value: any, type: 'UPDATE' | 'REMOVE') {\n if (sub.socket.readyState === 1) {\n sub.socket.send(serialize({\n type: 'QUERY_UPDATE',\n payload: {\n queryId: sub.id,\n key,\n value,\n type\n }\n }));\n }\n }\n\n private analyzeQueryFields(query: Query): Set<string> | 'ALL' {\n const fields = new Set<string>();\n try {\n if (query.predicate) {\n const extract = (node: PredicateNode) => {\n if (node.attribute) fields.add(node.attribute);\n if (node.children) node.children.forEach(extract);\n };\n extract(query.predicate);\n }\n if (query.where) {\n Object.keys(query.where).forEach(k => fields.add(k));\n }\n if (query.sort) {\n Object.keys(query.sort).forEach(k => fields.add(k));\n }\n } catch (e) {\n return 'ALL';\n }\n return fields.size > 0 ? fields : 'ALL';\n }\n\n private getChangedFields(oldValue: any, newValue: any): Set<string> | 'ALL' {\n // If values are arrays (ORMap), just return ALL for now to force check\n if (Array.isArray(oldValue) || Array.isArray(newValue)) return 'ALL';\n\n if (oldValue === newValue) return new Set();\n if (!oldValue && !newValue) return new Set();\n\n if (!oldValue) return new Set(Object.keys(newValue || {}));\n if (!newValue) return new Set(Object.keys(oldValue || {}));\n \n const changes = new Set<string>();\n const allKeys = new Set([...Object.keys(oldValue), ...Object.keys(newValue)]);\n \n for (const key of allKeys) {\n if (oldValue[key] !== newValue[key]) {\n changes.add(key);\n }\n }\n return changes;\n }\n}\n","import pino from 'pino';\n\nconst logLevel = process.env.LOG_LEVEL || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: process.env.NODE_ENV !== 'production' ? {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,hostname'\n }\n } : undefined,\n formatters: {\n level: (label) => {\n return { level: label };\n }\n }\n});\n\nexport type Logger = typeof logger;\n\n","import { ClusterManager } from '../cluster/ClusterManager';\nimport { logger } from '../utils/logger';\n\nexport interface TopicManagerConfig {\n cluster: ClusterManager;\n /** Callback to send message to a specific client */\n sendToClient: (clientId: string, message: any) => void;\n}\n\nexport class TopicManager {\n private subscribers: Map<string, Set<string>> = new Map(); // topic -> Set<clientId>\n private cluster: ClusterManager;\n private sendToClient: (clientId: string, message: any) => void;\n private readonly MAX_SUBSCRIPTIONS = 100; // M1: Basic limit\n\n constructor(config: TopicManagerConfig) {\n this.cluster = config.cluster;\n this.sendToClient = config.sendToClient;\n }\n\n private validateTopic(topic: string): void {\n // H2: Validation\n if (!topic || topic.length > 256 || !/^[\\w\\-.:/]+$/.test(topic)) {\n throw new Error('Invalid topic name');\n }\n }\n\n /**\n * Subscribe a client to a topic\n */\n public subscribe(clientId: string, topic: string) {\n this.validateTopic(topic);\n\n // Check limit (M1)\n // This is expensive (iterating all topics). Optimized: maintain client->topics map?\n // For now, iterate.\n let count = 0;\n for (const subs of this.subscribers.values()) {\n if (subs.has(clientId)) count++;\n }\n if (count >= this.MAX_SUBSCRIPTIONS) {\n throw new Error('Subscription limit reached');\n }\n\n if (!this.subscribers.has(topic)) {\n this.subscribers.set(topic, new Set());\n }\n this.subscribers.get(topic)!.add(clientId);\n logger.debug({ clientId, topic }, 'Client subscribed to topic');\n }\n\n /**\n * Unsubscribe a client from a topic\n */\n public unsubscribe(clientId: string, topic: string) {\n const subs = this.subscribers.get(topic);\n if (subs) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n logger.debug({ clientId, topic }, 'Client unsubscribed from topic');\n }\n }\n\n /**\n * Clean up all subscriptions for a client (e.g. on disconnect)\n */\n public unsubscribeAll(clientId: string) {\n for (const [topic, subs] of this.subscribers) {\n if (subs.has(clientId)) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n }\n }\n }\n\n /**\n * Publish a message to a topic\n * @param topic Topic name\n * @param data Message data\n * @param senderId Client ID of the publisher (optional)\n * @param fromCluster Whether this message came from another cluster node\n */\n public publish(topic: string, data: any, senderId?: string, fromCluster: boolean = false) {\n this.validateTopic(topic);\n\n // 1. Send to local subscribers\n const subs = this.subscribers.get(topic);\n if (subs) {\n const payload = {\n topic,\n data,\n publisherId: senderId,\n timestamp: Date.now()\n };\n \n const message = {\n type: 'TOPIC_MESSAGE',\n payload\n };\n\n for (const clientId of subs) {\n // Don't echo back to sender if local\n if (clientId !== senderId) {\n this.sendToClient(clientId, message);\n }\n }\n }\n\n // 2. Broadcast to cluster (only if not already from cluster)\n if (!fromCluster) {\n this.cluster.getMembers().forEach(nodeId => {\n if (!this.cluster.isLocal(nodeId)) {\n this.cluster.send(nodeId, 'CLUSTER_TOPIC_PUB', {\n topic,\n data,\n originalSenderId: senderId\n });\n }\n });\n }\n }\n}\n\n","import { WebSocket, WebSocketServer, ClientOptions as WsClientOptions } from 'ws';\nimport { EventEmitter } from 'events';\nimport * as dns from 'dns';\nimport { logger } from '../utils/logger';\nimport { readFileSync } from 'fs';\nimport * as https from 'https';\nimport { ClusterTLSConfig } from '../types/TLSConfig';\nimport { FailureDetector, FailureDetectorConfig, DEFAULT_FAILURE_DETECTOR_CONFIG } from './FailureDetector';\n\nexport interface ClusterConfig {\n nodeId: string;\n host: string;\n port: number;\n peers: string[]; // List of \"host:port\"\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: ClusterTLSConfig;\n /** Heartbeat interval in milliseconds. Default: 1000 */\n heartbeatIntervalMs?: number;\n /** Failure detection configuration */\n failureDetection?: Partial<FailureDetectorConfig>;\n}\n\nexport interface ClusterMember {\n nodeId: string;\n host: string;\n port: number;\n socket: WebSocket;\n isSelf: boolean;\n}\n\nexport interface ClusterMessage {\n type: 'HELLO' | 'OP_FORWARD' | 'PARTITION_UPDATE' | 'HEARTBEAT' | 'CLUSTER_EVENT' | 'CLUSTER_QUERY_EXEC' | 'CLUSTER_QUERY_RESP' | 'CLUSTER_GC_REPORT' | 'CLUSTER_GC_COMMIT' | 'CLUSTER_LOCK_REQ' | 'CLUSTER_LOCK_RELEASE' | 'CLUSTER_LOCK_GRANTED' | 'CLUSTER_LOCK_RELEASED' | 'CLUSTER_CLIENT_DISCONNECTED' | 'CLUSTER_TOPIC_PUB';\n senderId: string;\n payload: any;\n}\n\nexport class ClusterManager extends EventEmitter {\n public readonly config: ClusterConfig;\n private server?: WebSocketServer;\n private members: Map<string, ClusterMember> = new Map();\n private pendingConnections: Set<string> = new Set();\n private reconnectIntervals: Map<string, NodeJS.Timeout> = new Map();\n private discoveryTimer?: NodeJS.Timeout;\n private heartbeatTimer?: NodeJS.Timeout;\n private failureDetector: FailureDetector;\n\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\n\n // Initialize failure detector\n this.failureDetector = new FailureDetector({\n ...DEFAULT_FAILURE_DETECTOR_CONFIG,\n heartbeatIntervalMs: config.heartbeatIntervalMs ?? 1000,\n ...config.failureDetection,\n });\n\n // Forward failure detector events\n this.failureDetector.on('nodeSuspected', (event) => {\n logger.warn({ nodeId: event.nodeId, phi: event.phi }, 'Node suspected (failure detector)');\n this.emit('nodeSuspected', event.nodeId, event.phi);\n });\n\n this.failureDetector.on('nodeRecovered', (event) => {\n logger.info({ nodeId: event.nodeId }, 'Node recovered (failure detector)');\n this.emit('nodeRecovered', event.nodeId);\n });\n\n this.failureDetector.on('nodeConfirmedFailed', (event) => {\n logger.error({ nodeId: event.nodeId }, 'Node failure confirmed');\n this.emit('nodeConfirmedFailed', event.nodeId);\n // Remove failed node from members\n this.handleNodeFailure(event.nodeId);\n });\n }\n\n /**\n * Get the failure detector instance.\n */\n public getFailureDetector(): FailureDetector {\n return this.failureDetector;\n }\n\n private _actualPort: number = 0;\n\n /** Get the actual port the cluster is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n public start(): Promise<number> {\n return new Promise((resolve) => {\n logger.info({ port: this.config.port, tls: !!this.config.tls?.enabled }, 'Starting Cluster Manager');\n\n if (this.config.tls?.enabled) {\n // HTTPS-based WebSocket Server for cluster\n const tlsOptions = this.buildClusterTLSOptions();\n const httpsServer = https.createServer(tlsOptions);\n this.server = new WebSocketServer({ server: httpsServer });\n\n httpsServer.listen(this.config.port, () => {\n const addr = httpsServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening (TLS enabled)');\n this.onServerReady(resolve);\n });\n } else {\n this.server = new WebSocketServer({ port: this.config.port });\n\n this.server.on('listening', () => {\n const addr = this.server!.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening');\n this.onServerReady(resolve);\n });\n }\n\n this.server?.on('connection', (ws, req) => {\n logger.info({ remoteAddress: req.socket.remoteAddress }, 'Incoming cluster connection');\n this.handleSocket(ws, false);\n });\n });\n }\n\n /** Called when server is ready - registers self and initiates peer connections */\n private onServerReady(resolve: (port: number) => void): void {\n // Add self to members with actual port\n this.members.set(this.config.nodeId, {\n nodeId: this.config.nodeId,\n host: this.config.host,\n port: this._actualPort,\n socket: null as any,\n isSelf: true\n });\n\n // Connect to peers after we know our port\n if (this.config.discovery === 'kubernetes' && this.config.serviceName) {\n this.startDiscovery();\n } else {\n this.connectToPeers();\n }\n\n resolve(this._actualPort);\n }\n\n public stop() {\n logger.info({ port: this.config.port }, 'Stopping Cluster Manager');\n\n // Stop heartbeat\n this.stopHeartbeat();\n\n // Stop failure detector\n this.failureDetector.stop();\n\n // Clear reconnect intervals\n for (const timeout of this.reconnectIntervals.values()) {\n clearTimeout(timeout);\n }\n this.reconnectIntervals.clear();\n if (this.discoveryTimer) {\n clearInterval(this.discoveryTimer);\n this.discoveryTimer = undefined;\n }\n this.pendingConnections.clear();\n\n // Close all peer connections\n for (const member of this.members.values()) {\n if (member.socket) {\n member.socket.terminate(); // Force close\n }\n }\n this.members.clear();\n\n // Close server\n if (this.server) {\n this.server.close();\n }\n }\n\n /**\n * Start sending heartbeats to all peers.\n */\n private startHeartbeat(): void {\n if (this.heartbeatTimer) return;\n\n const intervalMs = this.config.heartbeatIntervalMs ?? 1000;\n\n this.heartbeatTimer = setInterval(() => {\n this.sendHeartbeatToAll();\n }, intervalMs);\n\n // Also start failure detector\n this.failureDetector.start();\n\n logger.debug({ intervalMs }, 'Heartbeat started');\n }\n\n /**\n * Stop sending heartbeats.\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n /**\n * Send heartbeat to all connected peers.\n */\n private sendHeartbeatToAll(): void {\n for (const [nodeId, member] of this.members) {\n if (member.isSelf) continue;\n if (member.socket && member.socket.readyState === WebSocket.OPEN) {\n this.send(nodeId, 'HEARTBEAT', { timestamp: Date.now() });\n }\n }\n }\n\n /**\n * Handle incoming heartbeat from a peer.\n */\n private handleHeartbeat(senderId: string, _payload: { timestamp: number }): void {\n this.failureDetector.recordHeartbeat(senderId);\n }\n\n /**\n * Handle confirmed node failure.\n */\n private handleNodeFailure(nodeId: string): void {\n const member = this.members.get(nodeId);\n if (!member) return;\n\n logger.warn({ nodeId }, 'Removing failed node from cluster');\n\n // Close socket if still connected\n if (member.socket && member.socket.readyState !== WebSocket.CLOSED) {\n try {\n member.socket.terminate();\n } catch (e) {\n // Ignore close errors\n }\n }\n\n // Remove from members\n this.members.delete(nodeId);\n\n // Stop monitoring\n this.failureDetector.stopMonitoring(nodeId);\n\n // Emit memberLeft event\n this.emit('memberLeft', nodeId);\n }\n\n private connectToPeers() {\n for (const peer of this.config.peers) {\n this.connectToPeer(peer);\n }\n }\n\n private startDiscovery() {\n const runDiscovery = async () => {\n if (!this.config.serviceName) return;\n\n try {\n const addresses = await dns.promises.resolve4(this.config.serviceName);\n logger.debug({ addresses, serviceName: this.config.serviceName }, 'DNS discovery results');\n\n for (const ip of addresses) {\n // Use actual port if available (likely matching K8s config), fallback to config port\n const targetPort = this._actualPort || this.config.port;\n const peerAddress = `${ip}:${targetPort}`;\n // Attempt to connect. connectToPeer handles dupes and self-checks (via handshake eventually)\n this.connectToPeer(peerAddress);\n }\n } catch (err: any) {\n logger.error({ err: err.message, serviceName: this.config.serviceName }, 'DNS discovery failed');\n }\n };\n\n logger.info({ serviceName: this.config.serviceName }, 'Starting Kubernetes DNS discovery');\n runDiscovery();\n // Default to 10s if not specified, to be less aggressive\n this.discoveryTimer = setInterval(runDiscovery, this.config.discoveryInterval || 10000);\n }\n\n private scheduleReconnect(peerAddress: string, attempt: number = 0) {\n if (this.reconnectIntervals.has(peerAddress)) return;\n\n // Exponential backoff: 5s, 10s, 20s, 40s, 60s (max)\n const delay = Math.min(5000 * Math.pow(2, attempt), 60000);\n\n const timeout = setTimeout(() => {\n this.reconnectIntervals.delete(peerAddress);\n // Pass next attempt number\n this.connectToPeerWithBackoff(peerAddress, attempt + 1);\n }, delay);\n\n this.reconnectIntervals.set(peerAddress, timeout);\n }\n\n // Helper to track attempts\n private connectToPeerWithBackoff(peerAddress: string, attempt: number) {\n // We need to modify connectToPeer to accept attempt or create a wrapper.\n // To keep it simple without changing signature of connectToPeer everywhere,\n // we'll just call connectToPeer and let it fail -> scheduleReconnect -> increment attempt.\n // But connectToPeer logic needs to pass the attempt to scheduleReconnect on failure.\n // Refactoring connectToPeer to take optional attempt param.\n this._connectToPeerInternal(peerAddress, attempt);\n }\n\n private connectToPeer(peerAddress: string) {\n this._connectToPeerInternal(peerAddress, 0);\n }\n\n private _connectToPeerInternal(peerAddress: string, attempt: number) {\n if (this.pendingConnections.has(peerAddress)) return;\n\n // Check if already connected\n for (const member of this.members.values()) {\n if (`${member.host}:${member.port}` === peerAddress) return;\n }\n\n // PREVENT LOOP: ... (omitted comments)\n\n logger.info({ peerAddress, attempt, tls: !!this.config.tls?.enabled }, 'Connecting to peer');\n this.pendingConnections.add(peerAddress);\n\n try {\n let ws: WebSocket;\n\n if (this.config.tls?.enabled) {\n // Secure WebSocket connection\n const protocol = 'wss://';\n const wsOptions: WsClientOptions = {\n rejectUnauthorized: this.config.tls.rejectUnauthorized !== false,\n };\n\n // mTLS: Provide client certificate\n if (this.config.tls.certPath && this.config.tls.keyPath) {\n wsOptions.cert = readFileSync(this.config.tls.certPath);\n wsOptions.key = readFileSync(this.config.tls.keyPath);\n\n if (this.config.tls.passphrase) {\n wsOptions.passphrase = this.config.tls.passphrase;\n }\n }\n\n // CA for peer verification\n if (this.config.tls.caCertPath) {\n wsOptions.ca = readFileSync(this.config.tls.caCertPath);\n }\n\n ws = new WebSocket(`${protocol}${peerAddress}`, wsOptions);\n } else {\n // Plain WebSocket (development)\n ws = new WebSocket(`ws://${peerAddress}`);\n }\n\n ws.on('open', () => {\n this.pendingConnections.delete(peerAddress);\n logger.info({ peerAddress }, 'Connected to peer');\n // Reset backoff on success\n this.handleSocket(ws, true, peerAddress);\n });\n\n ws.on('error', (err) => {\n logger.error({ peerAddress, err: err.message }, 'Connection error to peer');\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n });\n\n ws.on('close', () => {\n this.pendingConnections.delete(peerAddress);\n });\n\n } catch (e) {\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n }\n }\n\n private handleSocket(ws: WebSocket, initiated: boolean, peerAddress?: string) {\n // Handshake: Send my NodeID with actual port (not config port which may be 0)\n const helloMsg: ClusterMessage = {\n type: 'HELLO',\n senderId: this.config.nodeId,\n payload: {\n host: this.config.host,\n port: this._actualPort || this.config.port\n }\n };\n ws.send(JSON.stringify(helloMsg));\n\n let remoteNodeId: string | null = null;\n\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as ClusterMessage;\n\n if (msg.type === 'HELLO') {\n remoteNodeId = msg.senderId;\n const { host, port } = msg.payload;\n logger.info({ nodeId: remoteNodeId, host, port }, 'Peer identified');\n\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n\n const myId = this.config.nodeId;\n const otherId = remoteNodeId;\n\n // Determine who initiated this specific socket\n const initiatorId = initiated ? myId : otherId;\n const receiverId = initiated ? otherId : myId;\n\n /*\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n // \n // DISABLED: This strict rule prevents High-ID nodes from joining Low-ID seeds (common pattern).\n // We only use this for duplicate resolution now.\n \n if (initiatorId >= receiverId) {\n logger.info({ initiatorId, receiverId }, 'Dropping connection (Low-ID Initiator Policy)');\n try {\n ws.close();\n } catch(e) {}\n return;\n }\n */\n\n // If we get here, this is a VALID connection.\n // Check if we somehow already have a connection\n if (this.members.has(remoteNodeId)) {\n logger.warn({ nodeId: remoteNodeId }, 'Duplicate valid connection. Replacing.');\n // In a real production system, we should use the Tie-Breaker here to decide which one to keep\n // to avoid split-brain socket usage.\n // For now, 'Replacing' means Last-Write-Wins on the connection slot.\n }\n\n this.members.set(remoteNodeId, {\n nodeId: remoteNodeId,\n host,\n port,\n socket: ws,\n isSelf: false\n });\n\n // Start monitoring this node for failures\n this.failureDetector.startMonitoring(remoteNodeId);\n\n // Start heartbeat if not already started\n this.startHeartbeat();\n\n this.emit('memberJoined', remoteNodeId);\n } else if (msg.type === 'HEARTBEAT') {\n // Handle incoming heartbeat\n if (remoteNodeId) {\n this.handleHeartbeat(remoteNodeId, msg.payload);\n }\n } else {\n this.emit('message', msg);\n }\n } catch (err) {\n logger.error({ err }, 'Failed to parse cluster message');\n }\n });\n\n ws.on('close', () => {\n if (remoteNodeId) {\n // Only handle disconnect if this was the ACTIVE socket\n // This prevents \"duplicate connection\" cleanup from killing the valid session\n const current = this.members.get(remoteNodeId);\n if (current && current.socket === ws) {\n logger.info({ nodeId: remoteNodeId }, 'Peer disconnected');\n this.members.delete(remoteNodeId);\n\n // Stop monitoring this node\n this.failureDetector.stopMonitoring(remoteNodeId);\n\n this.emit('memberLeft', remoteNodeId);\n\n // If we initiated, we should try to reconnect\n if (initiated && peerAddress) {\n // Start with 0 attempt on fresh disconnect?\n // Or maybe we should consider this a failure and backoff?\n // Let's restart with 0 for now as it might be a temp network blip\n this.scheduleReconnect(peerAddress, 0);\n }\n } else {\n // console.log(`Ignored close from stale/duplicate socket for ${remoteNodeId}`);\n }\n }\n });\n }\n\n public send(nodeId: string, type: ClusterMessage['type'], payload: any) {\n const member = this.members.get(nodeId);\n if (member && member.socket && member.socket.readyState === WebSocket.OPEN) {\n const msg: ClusterMessage = {\n type,\n senderId: this.config.nodeId,\n payload\n };\n member.socket.send(JSON.stringify(msg));\n } else {\n logger.warn({ nodeId }, 'Cannot send to node: not connected');\n }\n }\n\n public sendToNode(nodeId: string, message: any) {\n this.send(nodeId, 'OP_FORWARD', message);\n }\n\n public getMembers(): string[] {\n return Array.from(this.members.keys());\n }\n\n public isLocal(nodeId: string): boolean {\n return nodeId === this.config.nodeId;\n }\n\n private buildClusterTLSOptions(): https.ServerOptions {\n const config = this.config.tls!;\n\n const options: https.ServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.requireClientCert) {\n options.requestCert = true;\n options.rejectUnauthorized = true;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n\n","/**\n * FailureDetector - Phi Accrual Failure Detector\n *\n * Implements the Phi Accrual Failure Detection algorithm for distributed systems.\n * Based on the paper: \"The φ Accrual Failure Detector\" by Hayashibara et al.\n *\n * The detector provides a suspicion level (phi) rather than binary alive/dead status,\n * allowing the application to make decisions based on configurable thresholds.\n *\n * Hazelcast equivalent: com.hazelcast.internal.cluster.fd.PhiAccrualFailureDetector\n */\n\nimport { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface FailureDetectorConfig {\n /** Interval between heartbeat checks (ms). Default: 1000 */\n heartbeatIntervalMs: number;\n /** Time after which a node is suspected if no heartbeat received (ms). Default: 5000 */\n suspicionTimeoutMs: number;\n /** Time after suspicion before confirming failure (ms). Default: 10000 */\n confirmationTimeoutMs: number;\n /** Phi threshold above which a node is considered suspected. Default: 8 */\n phiThreshold: number;\n /** Minimum samples required for accurate phi calculation. Default: 10 */\n minSamples: number;\n /** Maximum samples to keep in history. Default: 100 */\n maxSamples: number;\n /** Initial heartbeat interval estimate (ms). Default: 1000 */\n initialHeartbeatIntervalMs: number;\n}\n\nexport const DEFAULT_FAILURE_DETECTOR_CONFIG: FailureDetectorConfig = {\n heartbeatIntervalMs: 1000,\n suspicionTimeoutMs: 5000,\n confirmationTimeoutMs: 10000,\n phiThreshold: 8,\n minSamples: 10,\n maxSamples: 100,\n initialHeartbeatIntervalMs: 1000,\n};\n\nexport interface NodeState {\n /** Last heartbeat timestamp */\n lastHeartbeat: number;\n /** Heartbeat interval history for phi calculation */\n intervalHistory: number[];\n /** Whether node is currently suspected */\n isSuspected: boolean;\n /** Timestamp when suspicion started */\n suspicionStartTime?: number;\n /** Whether failure has been confirmed */\n isConfirmedFailed: boolean;\n}\n\nexport interface FailureDetectorEvents {\n nodeSuspected: { nodeId: string; phi: number };\n nodeRecovered: { nodeId: string };\n nodeConfirmedFailed: { nodeId: string };\n}\n\nexport class FailureDetector extends EventEmitter {\n private config: FailureDetectorConfig;\n private nodeStates: Map<string, NodeState> = new Map();\n private monitoringNodes: Set<string> = new Set();\n private checkTimer?: ReturnType<typeof setInterval>;\n private confirmationTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private started = false;\n\n constructor(config: Partial<FailureDetectorConfig> = {}) {\n super();\n this.config = { ...DEFAULT_FAILURE_DETECTOR_CONFIG, ...config };\n }\n\n /**\n * Start the failure detector monitoring loop.\n */\n start(): void {\n if (this.started) return;\n this.started = true;\n\n this.checkTimer = setInterval(() => {\n this.checkAllNodes();\n }, this.config.heartbeatIntervalMs);\n\n logger.info({ config: this.config }, 'FailureDetector started');\n }\n\n /**\n * Stop the failure detector and clean up.\n */\n stop(): void {\n if (!this.started) return;\n this.started = false;\n\n if (this.checkTimer) {\n clearInterval(this.checkTimer);\n this.checkTimer = undefined;\n }\n\n // Clear all confirmation timers\n for (const timer of this.confirmationTimers.values()) {\n clearTimeout(timer);\n }\n this.confirmationTimers.clear();\n\n logger.info('FailureDetector stopped');\n }\n\n /**\n * Start monitoring a node.\n */\n startMonitoring(nodeId: string): void {\n if (this.monitoringNodes.has(nodeId)) return;\n\n this.monitoringNodes.add(nodeId);\n this.nodeStates.set(nodeId, {\n lastHeartbeat: Date.now(),\n intervalHistory: [],\n isSuspected: false,\n isConfirmedFailed: false,\n });\n\n logger.debug({ nodeId }, 'Started monitoring node');\n }\n\n /**\n * Stop monitoring a node.\n */\n stopMonitoring(nodeId: string): void {\n this.monitoringNodes.delete(nodeId);\n this.nodeStates.delete(nodeId);\n\n const timer = this.confirmationTimers.get(nodeId);\n if (timer) {\n clearTimeout(timer);\n this.confirmationTimers.delete(nodeId);\n }\n\n logger.debug({ nodeId }, 'Stopped monitoring node');\n }\n\n /**\n * Record a heartbeat from a node.\n * This updates the node's state and clears any suspicion.\n */\n recordHeartbeat(nodeId: string): void {\n const state = this.nodeStates.get(nodeId);\n if (!state) {\n // Auto-start monitoring if not already\n this.startMonitoring(nodeId);\n return;\n }\n\n const now = Date.now();\n const interval = now - state.lastHeartbeat;\n\n // Update interval history\n state.intervalHistory.push(interval);\n if (state.intervalHistory.length > this.config.maxSamples) {\n state.intervalHistory.shift();\n }\n\n state.lastHeartbeat = now;\n\n // If was suspected, clear suspicion\n if (state.isSuspected) {\n state.isSuspected = false;\n state.suspicionStartTime = undefined;\n state.isConfirmedFailed = false;\n\n // Cancel confirmation timer\n const timer = this.confirmationTimers.get(nodeId);\n if (timer) {\n clearTimeout(timer);\n this.confirmationTimers.delete(nodeId);\n }\n\n this.emit('nodeRecovered', { nodeId });\n logger.info({ nodeId }, 'Node recovered');\n }\n }\n\n /**\n * Check all monitored nodes for failure.\n */\n private checkAllNodes(): void {\n for (const nodeId of this.monitoringNodes) {\n const phi = this.calculatePhi(nodeId);\n const state = this.nodeStates.get(nodeId);\n\n if (!state) continue;\n\n if (phi > this.config.phiThreshold) {\n if (!state.isSuspected) {\n state.isSuspected = true;\n state.suspicionStartTime = Date.now();\n\n this.emit('nodeSuspected', { nodeId, phi });\n logger.warn({ nodeId, phi }, 'Node suspected');\n\n // Schedule confirmation\n this.scheduleConfirmation(nodeId);\n }\n }\n }\n }\n\n /**\n * Schedule failure confirmation after suspicion timeout.\n */\n private scheduleConfirmation(nodeId: string): void {\n // Cancel existing timer if any\n const existingTimer = this.confirmationTimers.get(nodeId);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n const timer = setTimeout(() => {\n this.confirmFailure(nodeId);\n }, this.config.confirmationTimeoutMs);\n\n this.confirmationTimers.set(nodeId, timer);\n }\n\n /**\n * Confirm node failure after confirmation timeout.\n */\n private confirmFailure(nodeId: string): void {\n const state = this.nodeStates.get(nodeId);\n if (!state) return;\n\n // Only confirm if still suspected\n if (state.isSuspected && !state.isConfirmedFailed) {\n state.isConfirmedFailed = true;\n this.emit('nodeConfirmedFailed', { nodeId });\n logger.error({ nodeId }, 'Node failure confirmed');\n }\n\n this.confirmationTimers.delete(nodeId);\n }\n\n /**\n * Calculate the phi value for a node using the Phi Accrual algorithm.\n *\n * Phi = -log10(P_later(t_now - t_last))\n *\n * where P_later is the probability that a heartbeat will arrive later than expected.\n */\n calculatePhi(nodeId: string): number {\n const state = this.nodeStates.get(nodeId);\n if (!state) return 0;\n\n const now = Date.now();\n const timeSinceLastHeartbeat = now - state.lastHeartbeat;\n\n // If we don't have enough samples, use simple timeout-based detection\n if (state.intervalHistory.length < this.config.minSamples) {\n // Simple fallback: phi increases linearly with time since last heartbeat\n const expectedInterval = this.config.initialHeartbeatIntervalMs;\n return timeSinceLastHeartbeat / expectedInterval;\n }\n\n // Calculate mean and variance of intervals\n const mean = this.calculateMean(state.intervalHistory);\n const variance = this.calculateVariance(state.intervalHistory, mean);\n const stdDev = Math.sqrt(variance);\n\n // Phi Accrual formula using normal distribution approximation\n // Phi = -log10(1 - CDF(timeSinceLastHeartbeat))\n // For simplicity, we use: phi ≈ (t - μ) / σ for t > μ\n if (timeSinceLastHeartbeat <= mean) {\n return 0; // No suspicion if within expected interval\n }\n\n // Calculate how many standard deviations away we are\n const deviations = stdDev > 0 ? (timeSinceLastHeartbeat - mean) / stdDev : 0;\n\n // Convert to phi using exponential distribution approximation\n // This gives phi values in a similar range to Hazelcast (0-16+)\n const phi = Math.max(0, deviations);\n\n return phi;\n }\n\n /**\n * Calculate mean of an array of numbers.\n */\n private calculateMean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((sum, v) => sum + v, 0) / values.length;\n }\n\n /**\n * Calculate variance of an array of numbers.\n */\n private calculateVariance(values: number[], mean: number): number {\n if (values.length < 2) return 0;\n return values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length;\n }\n\n /**\n * Get list of currently suspected nodes.\n */\n getSuspectedNodes(): string[] {\n const suspected: string[] = [];\n for (const [nodeId, state] of this.nodeStates) {\n if (state.isSuspected) {\n suspected.push(nodeId);\n }\n }\n return suspected;\n }\n\n /**\n * Get list of confirmed failed nodes.\n */\n getConfirmedFailedNodes(): string[] {\n const failed: string[] = [];\n for (const [nodeId, state] of this.nodeStates) {\n if (state.isConfirmedFailed) {\n failed.push(nodeId);\n }\n }\n return failed;\n }\n\n /**\n * Check if a specific node is suspected.\n */\n isSuspected(nodeId: string): boolean {\n return this.nodeStates.get(nodeId)?.isSuspected ?? false;\n }\n\n /**\n * Check if a specific node's failure is confirmed.\n */\n isConfirmedFailed(nodeId: string): boolean {\n return this.nodeStates.get(nodeId)?.isConfirmedFailed ?? false;\n }\n\n /**\n * Get the current phi value for a node.\n */\n getPhi(nodeId: string): number {\n return this.calculatePhi(nodeId);\n }\n\n /**\n * Get all monitored nodes.\n */\n getMonitoredNodes(): string[] {\n return Array.from(this.monitoringNodes);\n }\n\n /**\n * Get metrics for monitoring.\n */\n getMetrics(): {\n monitoredNodes: number;\n suspectedNodes: number;\n confirmedFailedNodes: number;\n } {\n let suspectedCount = 0;\n let confirmedCount = 0;\n\n for (const state of this.nodeStates.values()) {\n if (state.isSuspected) suspectedCount++;\n if (state.isConfirmedFailed) confirmedCount++;\n }\n\n return {\n monitoredNodes: this.monitoringNodes.size,\n suspectedNodes: suspectedCount,\n confirmedFailedNodes: confirmedCount,\n };\n }\n}\n","import { EventEmitter } from 'events';\nimport { ClusterManager } from './ClusterManager';\nimport { MigrationManager } from './MigrationManager';\nimport {\n hashString,\n PartitionMap,\n PartitionInfo,\n NodeInfo,\n PartitionChange,\n MigrationConfig,\n MigrationStatus,\n PARTITION_COUNT,\n DEFAULT_BACKUP_COUNT,\n DEFAULT_MIGRATION_CONFIG,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport interface PartitionServiceEvents {\n 'rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;\n 'partitionMoved': (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => void;\n}\n\nexport interface PartitionServiceConfig {\n /** Enable gradual rebalancing (default: false for backward compatibility) */\n gradualRebalancing: boolean;\n /** Migration configuration */\n migration: Partial<MigrationConfig>;\n}\n\nexport const DEFAULT_PARTITION_SERVICE_CONFIG: PartitionServiceConfig = {\n gradualRebalancing: false,\n migration: DEFAULT_MIGRATION_CONFIG,\n};\n\nexport class PartitionService extends EventEmitter {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = PARTITION_COUNT;\n private readonly BACKUP_COUNT = DEFAULT_BACKUP_COUNT;\n\n // Phase 4: Version tracking for partition map\n private mapVersion: number = 0;\n private lastRebalanceTime: number = 0;\n\n // Phase 4 Task 03: Gradual rebalancing\n private config: PartitionServiceConfig;\n private migrationManager: MigrationManager | null = null;\n\n constructor(cluster: ClusterManager, config: Partial<PartitionServiceConfig> = {}) {\n super();\n this.cluster = cluster;\n this.config = {\n ...DEFAULT_PARTITION_SERVICE_CONFIG,\n ...config,\n };\n\n // Initialize migration manager if gradual rebalancing is enabled\n if (this.config.gradualRebalancing) {\n this.migrationManager = new MigrationManager(\n cluster,\n this,\n this.config.migration\n );\n\n // Forward migration events\n this.migrationManager.on('migrationComplete', (partitionId: number) => {\n logger.info({ partitionId }, 'Migration completed, updating ownership');\n });\n\n this.migrationManager.on('migrationFailed', (partitionId: number, error: Error) => {\n logger.error({ partitionId, error: error.message }, 'Migration failed');\n });\n }\n\n this.cluster.on('memberJoined', (nodeId: string) => this.onMembershipChange('JOIN', nodeId));\n this.cluster.on('memberLeft', (nodeId: string) => this.onMembershipChange('LEAVE', nodeId));\n\n // Initial rebalance (always immediate on startup)\n this.rebalance('REBALANCE');\n }\n\n /**\n * Handle membership change\n */\n private onMembershipChange(reason: 'JOIN' | 'LEAVE', nodeId: string): void {\n if (this.config.gradualRebalancing && this.migrationManager) {\n // Use gradual rebalancing\n this.rebalanceGradual(reason, nodeId);\n } else {\n // Use immediate rebalancing (original behavior)\n this.rebalance(reason, nodeId);\n }\n }\n\n public getPartitionId(key: string): number {\n // Use Math.abs to ensure positive partition ID\n return Math.abs(hashString(key)) % this.PARTITION_COUNT;\n }\n\n public getDistribution(key: string): PartitionDistribution {\n const pId = this.getPartitionId(key);\n return this.partitions.get(pId) || { \n owner: this.cluster.config.nodeId, \n backups: [] \n };\n }\n\n public getOwner(key: string): string {\n return this.getDistribution(key).owner;\n }\n\n public isLocalOwner(key: string): boolean {\n return this.getOwner(key) === this.cluster.config.nodeId;\n }\n\n public isLocalBackup(key: string): boolean {\n const dist = this.getDistribution(key);\n return dist.backups.includes(this.cluster.config.nodeId);\n }\n\n public isRelated(key: string): boolean {\n return this.isLocalOwner(key) || this.isLocalBackup(key);\n }\n\n // ============================================\n // Phase 4: Partition Map Methods\n // ============================================\n\n /**\n * Get current partition map version\n */\n public getMapVersion(): number {\n return this.mapVersion;\n }\n\n /**\n * Generate full PartitionMap for client consumption\n */\n public getPartitionMap(): PartitionMap {\n const nodes: NodeInfo[] = [];\n const partitions: PartitionInfo[] = [];\n\n // Build node info from cluster members\n for (const nodeId of this.cluster.getMembers()) {\n const isSelf = nodeId === this.cluster.config.nodeId;\n const host = isSelf ? this.cluster.config.host : 'unknown';\n const port = isSelf ? this.cluster.port : 0;\n\n nodes.push({\n nodeId,\n endpoints: {\n websocket: `ws://${host}:${port}`,\n },\n status: 'ACTIVE',\n });\n }\n\n // Build partition info\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const dist = this.partitions.get(i);\n if (dist) {\n partitions.push({\n partitionId: i,\n ownerNodeId: dist.owner,\n backupNodeIds: dist.backups,\n });\n }\n }\n\n return {\n version: this.mapVersion,\n partitionCount: this.PARTITION_COUNT,\n nodes,\n partitions,\n generatedAt: Date.now(),\n };\n }\n\n /**\n * Get partition info by ID\n */\n public getPartitionInfo(partitionId: number): PartitionInfo | null {\n const dist = this.partitions.get(partitionId);\n if (!dist) return null;\n\n return {\n partitionId,\n ownerNodeId: dist.owner,\n backupNodeIds: dist.backups,\n };\n }\n\n /**\n * Get owner node for a partition ID\n */\n public getPartitionOwner(partitionId: number): string | null {\n const dist = this.partitions.get(partitionId);\n return dist?.owner ?? null;\n }\n\n private rebalance(reason: 'REBALANCE' | 'FAILOVER' | 'JOIN' | 'LEAVE' = 'REBALANCE', triggerNodeId?: string) {\n // Store old partitions for change detection\n const oldPartitions = new Map(this.partitions);\n\n // this.cluster.getMembers() includes self (added in ClusterManager.start)\n let allMembers = this.cluster.getMembers().sort();\n\n // If no other members, include self\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n logger.info({ memberCount: allMembers.length, members: allMembers, reason }, 'Rebalancing partitions');\n\n const changes: PartitionChange[] = [];\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n\n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n // Track changes\n const oldDist = oldPartitions.get(i);\n if (oldDist && oldDist.owner !== owner) {\n changes.push({\n partitionId: i,\n previousOwner: oldDist.owner,\n newOwner: owner,\n reason,\n });\n }\n\n this.partitions.set(i, { owner, backups });\n }\n\n // Increment version if there were changes\n if (changes.length > 0 || this.mapVersion === 0) {\n this.mapVersion++;\n this.lastRebalanceTime = Date.now();\n\n logger.info({\n version: this.mapVersion,\n changesCount: changes.length,\n reason,\n }, 'Partition map updated');\n\n // Emit event for ServerCoordinator to broadcast to clients\n this.emit('rebalanced', this.getPartitionMap(), changes);\n }\n }\n\n // ============================================\n // Phase 4 Task 03: Gradual Rebalancing\n // ============================================\n\n /**\n * Perform gradual rebalancing using MigrationManager\n */\n private rebalanceGradual(reason: 'JOIN' | 'LEAVE', triggerNodeId: string): void {\n if (!this.migrationManager) {\n // Fall back to immediate rebalancing\n this.rebalance(reason, triggerNodeId);\n return;\n }\n\n // Store old distribution\n const oldDistribution = new Map(this.partitions);\n\n // Calculate new distribution\n let allMembers = this.cluster.getMembers().sort();\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n const newDistribution = new Map<number, PartitionDistribution>();\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n\n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n newDistribution.set(i, { owner, backups });\n }\n\n logger.info({ memberCount: allMembers.length, reason, triggerNodeId }, 'Planning gradual rebalance');\n\n // Plan migrations (MigrationManager will handle gradual transfer)\n this.migrationManager.planMigration(oldDistribution, newDistribution);\n\n // Update partition map immediately for routing purposes\n // Actual data migration happens in background\n for (const [partitionId, dist] of newDistribution) {\n this.partitions.set(partitionId, dist);\n }\n\n this.mapVersion++;\n this.lastRebalanceTime = Date.now();\n\n // Emit event for clients\n const changes: PartitionChange[] = [];\n for (const [partitionId, newDist] of newDistribution) {\n const oldDist = oldDistribution.get(partitionId);\n if (oldDist && oldDist.owner !== newDist.owner) {\n changes.push({\n partitionId,\n previousOwner: oldDist.owner,\n newOwner: newDist.owner,\n reason,\n });\n }\n }\n\n this.emit('rebalanced', this.getPartitionMap(), changes);\n }\n\n /**\n * Set partition owner (called after migration completes)\n */\n public setOwner(partitionId: number, nodeId: string): void {\n const partition = this.partitions.get(partitionId);\n if (!partition) return;\n\n const previousOwner = partition.owner;\n if (previousOwner === nodeId) return; // No change\n\n partition.owner = nodeId;\n this.mapVersion++;\n\n logger.info({ partitionId, previousOwner, newOwner: nodeId, version: this.mapVersion }, 'Partition owner updated');\n\n this.emit('partitionMoved', {\n partitionId,\n previousOwner,\n newOwner: nodeId,\n version: this.mapVersion,\n });\n }\n\n /**\n * Get backups for a partition\n */\n public getBackups(partitionId: number): string[] {\n const dist = this.partitions.get(partitionId);\n return dist?.backups ?? [];\n }\n\n /**\n * Get migration status\n */\n public getMigrationStatus(): MigrationStatus | null {\n return this.migrationManager?.getStatus() ?? null;\n }\n\n /**\n * Check if partition is currently migrating\n */\n public isMigrating(partitionId: number): boolean {\n return this.migrationManager?.isActive(partitionId) ?? false;\n }\n\n /**\n * Check if any partition is currently migrating\n */\n public isRebalancing(): boolean {\n const status = this.getMigrationStatus();\n return status?.inProgress ?? false;\n }\n\n /**\n * Get MigrationManager for configuration\n */\n public getMigrationManager(): MigrationManager | null {\n return this.migrationManager;\n }\n\n /**\n * Cancel all migrations\n */\n public async cancelMigrations(): Promise<void> {\n if (this.migrationManager) {\n await this.migrationManager.cancelAll();\n }\n }\n}\n","/**\n * MigrationManager - Manages gradual partition rebalancing\n *\n * Phase 4 Task 03: Parallel Partition Sync\n *\n * Features:\n * - Gradual rebalancing with configurable batch size\n * - State machine for migration lifecycle\n * - Backpressure via chunk acknowledgments\n * - Retry logic for failed migrations\n * - Metrics and observability\n */\n\nimport { EventEmitter } from 'events';\nimport {\n PartitionState,\n PartitionMigration,\n MigrationConfig,\n MigrationStatus,\n MigrationMetrics,\n DEFAULT_MIGRATION_CONFIG,\n PartitionChange,\n serialize,\n} from '@topgunbuild/core';\nimport { xxhash64AsNumber, createXxHash64State } from '@topgunbuild/native';\nimport { ClusterManager, ClusterMessage } from './ClusterManager';\nimport { PartitionService, PartitionDistribution } from './PartitionService';\nimport { logger } from '../utils/logger';\n\nexport interface IncomingMigration {\n sourceNode: string;\n chunks: Uint8Array[];\n expectedSize: number;\n receivedSize: number;\n startTime: number;\n}\n\nexport interface MigrationManagerEvents {\n 'migrationPlanned': (info: { total: number }) => void;\n 'batchStarted': (info: { count: number; remaining: number }) => void;\n 'migrationProgress': (migration: PartitionMigration) => void;\n 'migrationComplete': (partitionId: number) => void;\n 'migrationFailed': (partitionId: number, error: Error) => void;\n 'error': (error: Error) => void;\n}\n\nexport class MigrationManager extends EventEmitter {\n private readonly config: MigrationConfig;\n private readonly clusterManager: ClusterManager;\n private readonly partitionService: PartitionService;\n\n // Active outgoing migrations (this node is source)\n private activeMigrations: Map<number, PartitionMigration> = new Map();\n // Queue of migrations to process\n private migrationQueue: PartitionMigration[] = [];\n // Incoming migrations (this node is target)\n private incomingMigrations: Map<number, IncomingMigration> = new Map();\n // Pending chunk acknowledgments\n private pendingChunkAcks: Map<string, { resolve: () => void; reject: (err: Error) => void; timeout: ReturnType<typeof setTimeout> }> = new Map();\n // Pending verification results\n private pendingVerifications: Map<number, { resolve: (success: boolean) => void; timeout: ReturnType<typeof setTimeout> }> = new Map();\n\n // Metrics tracking\n private metrics: MigrationMetrics = {\n migrationsStarted: 0,\n migrationsCompleted: 0,\n migrationsFailed: 0,\n chunksTransferred: 0,\n bytesTransferred: 0,\n activeMigrations: 0,\n queuedMigrations: 0,\n };\n\n // Batch processing timer\n private batchTimer: ReturnType<typeof setInterval> | null = null;\n\n // Data collection callback (injected from ServerCoordinator)\n private dataCollector: ((partitionId: number) => Promise<Uint8Array[]>) | null = null;\n // Data storage callback (injected from ServerCoordinator)\n private dataStorer: ((partitionId: number, data: Uint8Array[]) => Promise<void>) | null = null;\n\n constructor(\n clusterManager: ClusterManager,\n partitionService: PartitionService,\n config: Partial<MigrationConfig> = {}\n ) {\n super();\n this.clusterManager = clusterManager;\n this.partitionService = partitionService;\n this.config = {\n ...DEFAULT_MIGRATION_CONFIG,\n ...config,\n };\n\n this.setupMessageHandlers();\n }\n\n // ============================================\n // Configuration\n // ============================================\n\n /**\n * Set the data collector callback\n * Called to collect all records for a partition before migration\n */\n public setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void {\n this.dataCollector = collector;\n }\n\n /**\n * Set the data storer callback\n * Called to store received records after successful migration\n */\n public setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void {\n this.dataStorer = storer;\n }\n\n // ============================================\n // Migration Planning\n // ============================================\n\n /**\n * Plan migration for topology change\n */\n public planMigration(\n oldDistribution: Map<number, PartitionDistribution>,\n newDistribution: Map<number, PartitionDistribution>\n ): void {\n const migrations: PartitionMigration[] = [];\n\n for (const [partitionId, newDist] of newDistribution) {\n const oldDist = oldDistribution.get(partitionId);\n const oldOwner = oldDist?.owner ?? this.clusterManager.config.nodeId;\n const newOwner = newDist.owner;\n\n // Only plan migration if owner changed AND we are the source\n if (oldOwner !== newOwner && oldOwner === this.clusterManager.config.nodeId) {\n migrations.push({\n partitionId,\n state: PartitionState.STABLE,\n sourceNode: oldOwner,\n targetNode: newOwner,\n startTime: 0,\n bytesTransferred: 0,\n totalBytes: 0,\n retryCount: 0,\n });\n }\n }\n\n // Sort by partition ID for deterministic ordering\n migrations.sort((a, b) => a.partitionId - b.partitionId);\n\n this.migrationQueue = migrations;\n this.metrics.queuedMigrations = migrations.length;\n\n logger.info({ total: migrations.length }, 'Migration planned');\n this.emit('migrationPlanned', { total: migrations.length });\n\n // Start processing if we have migrations\n if (migrations.length > 0) {\n this.startBatchProcessing();\n }\n }\n\n /**\n * Start batch processing timer\n */\n private startBatchProcessing(): void {\n if (this.batchTimer) return;\n\n // Process first batch immediately\n this.startNextBatch().catch(err => {\n logger.error({ error: err }, 'Failed to start first migration batch');\n this.emit('error', err);\n });\n\n // Then process batches on interval\n this.batchTimer = setInterval(() => {\n this.startNextBatch().catch(err => {\n logger.error({ error: err }, 'Failed to start migration batch');\n this.emit('error', err);\n });\n }, this.config.batchIntervalMs);\n }\n\n /**\n * Stop batch processing\n */\n private stopBatchProcessing(): void {\n if (this.batchTimer) {\n clearInterval(this.batchTimer);\n this.batchTimer = null;\n }\n }\n\n /**\n * Start next batch of migrations\n */\n public async startNextBatch(): Promise<void> {\n // Check if we have capacity\n if (this.activeMigrations.size >= this.config.parallelTransfers) {\n return; // Wait for current batch to complete\n }\n\n const slotsAvailable = this.config.parallelTransfers - this.activeMigrations.size;\n const batch = this.migrationQueue.splice(0, Math.min(slotsAvailable, this.config.batchSize));\n\n if (batch.length === 0) {\n // No more migrations, stop timer\n if (this.migrationQueue.length === 0 && this.activeMigrations.size === 0) {\n this.stopBatchProcessing();\n }\n return;\n }\n\n for (const migration of batch) {\n migration.state = PartitionState.MIGRATING;\n migration.startTime = Date.now();\n this.activeMigrations.set(migration.partitionId, migration);\n this.metrics.migrationsStarted++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n this.metrics.queuedMigrations = this.migrationQueue.length;\n\n // Start async migration\n this.startPartitionMigration(migration).catch(error => {\n this.onMigrationFailed(migration.partitionId, error);\n });\n }\n\n logger.info({ count: batch.length, remaining: this.migrationQueue.length }, 'Batch started');\n this.emit('batchStarted', { count: batch.length, remaining: this.migrationQueue.length });\n }\n\n // ============================================\n // Migration Execution\n // ============================================\n\n /**\n * Start migration for a single partition\n */\n private async startPartitionMigration(migration: PartitionMigration): Promise<void> {\n const { partitionId, targetNode } = migration;\n\n logger.info({ partitionId, targetNode }, 'Starting partition migration');\n\n // 1. Collect all records for partition\n let records: Uint8Array[];\n if (this.dataCollector) {\n records = await this.dataCollector(partitionId);\n } else {\n // No data collector set, send empty migration\n records = [];\n }\n\n migration.totalBytes = records.reduce((sum, r) => sum + r.length, 0);\n\n // 2. Send start message\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_START',\n payload: {\n partitionId,\n sourceNode: this.clusterManager.config.nodeId,\n estimatedSize: migration.totalBytes,\n },\n },\n });\n\n // 3. Stream chunks with backpressure\n const chunks = this.chunkify(records);\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n const checksum = this.calculateChecksum(chunk);\n\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_CHUNK',\n payload: {\n partitionId,\n chunkIndex: i,\n totalChunks: chunks.length,\n data: Array.from(chunk), // Convert Uint8Array to array for JSON serialization\n checksum,\n },\n },\n });\n\n // Wait for acknowledgment (backpressure)\n await this.waitForChunkAck(partitionId, i);\n\n migration.bytesTransferred += chunk.length;\n this.metrics.chunksTransferred++;\n this.metrics.bytesTransferred += chunk.length;\n this.emit('migrationProgress', migration);\n }\n\n // 4. Send completion and wait for verification\n const fullChecksum = this.calculatePartitionChecksum(records);\n\n migration.state = PartitionState.SYNC;\n\n this.clusterManager.send(targetNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_COMPLETE',\n payload: {\n partitionId,\n totalRecords: records.length,\n checksum: fullChecksum,\n },\n },\n });\n\n // 5. Wait for verification\n const verified = await this.waitForVerification(partitionId);\n\n if (verified) {\n await this.onMigrationComplete(partitionId);\n } else {\n throw new Error(`Migration verification failed for partition ${partitionId}`);\n }\n }\n\n /**\n * Split records into chunks\n */\n private chunkify(records: Uint8Array[]): Uint8Array[] {\n const chunks: Uint8Array[] = [];\n let currentChunk: number[] = [];\n let currentSize = 0;\n\n for (const record of records) {\n // Add record length prefix (4 bytes)\n const lengthPrefix = new Uint8Array(4);\n new DataView(lengthPrefix.buffer).setUint32(0, record.length, true);\n\n currentChunk.push(...lengthPrefix, ...record);\n currentSize += 4 + record.length;\n\n if (currentSize >= this.config.transferChunkSize) {\n chunks.push(new Uint8Array(currentChunk));\n currentChunk = [];\n currentSize = 0;\n }\n }\n\n // Add remaining data as last chunk\n if (currentChunk.length > 0) {\n chunks.push(new Uint8Array(currentChunk));\n }\n\n // Ensure at least one chunk (even if empty)\n if (chunks.length === 0) {\n chunks.push(new Uint8Array(0));\n }\n\n return chunks;\n }\n\n /**\n * Calculate checksum for a chunk using native xxhash\n */\n private calculateChecksum(data: Uint8Array): string {\n return String(xxhash64AsNumber(data));\n }\n\n /**\n * Calculate checksum for all partition records using streaming xxhash\n */\n private calculatePartitionChecksum(records: Uint8Array[]): string {\n const state = createXxHash64State();\n for (const record of records) {\n state.update(record);\n }\n return String(state.digestAsNumber());\n }\n\n /**\n * Wait for chunk acknowledgment\n */\n private waitForChunkAck(partitionId: number, chunkIndex: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const key = `${partitionId}:${chunkIndex}`;\n\n const timeout = setTimeout(() => {\n this.pendingChunkAcks.delete(key);\n reject(new Error(`Chunk ack timeout for partition ${partitionId}, chunk ${chunkIndex}`));\n }, this.config.syncTimeoutMs);\n\n this.pendingChunkAcks.set(key, { resolve, reject, timeout });\n });\n }\n\n /**\n * Wait for migration verification\n */\n private waitForVerification(partitionId: number): Promise<boolean> {\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n this.pendingVerifications.delete(partitionId);\n resolve(false); // Verification timed out\n }, this.config.syncTimeoutMs);\n\n this.pendingVerifications.set(partitionId, { resolve, timeout });\n });\n }\n\n // ============================================\n // Migration Completion\n // ============================================\n\n /**\n * Handle successful migration completion\n */\n private async onMigrationComplete(partitionId: number): Promise<void> {\n const migration = this.activeMigrations.get(partitionId);\n if (!migration) return;\n\n migration.state = PartitionState.STABLE;\n this.activeMigrations.delete(partitionId);\n\n this.metrics.migrationsCompleted++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.info({\n partitionId,\n duration: Date.now() - migration.startTime,\n bytesTransferred: migration.bytesTransferred,\n }, 'Migration completed');\n\n this.emit('migrationComplete', partitionId);\n }\n\n /**\n * Handle migration failure\n */\n private async onMigrationFailed(partitionId: number, error: Error): Promise<void> {\n const migration = this.activeMigrations.get(partitionId);\n if (!migration) return;\n\n migration.retryCount++;\n\n if (migration.retryCount <= this.config.maxRetries) {\n // Requeue for retry\n migration.state = PartitionState.STABLE;\n migration.bytesTransferred = 0;\n this.activeMigrations.delete(partitionId);\n this.migrationQueue.unshift(migration); // Add to front of queue\n this.metrics.queuedMigrations = this.migrationQueue.length;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.warn({\n partitionId,\n retryCount: migration.retryCount,\n error: error.message,\n }, 'Migration failed, will retry');\n } else {\n // Max retries exceeded\n migration.state = PartitionState.FAILED;\n this.activeMigrations.delete(partitionId);\n this.metrics.migrationsFailed++;\n this.metrics.activeMigrations = this.activeMigrations.size;\n\n logger.error({\n partitionId,\n retryCount: migration.retryCount,\n error: error.message,\n }, 'Migration failed permanently');\n\n this.emit('migrationFailed', partitionId, error);\n }\n }\n\n // ============================================\n // Incoming Migration Handlers (Target Node)\n // ============================================\n\n /**\n * Handle MIGRATION_START message\n */\n private handleMigrationStart(payload: { partitionId: number; sourceNode: string; estimatedSize: number }): void {\n const { partitionId, sourceNode, estimatedSize } = payload;\n\n logger.info({ partitionId, sourceNode, estimatedSize }, 'Receiving migration');\n\n this.incomingMigrations.set(partitionId, {\n sourceNode,\n chunks: [],\n expectedSize: estimatedSize,\n receivedSize: 0,\n startTime: Date.now(),\n });\n }\n\n /**\n * Handle MIGRATION_CHUNK message\n */\n private handleMigrationChunk(payload: {\n partitionId: number;\n chunkIndex: number;\n totalChunks: number;\n data: number[];\n checksum: string;\n }): void {\n const { partitionId, chunkIndex, data, checksum } = payload;\n const incoming = this.incomingMigrations.get(partitionId);\n\n if (!incoming) {\n logger.warn({ partitionId, chunkIndex }, 'Received chunk for unknown migration');\n return;\n }\n\n const chunkData = new Uint8Array(data);\n\n // Verify chunk checksum\n const actualChecksum = this.calculateChecksum(chunkData);\n const success = actualChecksum === checksum;\n\n if (success) {\n // Store chunk\n incoming.chunks[chunkIndex] = chunkData;\n incoming.receivedSize += chunkData.length;\n } else {\n logger.warn({ partitionId, chunkIndex, expected: checksum, actual: actualChecksum }, 'Chunk checksum mismatch');\n }\n\n // Send acknowledgment\n this.clusterManager.send(incoming.sourceNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_CHUNK_ACK',\n payload: {\n partitionId,\n chunkIndex,\n success,\n },\n },\n });\n }\n\n /**\n * Handle MIGRATION_COMPLETE message\n */\n private async handleMigrationComplete(payload: {\n partitionId: number;\n totalRecords: number;\n checksum: string;\n }): Promise<void> {\n const { partitionId, totalRecords, checksum } = payload;\n const incoming = this.incomingMigrations.get(partitionId);\n\n if (!incoming) {\n logger.warn({ partitionId }, 'Received complete for unknown migration');\n return;\n }\n\n // Reassemble data\n const allData = this.reassemble(incoming.chunks);\n const records = this.deserializeRecords(allData);\n\n // Verify checksum\n const actualChecksum = this.calculatePartitionChecksum(records);\n const checksumMatch = actualChecksum === checksum;\n const success = checksumMatch && records.length === totalRecords;\n\n if (success && this.dataStorer) {\n // Store records\n await this.dataStorer(partitionId, records);\n }\n\n logger.info({\n partitionId,\n duration: Date.now() - incoming.startTime,\n records: records.length,\n checksumMatch,\n }, 'Migration received');\n\n // Send verification result\n this.clusterManager.send(incoming.sourceNode, 'OP_FORWARD', {\n _migration: {\n type: 'MIGRATION_VERIFY',\n payload: {\n partitionId,\n success,\n checksumMatch,\n },\n },\n });\n\n this.incomingMigrations.delete(partitionId);\n }\n\n /**\n * Handle MIGRATION_CHUNK_ACK message\n */\n private handleMigrationChunkAck(payload: {\n partitionId: number;\n chunkIndex: number;\n success: boolean;\n }): void {\n const { partitionId, chunkIndex, success } = payload;\n const key = `${partitionId}:${chunkIndex}`;\n const pending = this.pendingChunkAcks.get(key);\n\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingChunkAcks.delete(key);\n\n if (success) {\n pending.resolve();\n } else {\n pending.reject(new Error(`Chunk ${chunkIndex} rejected by target`));\n }\n }\n }\n\n /**\n * Handle MIGRATION_VERIFY message\n */\n private handleMigrationVerify(payload: {\n partitionId: number;\n success: boolean;\n checksumMatch: boolean;\n }): void {\n const { partitionId, success } = payload;\n const pending = this.pendingVerifications.get(partitionId);\n\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingVerifications.delete(partitionId);\n pending.resolve(success);\n }\n }\n\n /**\n * Reassemble chunks into continuous data\n */\n private reassemble(chunks: Uint8Array[]): Uint8Array {\n const totalLength = chunks.reduce((sum, c) => sum + (c?.length ?? 0), 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n if (chunk) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n }\n\n return result;\n }\n\n /**\n * Deserialize records from chunk data\n */\n private deserializeRecords(data: Uint8Array): Uint8Array[] {\n const records: Uint8Array[] = [];\n let offset = 0;\n\n while (offset < data.length) {\n if (offset + 4 > data.length) break;\n\n const length = new DataView(data.buffer, data.byteOffset + offset, 4).getUint32(0, true);\n offset += 4;\n\n if (offset + length > data.length) break;\n\n records.push(data.slice(offset, offset + length));\n offset += length;\n }\n\n return records;\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n /**\n * Setup cluster message handlers\n */\n private setupMessageHandlers(): void {\n this.clusterManager.on('message', (msg: ClusterMessage) => {\n if (msg.payload?._migration) {\n const migration = msg.payload._migration;\n\n switch (migration.type) {\n case 'MIGRATION_START':\n this.handleMigrationStart(migration.payload);\n break;\n case 'MIGRATION_CHUNK':\n this.handleMigrationChunk(migration.payload);\n break;\n case 'MIGRATION_COMPLETE':\n this.handleMigrationComplete(migration.payload).catch(err => {\n logger.error({ error: err }, 'Error handling migration complete');\n });\n break;\n case 'MIGRATION_CHUNK_ACK':\n this.handleMigrationChunkAck(migration.payload);\n break;\n case 'MIGRATION_VERIFY':\n this.handleMigrationVerify(migration.payload);\n break;\n }\n }\n });\n }\n\n // ============================================\n // Status and Metrics\n // ============================================\n\n /**\n * Check if a partition is currently migrating\n */\n public isActive(partitionId: number): boolean {\n return this.activeMigrations.has(partitionId) || this.incomingMigrations.has(partitionId);\n }\n\n /**\n * Get migration status\n */\n public getStatus(): MigrationStatus {\n const avgMigrationTime = this.metrics.migrationsCompleted > 0\n ? (Date.now() - (this.activeMigrations.values().next().value?.startTime ?? Date.now()))\n : 0;\n\n const estimatedTimeRemainingMs =\n (this.migrationQueue.length + this.activeMigrations.size) *\n (avgMigrationTime || 1000); // Default to 1s if no data\n\n return {\n inProgress: this.activeMigrations.size > 0 || this.migrationQueue.length > 0,\n active: Array.from(this.activeMigrations.values()),\n queued: this.migrationQueue.length,\n completed: this.metrics.migrationsCompleted,\n failed: this.metrics.migrationsFailed,\n estimatedTimeRemainingMs,\n };\n }\n\n /**\n * Get migration metrics\n */\n public getMetrics(): MigrationMetrics {\n return { ...this.metrics };\n }\n\n /**\n * Cancel all active and queued migrations\n */\n public async cancelAll(): Promise<void> {\n this.stopBatchProcessing();\n\n // Clear queued migrations\n this.migrationQueue = [];\n this.metrics.queuedMigrations = 0;\n\n // Mark active migrations as failed\n for (const [partitionId, migration] of this.activeMigrations) {\n migration.state = PartitionState.FAILED;\n this.metrics.migrationsFailed++;\n this.emit('migrationFailed', partitionId, new Error('Migration cancelled'));\n }\n\n this.activeMigrations.clear();\n this.metrics.activeMigrations = 0;\n\n // Clear pending acks\n for (const pending of this.pendingChunkAcks.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Migration cancelled'));\n }\n this.pendingChunkAcks.clear();\n\n // Clear pending verifications\n for (const pending of this.pendingVerifications.values()) {\n clearTimeout(pending.timeout);\n pending.resolve(false);\n }\n this.pendingVerifications.clear();\n\n // Clear incoming migrations\n this.incomingMigrations.clear();\n\n logger.info('All migrations cancelled');\n }\n\n /**\n * Cleanup resources (sync version for backwards compatibility)\n */\n public close(): void {\n this.cancelAll();\n }\n\n /**\n * Async cleanup - waits for cancellation to complete\n */\n public async closeAsync(): Promise<void> {\n await this.cancelAll();\n this.removeAllListeners();\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface LockRequest {\n clientId: string;\n requestId: string;\n ttl: number;\n timestamp: number;\n}\n\nexport interface LockState {\n name: string;\n owner: string; // clientId\n fencingToken: number;\n expiry: number;\n queue: LockRequest[];\n}\n\nexport class LockManager extends EventEmitter {\n private locks: Map<string, LockState> = new Map();\n private checkInterval: NodeJS.Timeout;\n\n private static readonly MIN_TTL = 1000; // 1 second\n private static readonly MAX_TTL = 300000; // 5 minutes\n\n constructor() {\n super();\n this.checkInterval = setInterval(() => this.cleanupExpiredLocks(), 1000);\n }\n\n public stop() {\n clearInterval(this.checkInterval);\n }\n\n public acquire(name: string, clientId: string, requestId: string, ttl: number): { granted: boolean; fencingToken?: number; error?: string } {\n // Validate TTL\n const safeTtl = Math.max(LockManager.MIN_TTL, Math.min(ttl || LockManager.MIN_TTL, LockManager.MAX_TTL));\n\n let lock = this.locks.get(name);\n if (!lock) {\n lock = {\n name,\n owner: '',\n fencingToken: 0,\n expiry: 0,\n queue: []\n };\n this.locks.set(name, lock);\n }\n\n const now = Date.now();\n\n // If lock is free or expired\n if (!lock.owner || lock.expiry < now) {\n this.grantLock(lock, clientId, safeTtl);\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // If already owned by same client, extend lease\n if (lock.owner === clientId) {\n lock.expiry = Math.max(lock.expiry, now + safeTtl);\n logger.info({ name, clientId, fencingToken: lock.fencingToken }, 'Lock lease extended');\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // Queue request\n lock.queue.push({ clientId, requestId, ttl: safeTtl, timestamp: now });\n logger.info({ name, clientId, queueLength: lock.queue.length }, 'Lock queued');\n return { granted: false };\n }\n\n public release(name: string, clientId: string, fencingToken: number): boolean {\n const lock = this.locks.get(name);\n if (!lock) return false;\n\n if (lock.owner !== clientId) {\n logger.warn({ name, clientId, owner: lock.owner }, 'Release failed: Not owner');\n return false;\n }\n\n if (lock.fencingToken !== fencingToken) {\n logger.warn({ name, clientId, sentToken: fencingToken, actualToken: lock.fencingToken }, 'Release failed: Token mismatch');\n return false;\n }\n\n this.processNext(lock);\n return true;\n }\n\n public handleClientDisconnect(clientId: string) {\n for (const lock of this.locks.values()) {\n // 1. If client owns the lock, force release\n if (lock.owner === clientId) {\n logger.info({ name: lock.name, clientId }, 'Releasing lock due to disconnect');\n this.processNext(lock);\n } else {\n // 2. Remove from queue if present\n const initialLen = lock.queue.length;\n lock.queue = lock.queue.filter(req => req.clientId !== clientId);\n if (lock.queue.length < initialLen) {\n logger.info({ name: lock.name, clientId }, 'Removed from lock queue due to disconnect');\n }\n }\n }\n }\n\n private grantLock(lock: LockState, clientId: string, ttl: number) {\n lock.owner = clientId;\n lock.expiry = Date.now() + ttl;\n lock.fencingToken++;\n logger.info({ name: lock.name, clientId, fencingToken: lock.fencingToken }, 'Lock granted');\n }\n\n private processNext(lock: LockState) {\n const now = Date.now();\n \n // Reset owner\n lock.owner = '';\n lock.expiry = 0;\n\n // Process queue\n while (lock.queue.length > 0) {\n const next = lock.queue.shift()!;\n \n // Grant to next\n this.grantLock(lock, next.clientId, next.ttl);\n \n // Emit event so ServerCoordinator can notify the client\n this.emit('lockGranted', {\n clientId: next.clientId,\n requestId: next.requestId,\n name: lock.name,\n fencingToken: lock.fencingToken\n });\n \n return;\n }\n \n // No one waiting\n if (lock.queue.length === 0) {\n this.locks.delete(lock.name);\n }\n }\n\n private cleanupExpiredLocks() {\n const now = Date.now();\n // Use a copy of keys to avoid concurrent modification issues during iteration\n const lockNames = Array.from(this.locks.keys());\n \n for (const name of lockNames) {\n const lock = this.locks.get(name);\n if (!lock) continue;\n\n if (lock.owner && lock.expiry < now) {\n logger.info({ name: lock.name, owner: lock.owner }, 'Lock expired, processing next');\n this.processNext(lock);\n } else if (!lock.owner && lock.queue.length === 0) {\n // Cleanup empty orphaned locks\n this.locks.delete(name);\n }\n }\n }\n}\n\n","import { PermissionPolicy, Principal, PermissionType } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class SecurityManager {\n private policies: PermissionPolicy[] = [];\n\n constructor(policies: PermissionPolicy[] = []) {\n this.policies = policies;\n }\n\n public addPolicy(policy: PermissionPolicy) {\n this.policies.push(policy);\n }\n\n public checkPermission(principal: Principal, mapName: string, action: PermissionType): boolean {\n // 1. Superuser check (optional, but good practice)\n if (principal.roles.includes('ADMIN')) {\n return true;\n }\n\n // 2. System Map Protection\n if (mapName.startsWith('$sys/')) {\n logger.warn({ userId: principal.userId, mapName }, 'Access Denied: System Map requires ADMIN role');\n return false;\n }\n\n // 2. Iterate policies to find a match\n for (const policy of this.policies) {\n const hasRole = this.hasRole(principal, policy.role);\n const matchesMap = this.matchesMap(mapName, policy.mapNamePattern, principal);\n\n if (hasRole && matchesMap) {\n if (policy.actions.includes('ALL') || policy.actions.includes(action)) {\n return true;\n }\n } else {\n // Trace why it failed matching if needed (verbose)\n // logger.trace({ policy, hasRole, matchesMap, mapName, user: principal.userId }, 'Policy mismatch');\n }\n }\n\n logger.warn({\n userId: principal.userId,\n roles: principal.roles,\n mapName,\n action,\n policyCount: this.policies.length\n }, 'SecurityManager: Access Denied - No matching policy found');\n\n return false;\n }\n\n public filterObject(object: any, principal: Principal, mapName: string): any {\n if (!object || typeof object !== 'object') return object;\n if (principal.roles.includes('ADMIN')) return object;\n\n if (Array.isArray(object)) {\n return object.map(item => this.filterObject(item, principal, mapName));\n }\n\n let allowedFields: Set<string> | null = null;\n let accessGranted = false;\n\n for (const policy of this.policies) {\n if (this.hasRole(principal, policy.role) && this.matchesMap(mapName, policy.mapNamePattern, principal)) {\n if (policy.actions.includes('ALL') || policy.actions.includes('READ')) {\n accessGranted = true;\n\n // If any policy allows everything, return immediately\n if (!policy.allowedFields || policy.allowedFields.length === 0 || policy.allowedFields.includes('*')) {\n return object;\n }\n\n if (allowedFields === null) allowedFields = new Set();\n policy.allowedFields.forEach(f => allowedFields!.add(f));\n }\n }\n }\n\n if (!accessGranted) return null;\n if (allowedFields === null) return object; // Should have returned above, but as fallback\n\n const filtered: any = {};\n for (const key of Object.keys(object)) {\n if (allowedFields.has(key)) {\n filtered[key] = object[key];\n }\n }\n return filtered;\n }\n\n private hasRole(principal: Principal, role: string): boolean {\n return principal.roles.includes(role);\n }\n\n private matchesMap(mapName: string, pattern: string, principal?: Principal): boolean {\n // Dynamic substitution for {userId}\n let finalPattern = pattern;\n if (pattern.includes('{userId}') && principal) {\n finalPattern = pattern.replace('{userId}', principal.userId);\n }\n\n if (finalPattern === '*') return true;\n if (finalPattern === mapName) return true;\n\n if (finalPattern.endsWith('*')) {\n const prefix = finalPattern.slice(0, -1);\n return mapName.startsWith(prefix);\n }\n\n return false;\n }\n}\n","import { Registry, Gauge, Counter, Summary, collectDefaultMetrics } from 'prom-client';\n\nexport class MetricsService {\n public readonly registry: Registry;\n\n // Metrics\n private connectedClients: Gauge;\n private mapSizeItems: Gauge;\n private opsTotal: Counter;\n private memoryUsage: Gauge;\n private clusterMembers: Gauge;\n\n // Subscription-based routing metrics\n private eventsRoutedTotal: Counter;\n private eventsFilteredBySubscription: Counter;\n private subscribersPerEvent: Summary;\n\n // Bounded event queue metrics\n private eventQueueSize: Gauge;\n private eventQueueEnqueued: Counter;\n private eventQueueDequeued: Counter;\n private eventQueueRejected: Counter;\n\n // Backpressure metrics\n private backpressureSyncForcedTotal: Counter;\n private backpressurePendingOps: Gauge;\n private backpressureWaitsTotal: Counter;\n private backpressureTimeoutsTotal: Counter;\n\n // Connection scaling metrics\n private connectionsAcceptedTotal: Counter;\n private connectionsRejectedTotal: Counter;\n private connectionsPending: Gauge;\n private connectionRatePerSecond: Gauge;\n\n constructor() {\n this.registry = new Registry();\n\n // Enable default nodejs metrics (cpu, memory, etc.)\n collectDefaultMetrics({ register: this.registry, prefix: 'topgun_' });\n\n this.connectedClients = new Gauge({\n name: 'topgun_connected_clients',\n help: 'Number of currently connected clients',\n registers: [this.registry],\n });\n\n this.mapSizeItems = new Gauge({\n name: 'topgun_map_size_items',\n help: 'Number of items in a map',\n labelNames: ['map'],\n registers: [this.registry],\n });\n\n this.opsTotal = new Counter({\n name: 'topgun_ops_total',\n help: 'Total number of operations',\n labelNames: ['type', 'map'],\n registers: [this.registry],\n });\n\n this.memoryUsage = new Gauge({\n name: 'topgun_memory_usage_bytes',\n help: 'Current memory usage in bytes',\n registers: [this.registry],\n collect() {\n this.set(process.memoryUsage().heapUsed);\n }\n });\n\n this.clusterMembers = new Gauge({\n name: 'topgun_cluster_members',\n help: 'Number of active cluster members',\n registers: [this.registry],\n });\n\n // === Subscription-based routing metrics ===\n this.eventsRoutedTotal = new Counter({\n name: 'topgun_events_routed_total',\n help: 'Total number of events processed for routing',\n registers: [this.registry],\n });\n\n this.eventsFilteredBySubscription = new Counter({\n name: 'topgun_events_filtered_by_subscription',\n help: 'Events NOT sent due to no active subscriptions',\n registers: [this.registry],\n });\n\n this.subscribersPerEvent = new Summary({\n name: 'topgun_subscribers_per_event',\n help: 'Distribution of subscribers per event',\n percentiles: [0.5, 0.9, 0.99],\n registers: [this.registry],\n });\n\n // === Bounded event queue metrics ===\n this.eventQueueSize = new Gauge({\n name: 'topgun_event_queue_size',\n help: 'Current size of the event queue across all stripes',\n labelNames: ['stripe'],\n registers: [this.registry],\n });\n\n this.eventQueueEnqueued = new Counter({\n name: 'topgun_event_queue_enqueued_total',\n help: 'Total number of events enqueued',\n registers: [this.registry],\n });\n\n this.eventQueueDequeued = new Counter({\n name: 'topgun_event_queue_dequeued_total',\n help: 'Total number of events dequeued',\n registers: [this.registry],\n });\n\n this.eventQueueRejected = new Counter({\n name: 'topgun_event_queue_rejected_total',\n help: 'Total number of events rejected due to queue capacity',\n registers: [this.registry],\n });\n\n // === Backpressure metrics ===\n this.backpressureSyncForcedTotal = new Counter({\n name: 'topgun_backpressure_sync_forced_total',\n help: 'Total number of times sync processing was forced',\n registers: [this.registry],\n });\n\n this.backpressurePendingOps = new Gauge({\n name: 'topgun_backpressure_pending_ops',\n help: 'Current number of pending async operations',\n registers: [this.registry],\n });\n\n this.backpressureWaitsTotal = new Counter({\n name: 'topgun_backpressure_waits_total',\n help: 'Total number of times had to wait for capacity',\n registers: [this.registry],\n });\n\n this.backpressureTimeoutsTotal = new Counter({\n name: 'topgun_backpressure_timeouts_total',\n help: 'Total number of backpressure timeouts',\n registers: [this.registry],\n });\n\n // === Connection scaling metrics ===\n this.connectionsAcceptedTotal = new Counter({\n name: 'topgun_connections_accepted_total',\n help: 'Total number of connections accepted',\n registers: [this.registry],\n });\n\n this.connectionsRejectedTotal = new Counter({\n name: 'topgun_connections_rejected_total',\n help: 'Total number of connections rejected due to rate limiting',\n registers: [this.registry],\n });\n\n this.connectionsPending = new Gauge({\n name: 'topgun_connections_pending',\n help: 'Number of connections currently pending (handshake in progress)',\n registers: [this.registry],\n });\n\n this.connectionRatePerSecond = new Gauge({\n name: 'topgun_connection_rate_per_second',\n help: 'Current connection rate per second',\n registers: [this.registry],\n });\n }\n\n public destroy() {\n this.registry.clear();\n }\n\n public setConnectedClients(count: number) {\n this.connectedClients.set(count);\n }\n\n public setMapSize(mapName: string, size: number) {\n this.mapSizeItems.set({ map: mapName }, size);\n }\n\n public incOp(type: 'PUT' | 'GET' | 'DELETE' | 'SUBSCRIBE', mapName: string) {\n this.opsTotal.inc({ type, map: mapName });\n }\n\n public setClusterMembers(count: number) {\n this.clusterMembers.set(count);\n }\n\n // === Subscription-based routing metric methods ===\n\n /**\n * Increment counter for total events processed for routing.\n */\n public incEventsRouted(): void {\n this.eventsRoutedTotal.inc();\n }\n\n /**\n * Increment counter for events filtered out due to no subscribers.\n */\n public incEventsFilteredBySubscription(): void {\n this.eventsFilteredBySubscription.inc();\n }\n\n /**\n * Record the number of subscribers for an event (for average calculation).\n */\n public recordSubscribersPerEvent(count: number): void {\n this.subscribersPerEvent.observe(count);\n }\n\n // === Bounded event queue metric methods ===\n\n /**\n * Set the current size of a specific queue stripe.\n */\n public setEventQueueSize(stripe: number, size: number): void {\n this.eventQueueSize.set({ stripe: String(stripe) }, size);\n }\n\n /**\n * Increment counter for events enqueued.\n */\n public incEventQueueEnqueued(): void {\n this.eventQueueEnqueued.inc();\n }\n\n /**\n * Increment counter for events dequeued.\n */\n public incEventQueueDequeued(): void {\n this.eventQueueDequeued.inc();\n }\n\n /**\n * Increment counter for events rejected due to queue capacity.\n */\n public incEventQueueRejected(): void {\n this.eventQueueRejected.inc();\n }\n\n // === Backpressure metric methods ===\n\n /**\n * Increment counter for forced sync operations.\n */\n public incBackpressureSyncForced(): void {\n this.backpressureSyncForcedTotal.inc();\n }\n\n /**\n * Set the current number of pending async operations.\n */\n public setBackpressurePendingOps(count: number): void {\n this.backpressurePendingOps.set(count);\n }\n\n /**\n * Increment counter for times had to wait for capacity.\n */\n public incBackpressureWaits(): void {\n this.backpressureWaitsTotal.inc();\n }\n\n /**\n * Increment counter for backpressure timeouts.\n */\n public incBackpressureTimeouts(): void {\n this.backpressureTimeoutsTotal.inc();\n }\n\n // === Connection scaling metric methods ===\n\n /**\n * Increment counter for accepted connections.\n */\n public incConnectionsAccepted(): void {\n this.connectionsAcceptedTotal.inc();\n }\n\n /**\n * Increment counter for rejected connections.\n */\n public incConnectionsRejected(): void {\n this.connectionsRejectedTotal.inc();\n }\n\n /**\n * Set the current number of pending connections.\n */\n public setConnectionsPending(count: number): void {\n this.connectionsPending.set(count);\n }\n\n /**\n * Set the current connection rate per second.\n */\n public setConnectionRatePerSecond(rate: number): void {\n this.connectionRatePerSecond.set(rate);\n }\n\n public async getMetrics(): Promise<string> {\n return this.registry.metrics();\n }\n\n public async getMetricsJson(): Promise<Record<string, any>> {\n const metrics = await this.registry.getMetricsAsJSON();\n // Flatten or simplify for dashboard if needed, but raw JSON is fine for now\n const result: Record<string, any> = {};\n for (const metric of metrics) {\n // Simple flattening: name -> value (if single value)\n // metric.type is an enum/number in some versions or string in others. \n // To be safe and avoid type errors, we just check values length.\n if (metric.values.length === 1) {\n result[metric.name] = metric.values[0].value;\n } else {\n // Complex metrics\n result[metric.name] = metric.values;\n }\n }\n return result;\n }\n\n public getContentType(): string {\n return this.registry.contentType;\n }\n}\n\n","import { LWWMap, LWWRecord } from '@topgunbuild/core';\nimport { ClusterManager } from '../cluster/ClusterManager';\nimport { MetricsService } from '../monitoring/MetricsService';\nimport { logger } from '../utils/logger';\n\nexport class SystemManager {\n private cluster: ClusterManager;\n private metrics: MetricsService;\n private getMap: (name: string) => LWWMap<string, any>;\n\n private statsInterval?: NodeJS.Timeout;\n\n constructor(\n cluster: ClusterManager,\n metrics: MetricsService,\n getMap: (name: string) => LWWMap<string, any>\n ) {\n this.cluster = cluster;\n this.metrics = metrics;\n this.getMap = getMap;\n }\n\n public start() {\n this.setupClusterMap();\n this.setupStatsMap();\n this.setupMapsMap();\n\n // Update stats every 5 seconds\n this.statsInterval = setInterval(() => this.updateStats(), 5000);\n\n // Listen for cluster events\n this.cluster.on('memberJoined', () => this.updateClusterMap());\n this.cluster.on('memberLeft', () => this.updateClusterMap());\n\n // Initial updates\n this.updateClusterMap();\n this.updateStats();\n }\n\n public stop() {\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n }\n }\n\n public notifyMapCreated(mapName: string) {\n if (mapName.startsWith('$sys/')) return; // Don't track system maps\n this.updateMapsMap(mapName);\n }\n\n private setupClusterMap() {\n // Ensure map exists\n this.getMap('$sys/cluster');\n }\n\n private setupStatsMap() {\n this.getMap('$sys/stats');\n }\n\n private setupMapsMap() {\n this.getMap('$sys/maps');\n }\n\n private updateClusterMap() {\n try {\n const map = this.getMap('$sys/cluster');\n const members = this.cluster.getMembers();\n\n // We can't easily \"remove\" missing members without iterating the whole map\n // For now, we just put current members.\n // A proper sync would require diffing.\n\n // In a real implementation, we might want to store more info than just ID.\n // But ClusterManager currently only gives us IDs easily or we have to look them up.\n // Let's iterate members map from ClusterManager if possible, or just use IDs.\n\n // Accessing private members map via any cast for now or just using IDs\n // The ClusterManager.getMembers() returns IDs.\n\n for (const memberId of members) {\n const isLocal = this.cluster.isLocal(memberId);\n map.set(memberId, {\n id: memberId,\n status: 'UP',\n isLocal,\n lastUpdated: Date.now()\n });\n }\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/cluster');\n }\n }\n\n private async updateStats() {\n try {\n const map = this.getMap('$sys/stats');\n const metrics = await this.metrics.getMetricsJson(); // We need to add getMetricsJson to MetricsService\n\n map.set(this.cluster.config.nodeId, {\n ...metrics,\n timestamp: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/stats');\n }\n }\n\n private updateMapsMap(mapName: string) {\n try {\n const map = this.getMap('$sys/maps');\n map.set(mapName, {\n name: mapName,\n createdAt: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/maps');\n }\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from './logger';\n\nexport interface QueueMetrics {\n enqueued: number;\n dequeued: number;\n rejected: number;\n currentSize: number;\n}\n\nexport interface BoundedEventQueueOptions {\n maxSize: number;\n name: string;\n onReject?: (item: any) => void;\n /** High water mark percentage (0-1). Emits 'highWater' when reached. Default: 0.8 */\n highWaterMark?: number;\n}\n\nexport class BoundedEventQueue<T> extends EventEmitter {\n private queue: T[] = [];\n private readonly maxSize: number;\n private readonly name: string;\n private readonly onReject?: (item: any) => void;\n private readonly highWaterMark: number;\n private highWaterEmitted: boolean = false;\n private metrics: QueueMetrics = {\n enqueued: 0,\n dequeued: 0,\n rejected: 0,\n currentSize: 0\n };\n\n constructor(options: BoundedEventQueueOptions) {\n super();\n this.maxSize = options.maxSize;\n this.name = options.name;\n this.onReject = options.onReject;\n this.highWaterMark = options.highWaterMark ?? 0.8;\n }\n\n /**\n * Attempt to enqueue an item.\n * @returns true if enqueued, false if rejected due to capacity\n */\n enqueue(item: T): boolean {\n if (this.queue.length >= this.maxSize) {\n this.metrics.rejected++;\n logger.warn(\n { queue: this.name, currentSize: this.queue.length, maxSize: this.maxSize },\n 'Queue full, rejecting item'\n );\n this.onReject?.(item);\n return false;\n }\n\n this.queue.push(item);\n this.metrics.enqueued++;\n this.metrics.currentSize = this.queue.length;\n\n // Check for high water mark\n const usage = this.queue.length / this.maxSize;\n if (usage >= this.highWaterMark && !this.highWaterEmitted) {\n this.highWaterEmitted = true;\n this.emit('highWater', { name: this.name, usage, size: this.queue.length });\n }\n\n // Check if queue just became full\n if (this.queue.length === this.maxSize) {\n this.emit('full', { name: this.name, size: this.queue.length });\n }\n\n return true;\n }\n\n /**\n * Dequeue an item. Returns undefined if queue is empty.\n */\n dequeue(): T | undefined {\n const item = this.queue.shift();\n\n if (item !== undefined) {\n this.metrics.dequeued++;\n this.metrics.currentSize = this.queue.length;\n\n // Reset high water mark flag when below threshold\n const usage = this.queue.length / this.maxSize;\n if (usage < this.highWaterMark) {\n this.highWaterEmitted = false;\n }\n\n // Check if queue just became empty\n if (this.queue.length === 0) {\n this.emit('empty', { name: this.name });\n }\n }\n\n return item;\n }\n\n /**\n * Peek at front item without removing.\n */\n peek(): T | undefined {\n return this.queue[0];\n }\n\n /**\n * Current queue size.\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is full.\n */\n get isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Check if queue is empty.\n */\n get isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Get queue metrics.\n */\n getMetrics(): QueueMetrics {\n return { ...this.metrics };\n }\n\n /**\n * Clear the queue.\n */\n clear(): void {\n const wasNotEmpty = this.queue.length > 0;\n this.queue = [];\n this.metrics.currentSize = 0;\n this.highWaterEmitted = false;\n\n if (wasNotEmpty) {\n this.emit('empty', { name: this.name });\n }\n }\n}\n","import { BoundedEventQueue, QueueMetrics } from './BoundedEventQueue';\nimport { logger } from './logger';\n\nexport interface StripedExecutorOptions {\n /** Number of worker stripes (default: 4) */\n stripeCount: number;\n /** Total capacity across all stripes */\n queueCapacity: number;\n name: string;\n /** Callback when a task is rejected */\n onReject?: (task: StripedTask) => void;\n}\n\nexport interface StripedTask {\n /** Used for stripe selection (hash % stripeCount) */\n key: string | number;\n /** The task to execute */\n execute: () => Promise<void> | void;\n}\n\nexport interface StripeMetrics {\n stripe: number;\n metrics: QueueMetrics;\n}\n\n/**\n * Hazelcast-style striped executor that distributes tasks across multiple bounded queues.\n * Tasks with the same key are guaranteed to be processed in order on the same stripe.\n */\nexport class StripedEventExecutor {\n private stripes: BoundedEventQueue<StripedTask>[];\n private processing: boolean[];\n private readonly stripeCount: number;\n private readonly name: string;\n private readonly onReject?: (task: StripedTask) => void;\n private isShuttingDown: boolean = false;\n private pendingPromises: Set<Promise<void>> = new Set();\n\n constructor(options: StripedExecutorOptions) {\n this.stripeCount = options.stripeCount;\n this.name = options.name;\n this.onReject = options.onReject;\n\n // Distribute capacity across stripes\n const capacityPerStripe = Math.ceil(options.queueCapacity / options.stripeCount);\n\n this.stripes = [];\n this.processing = [];\n\n for (let i = 0; i < options.stripeCount; i++) {\n const queue = new BoundedEventQueue<StripedTask>({\n maxSize: capacityPerStripe,\n name: `${options.name}-stripe-${i}`,\n onReject: (task) => this.handleReject(task)\n });\n\n // Set up event handlers for monitoring\n queue.on('highWater', (event) => {\n logger.warn(\n { executor: this.name, stripe: i, ...event },\n 'Stripe approaching capacity'\n );\n });\n\n this.stripes.push(queue);\n this.processing.push(false);\n }\n }\n\n /**\n * Submit a task. Tasks with same key go to same stripe (ordering guarantee).\n * @returns true if accepted, false if rejected\n */\n submit(task: StripedTask): boolean {\n if (this.isShuttingDown) {\n logger.warn({ executor: this.name }, 'Executor is shutting down, rejecting task');\n this.handleReject(task);\n return false;\n }\n\n const stripeIndex = this.getStripeIndex(task.key);\n const stripe = this.stripes[stripeIndex];\n\n const accepted = stripe.enqueue(task);\n\n if (accepted) {\n this.processStripe(stripeIndex);\n }\n\n return accepted;\n }\n\n /**\n * Get metrics for all stripes.\n */\n getMetrics(): StripeMetrics[] {\n return this.stripes.map((stripe, index) => ({\n stripe: index,\n metrics: stripe.getMetrics()\n }));\n }\n\n /**\n * Get aggregated metrics across all stripes.\n */\n getTotalMetrics(): QueueMetrics {\n const total: QueueMetrics = {\n enqueued: 0,\n dequeued: 0,\n rejected: 0,\n currentSize: 0\n };\n\n for (const stripe of this.stripes) {\n const metrics = stripe.getMetrics();\n total.enqueued += metrics.enqueued;\n total.dequeued += metrics.dequeued;\n total.rejected += metrics.rejected;\n total.currentSize += metrics.currentSize;\n }\n\n return total;\n }\n\n /**\n * Check if all stripes are full.\n */\n get isFull(): boolean {\n return this.stripes.every(stripe => stripe.isFull);\n }\n\n /**\n * Get current total size across all stripes.\n */\n get size(): number {\n return this.stripes.reduce((sum, stripe) => sum + stripe.size, 0);\n }\n\n /**\n * Shutdown executor, optionally waiting for pending tasks.\n */\n async shutdown(waitForPending: boolean = true): Promise<void> {\n this.isShuttingDown = true;\n\n if (waitForPending) {\n // Wait for all currently processing tasks to complete\n const promises = Array.from(this.pendingPromises);\n if (promises.length > 0) {\n await Promise.all(promises);\n }\n\n // Process any remaining items in queues\n const drainPromises: Promise<void>[] = [];\n for (let i = 0; i < this.stripeCount; i++) {\n if (!this.stripes[i].isEmpty) {\n drainPromises.push(this.drainStripe(i));\n }\n }\n await Promise.all(drainPromises);\n }\n\n // Clear all queues\n for (const stripe of this.stripes) {\n stripe.clear();\n }\n\n logger.info({ executor: this.name }, 'Executor shutdown complete');\n }\n\n private getStripeIndex(key: string | number): number {\n const hash = typeof key === 'number' ? key : this.hashString(key);\n return Math.abs(hash) % this.stripeCount;\n }\n\n private hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return hash;\n }\n\n private handleReject(task: StripedTask): void {\n this.onReject?.(task);\n }\n\n private async processStripe(stripeIndex: number): Promise<void> {\n // Prevent concurrent processing of the same stripe\n if (this.processing[stripeIndex]) {\n return;\n }\n\n this.processing[stripeIndex] = true;\n const stripe = this.stripes[stripeIndex];\n\n try {\n while (!stripe.isEmpty && !this.isShuttingDown) {\n const task = stripe.dequeue();\n if (!task) break;\n\n try {\n const result = task.execute();\n if (result instanceof Promise) {\n this.pendingPromises.add(result);\n await result;\n this.pendingPromises.delete(result);\n }\n } catch (error) {\n logger.error(\n { executor: this.name, stripe: stripeIndex, key: task.key, error },\n 'Task execution failed'\n );\n }\n }\n } finally {\n this.processing[stripeIndex] = false;\n }\n }\n\n private async drainStripe(stripeIndex: number): Promise<void> {\n const stripe = this.stripes[stripeIndex];\n\n while (!stripe.isEmpty) {\n const task = stripe.dequeue();\n if (!task) break;\n\n try {\n const result = task.execute();\n if (result instanceof Promise) {\n await result;\n }\n } catch (error) {\n logger.error(\n { executor: this.name, stripe: stripeIndex, key: task.key, error },\n 'Task execution failed during drain'\n );\n }\n }\n }\n}\n","import { logger } from './logger';\n\nexport interface BackpressureConfig {\n /**\n * How often to force sync (every N operations).\n * Default: 100\n */\n syncFrequency: number;\n\n /**\n * Maximum pending async operations before blocking.\n * Default: 1000\n */\n maxPendingOps: number;\n\n /**\n * Backoff timeout in ms when at capacity.\n * Default: 5000\n */\n backoffTimeoutMs: number;\n\n /**\n * Enable/disable backpressure.\n * Default: true\n */\n enabled: boolean;\n}\n\nexport interface BackpressureStats {\n pendingOps: number;\n syncCounter: number;\n utilizationPercent: number;\n syncForcedTotal: number;\n waitsTotal: number;\n timeoutsTotal: number;\n}\n\nconst DEFAULT_CONFIG: BackpressureConfig = {\n syncFrequency: 100,\n maxPendingOps: 1000,\n backoffTimeoutMs: 5000,\n enabled: true\n};\n\n/**\n * BackpressureRegulator implements backpressure control similar to Hazelcast's approach.\n *\n * It periodically forces synchronous processing to drain queues and prevent\n * unbounded accumulation of async work. Key features:\n *\n * 1. Sync Window: Every N operations, force sync processing\n * 2. Capacity Limiting: Block when too many operations are pending\n * 3. Randomization: Prevents thundering herd on sync points\n */\nexport class BackpressureRegulator {\n private syncCounter = 0;\n private pendingOps = 0;\n private readonly config: BackpressureConfig;\n\n // Metrics tracking\n private syncForcedTotal = 0;\n private waitsTotal = 0;\n private timeoutsTotal = 0;\n\n // Waiters queue for capacity\n private waiters: Array<{ resolve: () => void; reject: (err: Error) => void }> = [];\n\n constructor(config?: Partial<BackpressureConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Check if current operation should be forced to sync.\n * Based on Hazelcast's sync window with randomization.\n *\n * Uses randomization within the sync window to prevent all operations\n * from syncing at the same point, which could cause thundering herd.\n */\n shouldForceSync(): boolean {\n if (!this.config.enabled) {\n return false;\n }\n\n this.syncCounter++;\n\n // Randomize within the sync window to prevent thundering herd\n // Each operation has a 1/syncFrequency chance of being forced to sync\n // This distributes sync points evenly across the window\n const syncThreshold = this.config.syncFrequency;\n const shouldSync = this.syncCounter >= syncThreshold;\n\n if (shouldSync) {\n this.syncCounter = 0;\n this.syncForcedTotal++;\n logger.debug({ syncForcedTotal: this.syncForcedTotal }, 'Forcing sync operation');\n return true;\n }\n\n // Additional randomization: 10% chance of early sync if above 80% capacity\n const utilization = this.pendingOps / this.config.maxPendingOps;\n if (utilization > 0.8 && Math.random() < 0.1) {\n this.syncForcedTotal++;\n logger.debug({ utilization, pendingOps: this.pendingOps }, 'Early sync due to high utilization');\n return true;\n }\n\n return false;\n }\n\n /**\n * Register a new pending async operation.\n * @returns true if allowed, false if should wait\n */\n registerPending(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n if (this.pendingOps >= this.config.maxPendingOps) {\n return false;\n }\n\n this.pendingOps++;\n return true;\n }\n\n /**\n * Mark an async operation as complete.\n * Also notifies any waiters that capacity may be available.\n */\n completePending(): void {\n if (this.pendingOps > 0) {\n this.pendingOps--;\n }\n\n // Notify a waiter if there's capacity now\n if (this.waiters.length > 0 && this.pendingOps < this.config.maxPendingOps) {\n const waiter = this.waiters.shift();\n if (waiter) {\n waiter.resolve();\n }\n }\n }\n\n /**\n * Wait until there's capacity for new operations.\n * Throws after backoffTimeoutMs if still no capacity.\n */\n async waitForCapacity(): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n if (this.pendingOps < this.config.maxPendingOps) {\n return;\n }\n\n this.waitsTotal++;\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n // Remove from waiters queue\n const index = this.waiters.findIndex(w => w.resolve === resolve);\n if (index !== -1) {\n this.waiters.splice(index, 1);\n }\n this.timeoutsTotal++;\n reject(new Error(`Backpressure timeout after ${this.config.backoffTimeoutMs}ms`));\n }, this.config.backoffTimeoutMs);\n\n this.waiters.push({\n resolve: () => {\n clearTimeout(timeoutId);\n resolve();\n },\n reject\n });\n });\n }\n\n /**\n * Get current stats.\n */\n getStats(): BackpressureStats {\n return {\n pendingOps: this.pendingOps,\n syncCounter: this.syncCounter,\n utilizationPercent: this.config.maxPendingOps > 0\n ? (this.pendingOps / this.config.maxPendingOps) * 100\n : 0,\n syncForcedTotal: this.syncForcedTotal,\n waitsTotal: this.waitsTotal,\n timeoutsTotal: this.timeoutsTotal\n };\n }\n\n /**\n * Check if backpressure is currently active (at capacity).\n */\n isAtCapacity(): boolean {\n return this.config.enabled && this.pendingOps >= this.config.maxPendingOps;\n }\n\n /**\n * Get current pending operations count.\n */\n getPendingOps(): number {\n return this.pendingOps;\n }\n\n /**\n * Check if backpressure is enabled.\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Reset all counters and clear waiters (for testing).\n */\n reset(): void {\n this.syncCounter = 0;\n this.pendingOps = 0;\n this.syncForcedTotal = 0;\n this.waitsTotal = 0;\n this.timeoutsTotal = 0;\n\n // Reject all pending waiters\n for (const waiter of this.waiters) {\n waiter.reject(new Error('BackpressureRegulator reset'));\n }\n this.waiters = [];\n }\n}\n","import { WebSocket } from 'ws';\nimport { serialize } from '@topgunbuild/core';\nimport { BufferPool, getGlobalBufferPool } from '../memory';\n\nexport interface CoalescingWriterOptions {\n /**\n * Maximum messages to batch before forcing flush.\n * Default: 100\n */\n maxBatchSize: number;\n\n /**\n * Maximum time to wait before flushing (ms).\n * Default: 5 (similar to Nagle's algorithm)\n */\n maxDelayMs: number;\n\n /**\n * Maximum batch size in bytes.\n * Default: 65536 (64KB)\n */\n maxBatchBytes: number;\n\n /**\n * Optional BufferPool for batch buffer reuse.\n * If not provided, uses the global buffer pool.\n */\n bufferPool?: BufferPool;\n}\n\ninterface QueuedMessage {\n data: Uint8Array;\n urgent: boolean;\n}\n\n/**\n * Extended metrics for CoalescingWriter performance analysis.\n */\nexport interface CoalescingWriterMetrics {\n /** Total messages sent */\n messagesSent: number;\n /** Total batches sent */\n batchesSent: number;\n /** Total bytes sent */\n bytesSent: number;\n /** Average messages per batch */\n avgMessagesPerBatch: number;\n /** Average bytes per batch */\n avgBytesPerBatch: number;\n /** Messages currently in queue */\n pendingMessages: number;\n /** Bytes currently pending */\n pendingBytes: number;\n /** Count of flushes triggered by size limits (batch full or bytes exceeded) */\n immediateFlushes: number;\n /** Count of flushes triggered by timer expiration */\n timedFlushes: number;\n /** Ratio of actual batch size to maxBatchSize (0-1, higher = better utilization) */\n batchUtilization: number;\n /** Ratio of immediate flushes to total flushes (high = batches filling up quickly) */\n immediateFlushRatio: number;\n /** Count of pooled buffer acquisitions (for monitoring buffer pool usage) */\n pooledBuffersUsed: number;\n /** Count of oversized (non-pooled) buffers that were allocated directly */\n oversizedBuffers: number;\n}\n\n/**\n * State machine for flush scheduling.\n * Similar to Hazelcast's NioOutboundPipeline.State\n */\nenum WriterState {\n IDLE, // No pending writes\n PENDING, // Has pending writes, flush scheduled\n FLUSHING, // Currently flushing\n}\n\nconst DEFAULT_OPTIONS: CoalescingWriterOptions = {\n maxBatchSize: 100,\n maxDelayMs: 5,\n maxBatchBytes: 65536,\n};\n\n/**\n * Per-connection write coalescing that batches multiple messages into single WebSocket frames.\n * Inspired by Hazelcast's NioOutboundPipeline batching strategy.\n */\nexport class CoalescingWriter {\n private socket: WebSocket;\n private queue: QueuedMessage[] = [];\n private urgentQueue: QueuedMessage[] = []; // Priority queue for urgent messages\n private pendingBytes = 0;\n private flushTimer: NodeJS.Immediate | null = null;\n private delayTimer: NodeJS.Timeout | null = null;\n private state: WriterState = WriterState.IDLE;\n private readonly options: CoalescingWriterOptions;\n private readonly bufferPool: BufferPool;\n private closed = false;\n\n // Metrics\n private messagesSent = 0;\n private batchesSent = 0;\n private bytesSent = 0;\n private immediateFlushCount = 0; // Size-triggered flushes\n private timedFlushCount = 0; // Timer-triggered flushes\n private pooledBuffersUsed = 0; // Count of pooled buffer acquisitions\n private oversizedBuffers = 0; // Count of oversized (non-pooled) buffers\n\n constructor(socket: WebSocket, options?: Partial<CoalescingWriterOptions>) {\n this.socket = socket;\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.bufferPool = options?.bufferPool ?? getGlobalBufferPool();\n }\n\n /**\n * Queue a message for sending.\n * @param message - The message object to serialize and send\n * @param urgent - If true, bypass batching and send immediately\n */\n write(message: any, urgent?: boolean): void {\n if (this.closed) {\n return;\n }\n\n const data = serialize(message);\n this.writeRaw(data, urgent);\n }\n\n /**\n * Queue pre-serialized data.\n */\n writeRaw(data: Uint8Array, urgent?: boolean): void {\n if (this.closed) {\n return;\n }\n\n if (urgent) {\n // Urgent messages bypass batching entirely\n this.sendImmediate(data);\n return;\n }\n\n const msg: QueuedMessage = { data, urgent: false };\n this.queue.push(msg);\n this.pendingBytes += data.length;\n\n // Check if we should flush immediately due to size limits\n if (this.queue.length >= this.options.maxBatchSize ||\n this.pendingBytes >= this.options.maxBatchBytes) {\n this.immediateFlushCount++;\n this.flush();\n return;\n }\n\n // Schedule flush if not already scheduled\n this.scheduleFlush();\n }\n\n /**\n * Force flush all pending messages immediately.\n */\n flush(): void {\n if (this.closed) {\n return;\n }\n\n // Cancel any pending timers\n this.cancelTimers();\n\n // Nothing to flush\n if (this.queue.length === 0) {\n this.state = WriterState.IDLE;\n return;\n }\n\n this.state = WriterState.FLUSHING;\n\n try {\n if (this.socket.readyState !== WebSocket.OPEN) {\n // Socket not ready, discard messages\n this.queue = [];\n this.pendingBytes = 0;\n this.state = WriterState.IDLE;\n return;\n }\n\n if (this.queue.length === 1) {\n // Single message - send directly without batching overhead\n const msg = this.queue[0];\n this.socket.send(msg.data);\n this.messagesSent++;\n this.batchesSent++;\n this.bytesSent += msg.data.length;\n } else {\n // Multiple messages - create a batch\n const batch = this.createBatch(this.queue);\n this.socket.send(batch);\n this.messagesSent += this.queue.length;\n this.batchesSent++;\n this.bytesSent += batch.length;\n }\n } catch (error) {\n // Socket error - discard messages silently\n // The connection close handler will clean up\n }\n\n // Clear queue\n this.queue = [];\n this.pendingBytes = 0;\n this.state = WriterState.IDLE;\n }\n\n /**\n * Get writer metrics.\n */\n getMetrics(): CoalescingWriterMetrics {\n const totalFlushes = this.immediateFlushCount + this.timedFlushCount;\n return {\n messagesSent: this.messagesSent,\n batchesSent: this.batchesSent,\n bytesSent: this.bytesSent,\n avgMessagesPerBatch: this.batchesSent > 0 ? this.messagesSent / this.batchesSent : 0,\n avgBytesPerBatch: this.batchesSent > 0 ? this.bytesSent / this.batchesSent : 0,\n pendingMessages: this.queue.length,\n pendingBytes: this.pendingBytes,\n // Extended metrics for tuning analysis\n immediateFlushes: this.immediateFlushCount,\n timedFlushes: this.timedFlushCount,\n batchUtilization: this.batchesSent > 0\n ? (this.messagesSent / this.batchesSent) / this.options.maxBatchSize\n : 0,\n immediateFlushRatio: totalFlushes > 0\n ? this.immediateFlushCount / totalFlushes\n : 0,\n pooledBuffersUsed: this.pooledBuffersUsed,\n oversizedBuffers: this.oversizedBuffers,\n };\n }\n\n /**\n * Get current configuration options.\n */\n getOptions(): Readonly<CoalescingWriterOptions> {\n return { ...this.options };\n }\n\n /**\n * Close and cleanup.\n */\n close(): void {\n if (this.closed) {\n return;\n }\n\n this.cancelTimers();\n\n // Flush any remaining messages before marking as closed\n if (this.queue.length > 0) {\n this.flush();\n }\n\n this.closed = true;\n this.queue = [];\n this.urgentQueue = [];\n this.pendingBytes = 0;\n }\n\n /**\n * Send a message immediately without batching.\n */\n private sendImmediate(data: Uint8Array): void {\n if (this.socket.readyState !== WebSocket.OPEN) {\n return;\n }\n\n try {\n this.socket.send(data);\n this.messagesSent++;\n this.batchesSent++;\n this.bytesSent += data.length;\n } catch (error) {\n // Socket error - ignore\n }\n }\n\n /**\n * Schedule a flush using setImmediate + setTimeout for the delay.\n */\n private scheduleFlush(): void {\n if (this.state === WriterState.PENDING) {\n // Already scheduled\n return;\n }\n\n this.state = WriterState.PENDING;\n\n // Use setTimeout for the delay (similar to Nagle's algorithm)\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n // Use setImmediate to batch with other I/O in the same tick\n this.flushTimer = setImmediate(() => {\n this.flushTimer = null;\n this.timedFlushCount++;\n this.flush();\n });\n }, this.options.maxDelayMs);\n }\n\n /**\n * Cancel any pending flush timers.\n */\n private cancelTimers(): void {\n if (this.flushTimer) {\n clearImmediate(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.delayTimer) {\n clearTimeout(this.delayTimer);\n this.delayTimer = null;\n }\n }\n\n /**\n * Create a batch message from multiple queued messages.\n * Uses length-prefixed format for efficiency:\n * [4 bytes: message count][4 bytes: msg1 length][msg1 bytes][4 bytes: msg2 length][msg2 bytes]...\n *\n * OPTIMIZATION: Uses BufferPool for batch buffer allocation to reduce GC pressure.\n * Pooled buffers are reused across batch operations.\n */\n private createBatch(messages: QueuedMessage[]): Uint8Array {\n // Calculate total size needed\n // 4 bytes for count + (4 bytes length + data) for each message\n let totalSize = 4; // message count\n for (const msg of messages) {\n totalSize += 4 + msg.data.length; // length prefix + data\n }\n\n // Acquire buffer from pool (or get oversized buffer if larger than pool chunk)\n const poolConfig = this.bufferPool.getConfig();\n const isPooled = totalSize <= poolConfig.chunkSize!;\n const batch = this.bufferPool.acquireSize(totalSize);\n\n if (isPooled) {\n this.pooledBuffersUsed++;\n } else {\n this.oversizedBuffers++;\n }\n\n const view = new DataView(batch.buffer, batch.byteOffset, batch.byteLength);\n let offset = 0;\n\n // Write message count\n view.setUint32(offset, messages.length, true); // little-endian\n offset += 4;\n\n // Write each message with length prefix\n for (const msg of messages) {\n view.setUint32(offset, msg.data.length, true);\n offset += 4;\n batch.set(msg.data, offset);\n offset += msg.data.length;\n }\n\n // Create the actual used portion of the buffer\n const usedBatch = batch.subarray(0, totalSize);\n\n // Wrap in a BATCH message envelope\n const batchEnvelope = serialize({\n type: 'BATCH',\n count: messages.length,\n data: usedBatch,\n });\n\n // Release the pooled buffer back to pool after serialization is complete\n // (serialize() copies the data, so we can safely release)\n if (isPooled) {\n this.bufferPool.release(batch);\n }\n\n return batchEnvelope;\n }\n}\n","/**\n * BufferPool - High-performance buffer reuse for serialization operations.\n * Phase 2.03: Memory Pooling\n *\n * Reduces GC pressure by reusing pre-allocated Uint8Array buffers instead of\n * creating new ones for each serialization operation.\n *\n * Reference: Hazelcast MemoryAllocator pattern\n */\n\nexport interface BufferPoolConfig {\n /**\n * Size of each buffer chunk in bytes.\n * Larger chunks = fewer allocations, but more memory waste.\n * Default: 64KB (65536)\n */\n chunkSize?: number;\n\n /**\n * Initial number of buffers to pre-allocate.\n * Default: 16\n */\n initialSize?: number;\n\n /**\n * Maximum buffers to keep in pool.\n * Excess buffers are released to GC.\n * Default: 256\n */\n maxSize?: number;\n\n /**\n * Auto-shrink pool when idle buffers exceed this ratio.\n * Default: true\n */\n autoShrink?: boolean;\n\n /**\n * Shrink threshold - shrink if (available / maxSize) > threshold.\n * Default: 0.75 (75% idle)\n */\n shrinkThreshold?: number;\n\n /**\n * Enable metrics collection.\n * Default: true\n */\n metricsEnabled?: boolean;\n}\n\nexport interface BufferPoolStats {\n /** Buffers currently available in pool */\n available: number;\n /** Buffers currently in use */\n inUse: number;\n /** Total buffers ever created */\n created: number;\n /** Total times a buffer was reused */\n reused: number;\n /** Reuse ratio: reused / (created + reused) */\n reuseRatio: number;\n /** Total bytes in pool (available buffers only) */\n poolSizeBytes: number;\n /** Peak concurrent usage */\n peakUsage: number;\n /** Times pool was exhausted (had to create new buffer) */\n misses: number;\n /** Chunk size configuration */\n chunkSize: number;\n /** Max pool size configuration */\n maxSize: number;\n}\n\nconst DEFAULT_CONFIG: Required<BufferPoolConfig> = {\n chunkSize: 65536, // 64KB\n initialSize: 16,\n maxSize: 256,\n autoShrink: true,\n shrinkThreshold: 0.75,\n metricsEnabled: true,\n};\n\n/**\n * High-performance buffer pool for serialization operations.\n *\n * Usage:\n * ```typescript\n * const pool = new BufferPool({ chunkSize: 64 * 1024 });\n * const buffer = pool.acquire();\n * // ... use buffer for serialization ...\n * pool.release(buffer);\n * ```\n *\n * Thread Safety:\n * This pool is designed for single-threaded use (Node.js main thread).\n * For worker threads, create separate pools or copy buffers.\n */\nexport class BufferPool {\n private readonly pool: Uint8Array[] = [];\n private readonly chunkSize: number;\n private readonly maxSize: number;\n private readonly autoShrink: boolean;\n private readonly shrinkThreshold: number;\n private readonly metricsEnabled: boolean;\n\n // Metrics\n private inUseCount = 0;\n private createdCount = 0;\n private reusedCount = 0;\n private peakUsage = 0;\n private missCount = 0;\n\n // Track buffers from this pool (for debugging/validation)\n private readonly pooledBuffers = new WeakSet<Uint8Array>();\n\n // Track released buffers to detect double-release\n private readonly releasedBuffers = new WeakSet<Uint8Array>();\n\n constructor(config?: BufferPoolConfig) {\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n this.chunkSize = cfg.chunkSize;\n this.maxSize = cfg.maxSize;\n this.autoShrink = cfg.autoShrink;\n this.shrinkThreshold = cfg.shrinkThreshold;\n this.metricsEnabled = cfg.metricsEnabled;\n\n // Pre-warm pool\n this.prewarm(cfg.initialSize);\n }\n\n /**\n * Acquire a buffer from the pool.\n * Creates new buffer if pool is empty.\n *\n * @returns A Uint8Array of exactly chunkSize bytes\n */\n acquire(): Uint8Array {\n let buffer: Uint8Array;\n\n if (this.pool.length > 0) {\n buffer = this.pool.pop()!;\n this.releasedBuffers.delete(buffer);\n if (this.metricsEnabled) {\n this.reusedCount++;\n }\n } else {\n buffer = this.createBuffer(this.chunkSize);\n if (this.metricsEnabled) {\n this.missCount++;\n }\n }\n\n if (this.metricsEnabled) {\n this.inUseCount++;\n this.peakUsage = Math.max(this.peakUsage, this.inUseCount);\n }\n\n return buffer;\n }\n\n /**\n * Release a buffer back to the pool.\n * Buffer contents are cleared before returning to pool.\n *\n * @param buffer - The buffer to release\n */\n release(buffer: Uint8Array): void {\n // Skip if already released (idempotent)\n if (this.releasedBuffers.has(buffer)) {\n return;\n }\n\n if (this.metricsEnabled) {\n this.inUseCount = Math.max(0, this.inUseCount - 1);\n }\n\n // Don't return oversized buffers to pool - let GC handle\n if (buffer.byteLength > this.chunkSize) {\n return;\n }\n\n // Don't return undersized buffers (shouldn't happen, but be safe)\n if (buffer.byteLength < this.chunkSize) {\n return;\n }\n\n // Don't exceed max pool size - let GC handle excess\n if (this.pool.length >= this.maxSize) {\n return;\n }\n\n // Clear buffer before returning (security + helps with debugging)\n // Note: fill(0) is optimized in V8 for typed arrays\n buffer.fill(0);\n\n this.pool.push(buffer);\n this.releasedBuffers.add(buffer);\n\n // Auto-shrink if needed\n if (this.autoShrink && this.shouldShrink()) {\n this.shrink();\n }\n }\n\n /**\n * Acquire a buffer of specific minimum size.\n * May return a buffer larger than requested.\n * For sizes > chunkSize, creates new buffer (not pooled).\n *\n * @param minSize - Minimum required size in bytes\n * @returns A Uint8Array of at least minSize bytes\n */\n acquireSize(minSize: number): Uint8Array {\n // For zero or negative, return standard chunk\n if (minSize <= 0) {\n return this.acquire();\n }\n\n // If fits in chunk, use pool\n if (minSize <= this.chunkSize) {\n return this.acquire();\n }\n\n // Large buffer - create directly, not pooled\n // These will be GC'd after use\n return this.createBuffer(minSize);\n }\n\n /**\n * Get current pool statistics.\n */\n getStats(): BufferPoolStats {\n const total = this.createdCount + this.reusedCount;\n return {\n available: this.pool.length,\n inUse: this.inUseCount,\n created: this.createdCount,\n reused: this.reusedCount,\n reuseRatio: total > 0 ? this.reusedCount / total : 0,\n poolSizeBytes: this.pool.length * this.chunkSize,\n peakUsage: this.peakUsage,\n misses: this.missCount,\n chunkSize: this.chunkSize,\n maxSize: this.maxSize,\n };\n }\n\n /**\n * Clear all buffers from pool.\n * Use for shutdown or memory pressure situations.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Manually trigger shrink operation.\n * Removes excess idle buffers to reduce memory footprint.\n */\n shrink(): void {\n // Target 50% of threshold to avoid immediate re-shrink\n const targetSize = Math.floor(this.maxSize * (this.shrinkThreshold - 0.25));\n const safeTarget = Math.max(0, targetSize);\n\n while (this.pool.length > safeTarget) {\n this.pool.pop(); // Let GC collect\n }\n }\n\n /**\n * Pre-warm pool by creating buffers up to specified count.\n * Called automatically in constructor.\n *\n * @param count - Number of buffers to create\n */\n prewarm(count?: number): void {\n const targetCount = count ?? DEFAULT_CONFIG.initialSize;\n const toCreate = Math.min(targetCount, this.maxSize) - this.pool.length;\n\n for (let i = 0; i < toCreate; i++) {\n const buffer = this.createBuffer(this.chunkSize);\n this.pool.push(buffer);\n this.releasedBuffers.add(buffer);\n }\n }\n\n /**\n * Reset all metrics to zero.\n * Useful for testing or periodic metric collection.\n */\n resetStats(): void {\n this.createdCount = 0;\n this.reusedCount = 0;\n this.peakUsage = this.inUseCount;\n this.missCount = 0;\n }\n\n /**\n * Get configuration values.\n */\n getConfig(): Readonly<Required<BufferPoolConfig>> {\n return {\n chunkSize: this.chunkSize,\n initialSize: DEFAULT_CONFIG.initialSize,\n maxSize: this.maxSize,\n autoShrink: this.autoShrink,\n shrinkThreshold: this.shrinkThreshold,\n metricsEnabled: this.metricsEnabled,\n };\n }\n\n // ============ Private Methods ============\n\n private createBuffer(size: number): Uint8Array {\n const buffer = new Uint8Array(size);\n this.pooledBuffers.add(buffer);\n if (this.metricsEnabled) {\n this.createdCount++;\n }\n return buffer;\n }\n\n private shouldShrink(): boolean {\n if (this.maxSize === 0) return false;\n const ratio = this.pool.length / this.maxSize;\n return ratio > this.shrinkThreshold;\n }\n}\n\n/**\n * Global shared buffer pool instance.\n * Use this for most serialization operations.\n *\n * For custom configurations or isolated pools, create new BufferPool instances.\n */\nlet globalPool: BufferPool | null = null;\n\n/**\n * Get or create the global buffer pool.\n * Uses default configuration (64KB chunks, 256 max).\n */\nexport function getGlobalBufferPool(): BufferPool {\n if (!globalPool) {\n globalPool = new BufferPool();\n }\n return globalPool;\n}\n\n/**\n * Replace the global buffer pool with a custom instance.\n * Useful for testing or custom configurations.\n *\n * @param pool - New pool instance, or null to reset to default\n */\nexport function setGlobalBufferPool(pool: BufferPool | null): void {\n globalPool = pool;\n}\n","/**\n * ObjectPool - Generic object pooling for reducing GC pressure.\n * Phase 2.04: Object Pool Implementation\n *\n * Reuses objects instead of creating new ones in hot paths.\n * Works with any plain data structure type.\n */\n\nexport interface ObjectPoolConfig<T> {\n /**\n * Factory function to create new objects.\n * Called when pool is empty.\n */\n factory: () => T;\n\n /**\n * Reset function to clean object before reuse.\n * Should clear all fields to initial state.\n */\n reset: (obj: T) => void;\n\n /**\n * Optional validator to check if object is reusable.\n * Return false to discard corrupted objects.\n */\n validate?: (obj: T) => boolean;\n\n /**\n * Initial pool size.\n * Default: 32\n */\n initialSize?: number;\n\n /**\n * Maximum pool size.\n * Excess objects are discarded.\n * Default: 512\n */\n maxSize?: number;\n\n /**\n * Name for debugging/metrics.\n */\n name?: string;\n}\n\nexport interface ObjectPoolStats {\n /** Pool name */\n name: string;\n /** Objects currently available */\n available: number;\n /** Objects currently in use */\n inUse: number;\n /** Total objects created */\n created: number;\n /** Total times an object was reused */\n reused: number;\n /** Reuse ratio */\n reuseRatio: number;\n /** Objects discarded (failed validation) */\n discarded: number;\n /** Max pool size */\n maxSize: number;\n /** Peak concurrent usage */\n peakUsage: number;\n}\n\nconst DEFAULT_INITIAL_SIZE = 32;\nconst DEFAULT_MAX_SIZE = 512;\n\n/**\n * Generic object pool for reusing plain data structures.\n *\n * Usage:\n * ```typescript\n * const pool = new ObjectPool({\n * factory: () => ({ name: '', value: 0 }),\n * reset: (obj) => { obj.name = ''; obj.value = 0; },\n * });\n *\n * const obj = pool.acquire();\n * obj.name = 'test';\n * obj.value = 42;\n * // ... use obj ...\n * pool.release(obj);\n * ```\n *\n * Important:\n * - Only use for plain data objects\n * - Don't pool objects with closures or event listeners\n * - Don't keep references after release\n */\nexport class ObjectPool<T> {\n private readonly pool: T[] = [];\n private readonly factory: () => T;\n private readonly resetFn: (obj: T) => void;\n private readonly validate?: (obj: T) => boolean;\n private readonly maxSize: number;\n private readonly name: string;\n\n // Metrics\n private inUseCount = 0;\n private createdCount = 0;\n private reusedCount = 0;\n private discardedCount = 0;\n private peakUsage = 0;\n\n // Track released objects for idempotent release\n private readonly releasedObjects = new WeakSet<object>();\n\n constructor(config: ObjectPoolConfig<T>) {\n this.factory = config.factory;\n this.resetFn = config.reset;\n this.validate = config.validate;\n this.maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;\n this.name = config.name ?? 'unnamed';\n\n // Pre-warm\n const initialSize = config.initialSize ?? DEFAULT_INITIAL_SIZE;\n this.prewarm(initialSize);\n }\n\n /**\n * Acquire an object from the pool.\n * Creates new object via factory if pool is empty.\n *\n * @returns A clean, ready-to-use object\n */\n acquire(): T {\n let obj: T;\n\n if (this.pool.length > 0) {\n obj = this.pool.pop()!;\n\n // Remove from released set\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.delete(obj as object);\n }\n\n // Validate if validator provided\n if (this.validate && !this.validate(obj)) {\n this.discardedCount++;\n obj = this.createObject();\n } else {\n this.reusedCount++;\n }\n } else {\n obj = this.createObject();\n }\n\n this.inUseCount++;\n this.peakUsage = Math.max(this.peakUsage, this.inUseCount);\n return obj;\n }\n\n /**\n * Release an object back to the pool.\n * Object is reset before returning to pool.\n *\n * @param obj - The object to release\n */\n release(obj: T): void {\n // Skip if already released (idempotent)\n if (typeof obj === 'object' && obj !== null) {\n if (this.releasedObjects.has(obj as object)) {\n return;\n }\n }\n\n this.inUseCount = Math.max(0, this.inUseCount - 1);\n\n // Don't exceed max pool size\n if (this.pool.length >= this.maxSize) {\n return; // Let GC handle it\n }\n\n // Validate before returning\n if (this.validate && !this.validate(obj)) {\n this.discardedCount++;\n return;\n }\n\n // Reset object\n this.resetFn(obj);\n\n // Track as released\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.add(obj as object);\n }\n\n this.pool.push(obj);\n }\n\n /**\n * Acquire multiple objects at once.\n * More efficient than multiple acquire() calls.\n *\n * @param count - Number of objects to acquire\n * @returns Array of objects\n */\n acquireBatch(count: number): T[] {\n const result: T[] = new Array(count);\n for (let i = 0; i < count; i++) {\n result[i] = this.acquire();\n }\n return result;\n }\n\n /**\n * Release multiple objects at once.\n *\n * @param objects - Objects to release\n */\n releaseBatch(objects: T[]): void {\n for (const obj of objects) {\n this.release(obj);\n }\n }\n\n /**\n * Get pool statistics.\n */\n getStats(): ObjectPoolStats {\n const total = this.createdCount + this.reusedCount;\n return {\n name: this.name,\n available: this.pool.length,\n inUse: this.inUseCount,\n created: this.createdCount,\n reused: this.reusedCount,\n reuseRatio: total > 0 ? this.reusedCount / total : 0,\n discarded: this.discardedCount,\n maxSize: this.maxSize,\n peakUsage: this.peakUsage,\n };\n }\n\n /**\n * Clear all objects from pool.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Pre-warm pool with objects.\n *\n * @param count - Number of objects to create\n */\n prewarm(count: number = DEFAULT_INITIAL_SIZE): void {\n const toCreate = Math.min(count, this.maxSize) - this.pool.length;\n for (let i = 0; i < toCreate; i++) {\n const obj = this.createObject();\n if (typeof obj === 'object' && obj !== null) {\n this.releasedObjects.add(obj as object);\n }\n this.pool.push(obj);\n }\n }\n\n /**\n * Reset statistics.\n */\n resetStats(): void {\n this.createdCount = 0;\n this.reusedCount = 0;\n this.discardedCount = 0;\n this.peakUsage = this.inUseCount;\n }\n\n // ============ Private Methods ============\n\n private createObject(): T {\n this.createdCount++;\n return this.factory();\n }\n}\n","/**\n * MessagePool - Pre-configured ObjectPool for message objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure when processing incoming messages.\n */\n\nimport { ObjectPool } from '../ObjectPool';\n\n/**\n * Pooled message structure matching common message format.\n */\nexport interface PooledMessage {\n type: string;\n payload: unknown;\n timestamp: number | null;\n clientId: string | null;\n mapName: string | null;\n key: string | null;\n}\n\nconst DEFAULT_MAX_SIZE = 1024;\n\n/**\n * Create a new MessagePool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for messages\n */\nexport function createMessagePool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledMessage> {\n return new ObjectPool<PooledMessage>({\n name: 'message',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n type: '',\n payload: null,\n timestamp: null,\n clientId: null,\n mapName: null,\n key: null,\n }),\n reset: (msg) => {\n msg.type = '';\n msg.payload = null;\n msg.timestamp = null;\n msg.clientId = null;\n msg.mapName = null;\n msg.key = null;\n },\n });\n}\n\n// Global singleton instance\nlet globalMessagePool: ObjectPool<PooledMessage> | null = null;\n\n/**\n * Get or create the global message pool.\n */\nexport function getGlobalMessagePool(): ObjectPool<PooledMessage> {\n if (!globalMessagePool) {\n globalMessagePool = createMessagePool();\n }\n return globalMessagePool;\n}\n\n/**\n * Replace the global message pool (for testing).\n */\nexport function setGlobalMessagePool(pool: ObjectPool<PooledMessage> | null): void {\n globalMessagePool = pool;\n}\n","/**\n * TimestampPool - Pre-configured ObjectPool for HLC timestamp objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure for frequent timestamp operations.\n * Timestamps are created on every operation (set, merge, etc.).\n */\n\nimport { ObjectPool } from '../ObjectPool';\n\n/**\n * Pooled timestamp structure matching HLC format.\n */\nexport interface PooledTimestamp {\n millis: number;\n counter: number;\n nodeId: string;\n}\n\nconst DEFAULT_MAX_SIZE = 2048;\n\n/**\n * Create a new TimestampPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for timestamps\n */\nexport function createTimestampPool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledTimestamp> {\n return new ObjectPool<PooledTimestamp>({\n name: 'timestamp',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 128,\n factory: () => ({\n millis: 0,\n counter: 0,\n nodeId: '',\n }),\n reset: (ts) => {\n ts.millis = 0;\n ts.counter = 0;\n ts.nodeId = '';\n },\n validate: (ts) => {\n // Ensure fields are correct type (for corruption detection)\n return (\n typeof ts.millis === 'number' &&\n typeof ts.counter === 'number' &&\n typeof ts.nodeId === 'string'\n );\n },\n });\n}\n\n// Global singleton instance\nlet globalTimestampPool: ObjectPool<PooledTimestamp> | null = null;\n\n/**\n * Get or create the global timestamp pool.\n */\nexport function getGlobalTimestampPool(): ObjectPool<PooledTimestamp> {\n if (!globalTimestampPool) {\n globalTimestampPool = createTimestampPool();\n }\n return globalTimestampPool;\n}\n\n/**\n * Replace the global timestamp pool (for testing).\n */\nexport function setGlobalTimestampPool(pool: ObjectPool<PooledTimestamp> | null): void {\n globalTimestampPool = pool;\n}\n","/**\n * RecordPool - Pre-configured ObjectPool for LWW/OR record objects.\n * Phase 2.04: Object Pool Implementation\n *\n * Reduces GC pressure when processing CRDT operations.\n */\n\nimport { ObjectPool } from '../ObjectPool';\nimport type { PooledTimestamp } from './TimestampPool';\n\n/**\n * Pooled record structure matching LWWRecord format.\n */\nexport interface PooledRecord<T = unknown> {\n value: T | null;\n timestamp: PooledTimestamp | null;\n ttlMs?: number;\n}\n\n/**\n * Pooled event payload structure.\n */\nexport interface PooledEventPayload {\n mapName: string;\n key: string;\n eventType: string;\n record: PooledRecord | null;\n orRecord: PooledRecord | null;\n orTag: string | null;\n}\n\nconst DEFAULT_MAX_SIZE = 4096;\nconst DEFAULT_EVENT_MAX_SIZE = 2048;\n\n/**\n * Create a new RecordPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for records\n */\nexport function createRecordPool<T = unknown>(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledRecord<T>> {\n return new ObjectPool<PooledRecord<T>>({\n name: 'record',\n maxSize: config?.maxSize ?? DEFAULT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n value: null,\n timestamp: null,\n ttlMs: undefined,\n }),\n reset: (rec) => {\n rec.value = null;\n rec.timestamp = null;\n rec.ttlMs = undefined;\n },\n });\n}\n\n/**\n * Create a new EventPayloadPool instance.\n *\n * @param config - Pool configuration\n * @returns Configured ObjectPool for event payloads\n */\nexport function createEventPayloadPool(config?: { maxSize?: number; initialSize?: number }): ObjectPool<PooledEventPayload> {\n return new ObjectPool<PooledEventPayload>({\n name: 'eventPayload',\n maxSize: config?.maxSize ?? DEFAULT_EVENT_MAX_SIZE,\n initialSize: config?.initialSize ?? 64,\n factory: () => ({\n mapName: '',\n key: '',\n eventType: '',\n record: null,\n orRecord: null,\n orTag: null,\n }),\n reset: (payload) => {\n payload.mapName = '';\n payload.key = '';\n payload.eventType = '';\n payload.record = null;\n payload.orRecord = null;\n payload.orTag = null;\n },\n });\n}\n\n// Global singleton instances\nlet globalRecordPool: ObjectPool<PooledRecord> | null = null;\nlet globalEventPayloadPool: ObjectPool<PooledEventPayload> | null = null;\n\n/**\n * Get or create the global record pool.\n */\nexport function getGlobalRecordPool(): ObjectPool<PooledRecord> {\n if (!globalRecordPool) {\n globalRecordPool = createRecordPool();\n }\n return globalRecordPool;\n}\n\n/**\n * Replace the global record pool (for testing).\n */\nexport function setGlobalRecordPool(pool: ObjectPool<PooledRecord> | null): void {\n globalRecordPool = pool;\n}\n\n/**\n * Get or create the global event payload pool.\n */\nexport function getGlobalEventPayloadPool(): ObjectPool<PooledEventPayload> {\n if (!globalEventPayloadPool) {\n globalEventPayloadPool = createEventPayloadPool();\n }\n return globalEventPayloadPool;\n}\n\n/**\n * Replace the global event payload pool (for testing).\n */\nexport function setGlobalEventPayloadPool(pool: ObjectPool<PooledEventPayload> | null): void {\n globalEventPayloadPool = pool;\n}\n","import type { CoalescingWriterOptions } from './CoalescingWriter';\n\n/**\n * Preset configurations for CoalescingWriter.\n * Based on Hazelcast OutboxImpl (batch size 2048) and real-world benchmarking.\n *\n * Trade-offs:\n * - Larger batch size = higher throughput, higher latency\n * - Longer delay = more messages per batch, higher latency\n * - Larger maxBatchBytes = handles larger payloads, more memory\n *\n * NOTE: A/B testing (Dec 2024) showed maxDelayMs is the primary bottleneck:\n * - 10ms delay: ~10K ops/sec, p50=11ms\n * - 1ms delay: ~18K ops/sec, p50=8ms (+80% throughput)\n * - 0ms (disabled): ~18K ops/sec, p50=2ms (best latency)\n */\nexport const coalescingPresets = {\n /**\n * Low latency - optimized for minimal response time.\n * Best for: gaming, real-time chat, interactive applications.\n * Benchmark: p50=2ms, ~18K ops/sec\n */\n lowLatency: {\n maxBatchSize: 100,\n maxDelayMs: 1,\n maxBatchBytes: 65536, // 64KB\n },\n\n /**\n * Conservative - good balance of latency and batching.\n * Use for: general purpose with latency sensitivity.\n */\n conservative: {\n maxBatchSize: 100,\n maxDelayMs: 2,\n maxBatchBytes: 65536, // 64KB\n },\n\n /**\n * Balanced - good for most workloads.\n * Reasonable trade-off between throughput and latency.\n * Use for: mixed read/write applications, general purpose.\n */\n balanced: {\n maxBatchSize: 300,\n maxDelayMs: 2,\n maxBatchBytes: 131072, // 128KB\n },\n\n /**\n * High throughput - optimized for write-heavy workloads.\n * Higher batching for better network utilization.\n * Use for: data ingestion, logging, IoT data streams.\n * Benchmark: p50=7ms, ~18K ops/sec\n */\n highThroughput: {\n maxBatchSize: 500,\n maxDelayMs: 2,\n maxBatchBytes: 262144, // 256KB\n },\n\n /**\n * Aggressive - maximum batching for batch processing.\n * Closest to Hazelcast's OutboxImpl (batch size 2048).\n * Use for: batch imports, bulk operations, offline sync.\n */\n aggressive: {\n maxBatchSize: 1000,\n maxDelayMs: 5,\n maxBatchBytes: 524288, // 512KB\n },\n} as const satisfies Record<string, CoalescingWriterOptions>;\n\n/**\n * Available preset names for type safety.\n */\nexport type CoalescingPreset = keyof typeof coalescingPresets;\n\n/**\n * Get preset configuration by name.\n * @param preset - Preset name\n * @returns CoalescingWriterOptions\n */\nexport function getCoalescingPreset(preset: CoalescingPreset): CoalescingWriterOptions {\n return { ...coalescingPresets[preset] };\n}\n","/**\n * ConnectionRateLimiter - Rate limiter for incoming WebSocket connections.\n *\n * Implements connection rate limiting to prevent connection storms and\n * protect the server from being overwhelmed during high load scenarios.\n *\n * Features:\n * - Limits connections per second to prevent TCP backlog exhaustion\n * - Tracks pending connections (in-progress handshakes)\n * - Provides graceful rejection when limits are exceeded\n * - Auto-resets counters after cooldown period\n */\n\nimport { logger } from './logger';\n\nexport interface RateLimiterConfig {\n /** Maximum new connections allowed per second (default: 100) */\n maxConnectionsPerSecond: number;\n /** Maximum pending connections waiting for handshake (default: 1000) */\n maxPendingConnections: number;\n /** Cooldown period in ms after which counters reset (default: 1000) */\n cooldownMs: number;\n}\n\nexport interface RateLimiterStats {\n /** Current connections per second rate */\n connectionsPerSecond: number;\n /** Number of connections currently pending (handshake in progress) */\n pendingConnections: number;\n /** Total connections established since start */\n totalConnections: number;\n /** Total connections rejected due to rate limiting */\n totalRejected: number;\n}\n\nconst DEFAULT_CONFIG: RateLimiterConfig = {\n maxConnectionsPerSecond: 100,\n maxPendingConnections: 1000,\n cooldownMs: 1000,\n};\n\nexport class ConnectionRateLimiter {\n private config: RateLimiterConfig;\n\n /** Connection attempts in current window */\n private connectionCount: number = 0;\n\n /** Timestamp when current window started */\n private windowStart: number = Date.now();\n\n /** Currently pending connections (handshake in progress) */\n private pendingCount: number = 0;\n\n /** Total connections established since start */\n private totalConnections: number = 0;\n\n /** Total connections rejected since start */\n private totalRejected: number = 0;\n\n constructor(config?: Partial<RateLimiterConfig>) {\n this.config = {\n ...DEFAULT_CONFIG,\n ...config,\n };\n }\n\n /**\n * Check if a new connection should be accepted.\n * @returns true if the connection should be accepted, false if it should be rejected\n */\n shouldAccept(): boolean {\n this.maybeResetWindow();\n\n // Check if pending connections limit is exceeded\n if (this.pendingCount >= this.config.maxPendingConnections) {\n logger.debug(\n { pendingCount: this.pendingCount, maxPending: this.config.maxPendingConnections },\n 'Connection rejected: pending connections limit exceeded'\n );\n return false;\n }\n\n // Check if connections per second limit is exceeded\n if (this.connectionCount >= this.config.maxConnectionsPerSecond) {\n logger.debug(\n { connectionCount: this.connectionCount, maxPerSecond: this.config.maxConnectionsPerSecond },\n 'Connection rejected: rate limit exceeded'\n );\n return false;\n }\n\n return true;\n }\n\n /**\n * Register a connection attempt.\n * Call this when a new connection is initiated (before handshake completes).\n */\n onConnectionAttempt(): void {\n this.maybeResetWindow();\n this.connectionCount++;\n this.pendingCount++;\n }\n\n /**\n * Register that a connection has been established (handshake complete).\n * Call this when the connection is fully established and authenticated.\n */\n onConnectionEstablished(): void {\n this.pendingCount = Math.max(0, this.pendingCount - 1);\n this.totalConnections++;\n }\n\n /**\n * Register that a connection has been closed.\n * Call this when a pending connection is closed before completing handshake.\n */\n onConnectionClosed(): void {\n // If connection was pending, decrement pending count\n // Note: We can't distinguish between pending and established connections here,\n // so this is a best-effort tracking. In practice, onConnectionEstablished\n // should be called first for established connections.\n }\n\n /**\n * Register that a connection was rejected.\n * Call this when shouldAccept() returns false and the connection is rejected.\n */\n onConnectionRejected(): void {\n this.totalRejected++;\n }\n\n /**\n * Decrease pending count when a connection times out or fails.\n * Call this when a pending connection fails to complete handshake.\n */\n onPendingConnectionFailed(): void {\n this.pendingCount = Math.max(0, this.pendingCount - 1);\n }\n\n /**\n * Get current rate limiter statistics.\n */\n getStats(): RateLimiterStats {\n this.maybeResetWindow();\n\n // Calculate actual connections per second\n const elapsed = Date.now() - this.windowStart;\n const connectionsPerSecond = elapsed > 0\n ? Math.round((this.connectionCount / elapsed) * 1000)\n : this.connectionCount;\n\n return {\n connectionsPerSecond,\n pendingConnections: this.pendingCount,\n totalConnections: this.totalConnections,\n totalRejected: this.totalRejected,\n };\n }\n\n /**\n * Reset the rate limiter state.\n * Useful for testing or when recovering from errors.\n */\n reset(): void {\n this.connectionCount = 0;\n this.windowStart = Date.now();\n this.pendingCount = 0;\n this.totalConnections = 0;\n this.totalRejected = 0;\n }\n\n /**\n * Update configuration at runtime.\n */\n updateConfig(config: Partial<RateLimiterConfig>): void {\n this.config = {\n ...this.config,\n ...config,\n };\n }\n\n /**\n * Check if window has expired and reset if needed.\n */\n private maybeResetWindow(): void {\n const now = Date.now();\n if (now - this.windowStart >= this.config.cooldownMs) {\n this.connectionCount = 0;\n this.windowStart = now;\n }\n }\n}\n","/**\n * WorkerPool Implementation\n * Phase 1.02: Worker Threads Implementation\n *\n * Manages a pool of worker threads for CPU-bound operations.\n * Features:\n * - Auto-scaling (minWorkers to maxWorkers)\n * - Priority queue (high > normal > low)\n * - Task timeouts\n * - Worker crash recovery\n * - Graceful shutdown\n */\n\nimport { Worker } from 'worker_threads';\nimport { cpus } from 'os';\nimport { join } from 'path';\nimport {\n WorkerPoolConfig,\n WorkerTask,\n WorkerPoolStats,\n WorkerState,\n PendingTask,\n WorkerMessage,\n WorkerResponse,\n TaskPriority,\n} from './types';\nimport {\n WorkerTimeoutError,\n WorkerTaskError,\n WorkerPoolShutdownError,\n WorkerCrashError,\n} from './errors';\n\n// Priority order for queue sorting\nconst PRIORITY_ORDER: Record<TaskPriority, number> = {\n high: 0,\n normal: 1,\n low: 2,\n};\n\nexport class WorkerPool {\n private readonly config: Required<Omit<WorkerPoolConfig, 'workerScript'>> & { workerScript: string };\n private readonly workers: Map<number, WorkerState> = new Map();\n private readonly taskQueue: PendingTask[] = [];\n private readonly pendingTasks: Map<string, PendingTask> = new Map();\n\n private workerIdCounter = 0;\n private isShuttingDown = false;\n private isShutdown = false;\n\n // Statistics\n private completedTaskCount = 0;\n private failedTaskCount = 0;\n private totalTaskDuration = 0;\n\n // Idle check interval\n private idleCheckInterval?: NodeJS.Timeout;\n\n constructor(config?: WorkerPoolConfig) {\n const cpuCount = cpus().length;\n\n // Determine worker script path\n // In production: compiled .js file\n // In tests: .ts file needs ts-node registration\n const defaultWorkerScript = this.resolveWorkerScript();\n\n this.config = {\n minWorkers: config?.minWorkers ?? 2,\n maxWorkers: config?.maxWorkers ?? Math.max(1, cpuCount - 1),\n taskTimeout: config?.taskTimeout ?? 5000,\n idleTimeout: config?.idleTimeout ?? 30000,\n autoRestart: config?.autoRestart ?? true,\n workerScript: config?.workerScript ?? defaultWorkerScript,\n };\n\n // Validate config\n if (this.config.minWorkers < 1) {\n this.config.minWorkers = 1;\n }\n if (this.config.maxWorkers < this.config.minWorkers) {\n this.config.maxWorkers = this.config.minWorkers;\n }\n\n // Initialize minimum workers\n this.initializeWorkers();\n\n // Start idle check interval\n this.startIdleCheck();\n }\n\n /**\n * Resolve the worker script path based on environment\n */\n private resolveWorkerScript(): string {\n // Check if we're in a compiled environment (.js files)\n const jsPath = join(__dirname, 'worker-scripts', 'base.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'base.worker.ts');\n\n // Prefer .js if it exists (production), otherwise use .ts (development/test)\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n // .js not found, try .ts for ts-node environments\n return tsPath;\n }\n }\n\n /**\n * Submit a task to the pool\n */\n public submit<TPayload, TResult>(\n task: WorkerTask<TPayload, TResult>\n ): Promise<TResult> {\n return new Promise((resolve, reject) => {\n if (this.isShuttingDown || this.isShutdown) {\n reject(new WorkerPoolShutdownError());\n return;\n }\n\n const pendingTask: PendingTask<TResult> = {\n task: task as WorkerTask<unknown, TResult>,\n resolve,\n reject,\n submittedAt: Date.now(),\n };\n\n // Set up timeout\n if (this.config.taskTimeout > 0) {\n pendingTask.timeoutId = setTimeout(() => {\n this.handleTaskTimeout(task.id);\n }, this.config.taskTimeout);\n }\n\n // Add to pending map\n this.pendingTasks.set(task.id, pendingTask as PendingTask);\n\n // Try to assign to idle worker\n const idleWorker = this.findIdleWorker();\n if (idleWorker) {\n this.assignTaskToWorker(idleWorker, pendingTask as PendingTask);\n } else {\n // Queue the task with priority\n this.enqueueTask(pendingTask as PendingTask);\n\n // Try to scale up if possible\n this.tryScaleUp();\n }\n });\n }\n\n /**\n * Get current pool statistics\n */\n public getStats(): WorkerPoolStats {\n let activeWorkers = 0;\n let idleWorkers = 0;\n\n for (const state of this.workers.values()) {\n if (state.busy) {\n activeWorkers++;\n } else {\n idleWorkers++;\n }\n }\n\n return {\n activeWorkers,\n idleWorkers,\n pendingTasks: this.taskQueue.length,\n completedTasks: this.completedTaskCount,\n failedTasks: this.failedTaskCount,\n avgTaskDuration:\n this.completedTaskCount > 0\n ? this.totalTaskDuration / this.completedTaskCount\n : 0,\n };\n }\n\n /**\n * Gracefully shutdown all workers\n */\n public async shutdown(timeout = 10000): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n // Stop idle check\n if (this.idleCheckInterval) {\n clearInterval(this.idleCheckInterval);\n this.idleCheckInterval = undefined;\n }\n\n // Reject all queued tasks\n for (const pendingTask of this.taskQueue) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n pendingTask.reject(new WorkerPoolShutdownError());\n }\n this.taskQueue.length = 0;\n\n // Wait for active tasks to complete (with timeout)\n const startTime = Date.now();\n while (this.pendingTasks.size > 0 && Date.now() - startTime < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n // Force reject remaining pending tasks\n for (const [taskId, pendingTask] of this.pendingTasks) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n pendingTask.reject(new WorkerPoolShutdownError());\n this.pendingTasks.delete(taskId);\n }\n\n // Terminate all workers\n const terminatePromises: Promise<number>[] = [];\n for (const [workerId, state] of this.workers) {\n terminatePromises.push(\n state.worker.terminate().then(() => workerId)\n );\n }\n\n await Promise.all(terminatePromises);\n this.workers.clear();\n\n this.isShutdown = true;\n this.isShuttingDown = false;\n }\n\n /**\n * Check if pool is accepting tasks\n */\n public isRunning(): boolean {\n return !this.isShuttingDown && !this.isShutdown;\n }\n\n // ============ Private Methods ============\n\n private initializeWorkers(): void {\n for (let i = 0; i < this.config.minWorkers; i++) {\n this.createWorker();\n }\n }\n\n private createWorker(): WorkerState | null {\n if (this.isShuttingDown || this.isShutdown) {\n return null;\n }\n\n try {\n const workerId = ++this.workerIdCounter;\n\n // For TypeScript files in test environment, use ts-node\n const workerOptions = this.config.workerScript.endsWith('.ts')\n ? { execArgv: ['--require', 'ts-node/register'] }\n : {};\n\n const worker = new Worker(this.config.workerScript, workerOptions);\n\n const state: WorkerState = {\n worker,\n busy: false,\n idleSince: Date.now(),\n tasksCompleted: 0,\n };\n\n // Handle messages from worker\n worker.on('message', (response: WorkerResponse) => {\n this.handleWorkerResponse(workerId, response);\n });\n\n // Handle worker errors\n worker.on('error', (error) => {\n console.error(`Worker ${workerId} error:`, error);\n this.handleWorkerError(workerId, error);\n });\n\n // Handle worker exit\n worker.on('exit', (code) => {\n this.handleWorkerExit(workerId, code);\n });\n\n this.workers.set(workerId, state);\n return state;\n } catch (error) {\n console.error('Failed to create worker:', error);\n return null;\n }\n }\n\n private findIdleWorker(): WorkerState | undefined {\n for (const state of this.workers.values()) {\n if (!state.busy) {\n return state;\n }\n }\n return undefined;\n }\n\n private assignTaskToWorker(\n workerState: WorkerState,\n pendingTask: PendingTask\n ): void {\n workerState.busy = true;\n workerState.currentTaskId = pendingTask.task.id;\n workerState.idleSince = undefined;\n\n const message: WorkerMessage = {\n id: pendingTask.task.id,\n type: pendingTask.task.type,\n payload: pendingTask.task.payload,\n };\n\n workerState.worker.postMessage(message);\n }\n\n private enqueueTask(pendingTask: PendingTask): void {\n const priority = pendingTask.task.priority ?? 'normal';\n const priorityOrder = PRIORITY_ORDER[priority];\n\n // Find insertion point (maintain priority order, FIFO within same priority)\n let insertIndex = this.taskQueue.length;\n for (let i = 0; i < this.taskQueue.length; i++) {\n const queuedPriority = this.taskQueue[i].task.priority ?? 'normal';\n if (PRIORITY_ORDER[queuedPriority] > priorityOrder) {\n insertIndex = i;\n break;\n }\n }\n\n this.taskQueue.splice(insertIndex, 0, pendingTask);\n }\n\n private tryScaleUp(): void {\n if (this.workers.size < this.config.maxWorkers) {\n const newWorker = this.createWorker();\n if (newWorker && this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(newWorker, nextTask);\n }\n }\n }\n\n private handleWorkerResponse(\n workerId: number,\n response: WorkerResponse\n ): void {\n const pendingTask = this.pendingTasks.get(response.id);\n if (!pendingTask) {\n // Task already timed out or cancelled\n return;\n }\n\n // Clear timeout\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n\n // Remove from pending\n this.pendingTasks.delete(response.id);\n\n // Update stats\n const duration = Date.now() - pendingTask.submittedAt;\n this.totalTaskDuration += duration;\n\n // Resolve or reject\n if (response.success) {\n this.completedTaskCount++;\n pendingTask.resolve(response.result);\n } else {\n this.failedTaskCount++;\n pendingTask.reject(\n new WorkerTaskError(response.id, response.error ?? 'Unknown error')\n );\n }\n\n // Mark worker as idle and process next task\n const workerState = this.workers.get(workerId);\n if (workerState) {\n workerState.busy = false;\n workerState.currentTaskId = undefined;\n workerState.idleSince = Date.now();\n workerState.tasksCompleted++;\n\n // Process next task from queue\n if (this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(workerState, nextTask);\n }\n }\n }\n\n private handleTaskTimeout(taskId: string): void {\n const pendingTask = this.pendingTasks.get(taskId);\n if (!pendingTask) {\n return;\n }\n\n // Remove from pending\n this.pendingTasks.delete(taskId);\n this.failedTaskCount++;\n\n // Reject with timeout error\n pendingTask.reject(\n new WorkerTimeoutError(taskId, this.config.taskTimeout)\n );\n\n // Note: The worker will continue processing and send a response,\n // but it will be ignored since we removed from pendingTasks.\n // The worker itself is not killed - it will become available for\n // next task when it finishes.\n }\n\n private handleWorkerError(workerId: number, error: Error): void {\n const workerState = this.workers.get(workerId);\n if (!workerState) {\n return;\n }\n\n // If worker was processing a task, reject it\n if (workerState.currentTaskId) {\n const pendingTask = this.pendingTasks.get(workerState.currentTaskId);\n if (pendingTask) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n this.pendingTasks.delete(workerState.currentTaskId);\n this.failedTaskCount++;\n pendingTask.reject(\n new WorkerTaskError(workerState.currentTaskId, error.message)\n );\n }\n }\n }\n\n private handleWorkerExit(workerId: number, exitCode: number | null): void {\n const workerState = this.workers.get(workerId);\n this.workers.delete(workerId);\n\n if (!workerState) {\n return;\n }\n\n // If worker was processing a task, reject it\n if (workerState.currentTaskId) {\n const pendingTask = this.pendingTasks.get(workerState.currentTaskId);\n if (pendingTask) {\n if (pendingTask.timeoutId) {\n clearTimeout(pendingTask.timeoutId);\n }\n this.pendingTasks.delete(workerState.currentTaskId);\n this.failedTaskCount++;\n pendingTask.reject(new WorkerCrashError(workerId, exitCode));\n }\n }\n\n // Auto-restart if needed\n if (\n this.config.autoRestart &&\n !this.isShuttingDown &&\n !this.isShutdown &&\n this.workers.size < this.config.minWorkers\n ) {\n const newWorker = this.createWorker();\n if (newWorker && this.taskQueue.length > 0) {\n const nextTask = this.taskQueue.shift()!;\n this.assignTaskToWorker(newWorker, nextTask);\n }\n }\n }\n\n private startIdleCheck(): void {\n this.idleCheckInterval = setInterval(() => {\n this.checkIdleWorkers();\n }, 5000); // Check every 5 seconds\n\n // Don't prevent process from exiting\n this.idleCheckInterval.unref();\n }\n\n private checkIdleWorkers(): void {\n if (this.isShuttingDown || this.isShutdown) {\n return;\n }\n\n const now = Date.now();\n const workersToTerminate: number[] = [];\n\n for (const [workerId, state] of this.workers) {\n // Don't terminate if we're at minimum\n if (this.workers.size <= this.config.minWorkers) {\n break;\n }\n\n // Check if worker is idle and past timeout\n if (\n !state.busy &&\n state.idleSince &&\n now - state.idleSince > this.config.idleTimeout\n ) {\n workersToTerminate.push(workerId);\n\n // Don't terminate below minimum\n if (\n this.workers.size - workersToTerminate.length <=\n this.config.minWorkers\n ) {\n break;\n }\n }\n }\n\n // Terminate idle workers\n for (const workerId of workersToTerminate) {\n const state = this.workers.get(workerId);\n if (state) {\n this.workers.delete(workerId);\n state.worker.terminate().catch(() => {\n // Ignore termination errors\n });\n }\n }\n }\n}\n","/**\n * WorkerPool Custom Errors\n * Phase 1.02: Worker Threads Implementation\n */\n\n/**\n * Base error class for worker-related errors\n */\nexport class WorkerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'WorkerError';\n // Maintain proper stack trace in V8\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Thrown when a task exceeds its timeout\n */\nexport class WorkerTimeoutError extends WorkerError {\n public readonly taskId: string;\n public readonly timeout: number;\n\n constructor(taskId: string, timeout: number) {\n super(`Task ${taskId} timed out after ${timeout}ms`);\n this.name = 'WorkerTimeoutError';\n this.taskId = taskId;\n this.timeout = timeout;\n }\n}\n\n/**\n * Thrown when a task fails during execution\n */\nexport class WorkerTaskError extends WorkerError {\n public readonly taskId: string;\n public readonly originalError: string;\n\n constructor(taskId: string, originalError: string) {\n super(`Task ${taskId} failed: ${originalError}`);\n this.name = 'WorkerTaskError';\n this.taskId = taskId;\n this.originalError = originalError;\n }\n}\n\n/**\n * Thrown when trying to submit a task to a shutdown pool\n */\nexport class WorkerPoolShutdownError extends WorkerError {\n constructor() {\n super('Cannot submit task: WorkerPool is shutting down or already shut down');\n this.name = 'WorkerPoolShutdownError';\n }\n}\n\n/**\n * Thrown when a worker crashes unexpectedly\n */\nexport class WorkerCrashError extends WorkerError {\n public readonly workerId: number;\n public readonly exitCode: number | null;\n\n constructor(workerId: number, exitCode: number | null) {\n super(`Worker ${workerId} crashed with exit code ${exitCode}`);\n this.name = 'WorkerCrashError';\n this.workerId = workerId;\n this.exitCode = exitCode;\n }\n}\n","/**\n * MerkleWorker - High-level API for Merkle tree operations in worker threads\n * Phase 1.03: MerkleWorker Implementation\n *\n * Provides a clean interface for CPU-intensive Merkle tree operations.\n * Delegates actual work to worker threads via WorkerPool.\n */\n\nimport { join } from 'path';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n MerkleHashPayload,\n MerkleHashResult,\n ORMapMerkleHashPayload,\n ORMapMerkleHashResult,\n MerkleDiffPayload,\n MerkleDiffResult,\n MerkleRebuildPayload,\n MerkleRebuildResult,\n ORMapMerkleRebuildPayload,\n BucketInfo,\n} from './merkle-types';\nimport { hashString as coreHashString } from '@topgunbuild/core';\n\n// Threshold: use worker only if entries exceed this count\nconst WORKER_THRESHOLD = 10;\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `merkle-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * MerkleWorker provides methods for Merkle tree operations.\n * Automatically decides whether to use worker threads based on workload size.\n */\nexport class MerkleWorker {\n private readonly pool: WorkerPool;\n private readonly workerScript: string;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n // Resolve path to merkle worker script\n this.workerScript = this.resolveMerkleWorkerScript();\n }\n\n private resolveMerkleWorkerScript(): string {\n const jsPath = join(__dirname, 'worker-scripts', 'merkle.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'merkle.worker.ts');\n\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n return tsPath;\n }\n }\n\n /**\n * Compute hashes for a batch of LWWMap entries.\n * Uses worker thread if entries count exceeds threshold.\n */\n async computeHashes(payload: MerkleHashPayload): Promise<MerkleHashResult> {\n if (payload.entries.length < WORKER_THRESHOLD) {\n // Execute inline for small batches\n return this.computeHashesInline(payload);\n }\n\n const task: WorkerTask<MerkleHashPayload, MerkleHashResult> = {\n id: generateTaskId(),\n type: 'merkle-hash' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Compute hashes for a batch of ORMap entries.\n * Uses worker thread if entries count exceeds threshold.\n */\n async computeORMapHashes(payload: ORMapMerkleHashPayload): Promise<ORMapMerkleHashResult> {\n if (payload.entries.length < WORKER_THRESHOLD) {\n return this.computeORMapHashesInline(payload);\n }\n\n const task: WorkerTask<ORMapMerkleHashPayload, ORMapMerkleHashResult> = {\n id: generateTaskId(),\n type: 'merkle-hash-ormap' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Find differences between local and remote Merkle trees.\n */\n async diff(payload: MerkleDiffPayload): Promise<MerkleDiffResult> {\n const totalKeys =\n payload.localBuckets.reduce((sum, [, b]) => sum + b.keys.length, 0) +\n payload.remoteBuckets.reduce((sum, [, b]) => sum + b.keys.length, 0);\n\n if (totalKeys < WORKER_THRESHOLD * 2) {\n return this.diffInline(payload);\n }\n\n const task: WorkerTask<MerkleDiffPayload, MerkleDiffResult> = {\n id: generateTaskId(),\n type: 'merkle-diff' as WorkerTaskType,\n payload,\n priority: 'high', // Sync operations should be prioritized\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Rebuild Merkle tree from LWWMap records.\n * Always uses worker thread as this is typically a heavy operation.\n */\n async rebuild(payload: MerkleRebuildPayload): Promise<MerkleRebuildResult> {\n if (payload.records.length < WORKER_THRESHOLD) {\n return this.rebuildInline(payload);\n }\n\n const task: WorkerTask<MerkleRebuildPayload, MerkleRebuildResult> = {\n id: generateTaskId(),\n type: 'merkle-rebuild' as WorkerTaskType,\n payload,\n priority: 'low', // Rebuild can wait\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Rebuild Merkle tree from ORMap records.\n */\n async rebuildORMap(payload: ORMapMerkleRebuildPayload): Promise<MerkleRebuildResult> {\n if (payload.records.length < WORKER_THRESHOLD) {\n return this.rebuildORMapInline(payload);\n }\n\n const task: WorkerTask<ORMapMerkleRebuildPayload, MerkleRebuildResult> = {\n id: generateTaskId(),\n type: 'merkle-rebuild-ormap' as WorkerTaskType,\n payload,\n priority: 'low',\n };\n\n return this.pool.submit(task);\n }\n\n // ============ Inline implementations for small batches ============\n\n private computeHashesInline(payload: MerkleHashPayload): MerkleHashResult {\n const { entries, depth = 3 } = payload;\n const hashes: Array<[string, number]> = [];\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const entry of entries) {\n const itemHash = this.hashString(\n `${entry.key}:${entry.timestamp.millis}:${entry.timestamp.counter}:${entry.timestamp.nodeId}`\n );\n hashes.push([entry.key, itemHash]);\n hashEntries.push({ key: entry.key, hash: itemHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n hashes,\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private computeORMapHashesInline(payload: ORMapMerkleHashPayload): ORMapMerkleHashResult {\n const { entries, depth = 3 } = payload;\n const hashes: Array<[string, number]> = [];\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const entry of entries) {\n const sortedRecords = [...entry.records].sort((a, b) => a.tag.localeCompare(b.tag));\n let combinedStr = entry.key;\n for (const record of sortedRecords) {\n combinedStr += `:${record.tag}:${record.timestamp.millis}:${record.timestamp.counter}:${record.timestamp.nodeId}`;\n }\n const entryHash = this.hashString(combinedStr);\n hashes.push([entry.key, entryHash]);\n hashEntries.push({ key: entry.key, hash: entryHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n hashes,\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private diffInline(payload: MerkleDiffPayload): MerkleDiffResult {\n const localMap = new Map<string, BucketInfo>(payload.localBuckets);\n const remoteMap = new Map<string, BucketInfo>(payload.remoteBuckets);\n\n const missingLocal: string[] = [];\n const missingRemote: string[] = [];\n const differingPaths: string[] = [];\n\n for (const [path, remoteBucket] of remoteMap) {\n const localBucket = localMap.get(path);\n\n if (!localBucket) {\n missingLocal.push(...remoteBucket.keys);\n } else if (localBucket.hash !== remoteBucket.hash) {\n differingPaths.push(path);\n\n const localKeys = new Set(localBucket.keys);\n const remoteKeys = new Set(remoteBucket.keys);\n\n for (const key of remoteKeys) {\n if (!localKeys.has(key)) {\n missingLocal.push(key);\n }\n }\n\n for (const key of localKeys) {\n if (!remoteKeys.has(key)) {\n missingRemote.push(key);\n }\n }\n }\n }\n\n for (const [path, localBucket] of localMap) {\n if (!remoteMap.has(path)) {\n missingRemote.push(...localBucket.keys);\n }\n }\n\n return { missingLocal, missingRemote, differingPaths };\n }\n\n private rebuildInline(payload: MerkleRebuildPayload): MerkleRebuildResult {\n const { records, depth = 3 } = payload;\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const record of records) {\n const itemHash = this.hashString(\n `${record.key}:${record.timestamp.millis}:${record.timestamp.counter}:${record.timestamp.nodeId}`\n );\n hashEntries.push({ key: record.key, hash: itemHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n private rebuildORMapInline(payload: ORMapMerkleRebuildPayload): MerkleRebuildResult {\n const { records, depth = 3 } = payload;\n const hashEntries: Array<{ key: string; hash: number }> = [];\n\n for (const record of records) {\n const sortedTags = [...record.tags].sort((a, b) => a.tag.localeCompare(b.tag));\n let combinedStr = record.key;\n for (const tag of sortedTags) {\n combinedStr += `:${tag.tag}:${tag.timestamp.millis}:${tag.timestamp.counter}:${tag.timestamp.nodeId}`;\n }\n const entryHash = this.hashString(combinedStr);\n hashEntries.push({ key: record.key, hash: entryHash });\n }\n\n const { root, buckets } = this.buildTree(hashEntries, depth);\n\n return {\n rootHash: root.hash,\n buckets: Array.from(buckets.entries()),\n };\n }\n\n // ============ Hash utilities ============\n\n private hashString(str: string): number {\n return coreHashString(str);\n }\n\n private buildTree(\n entries: Array<{ key: string; hash: number }>,\n depth: number\n ): { root: { hash: number }; buckets: Map<string, { hash: number; keys: string[] }> } {\n interface Node {\n hash: number;\n children?: { [key: string]: Node };\n entries?: Map<string, number>;\n }\n\n const root: Node = { hash: 0, children: {} };\n const buckets = new Map<string, { hash: number; keys: string[] }>();\n\n const updateNode = (\n node: Node,\n key: string,\n itemHash: number,\n pathHash: string,\n level: number\n ): number => {\n if (level >= depth) {\n if (!node.entries) node.entries = new Map();\n node.entries.set(key, itemHash);\n\n let h = 0;\n for (const val of node.entries.values()) {\n h = (h + val) | 0;\n }\n node.hash = h >>> 0;\n return node.hash;\n }\n\n const bucketChar = pathHash[level];\n if (!node.children) node.children = {};\n if (!node.children[bucketChar]) {\n node.children[bucketChar] = { hash: 0 };\n }\n\n updateNode(node.children[bucketChar], key, itemHash, pathHash, level + 1);\n\n let h = 0;\n for (const child of Object.values(node.children)) {\n h = (h + child.hash) | 0;\n }\n node.hash = h >>> 0;\n return node.hash;\n };\n\n for (const { key, hash } of entries) {\n const pathHash = this.hashString(key).toString(16).padStart(8, '0');\n updateNode(root, key, hash, pathHash, 0);\n }\n\n // Collect buckets\n const collectBuckets = (node: Node, path: string): void => {\n if (path.length >= depth) {\n if (node.entries && node.entries.size > 0) {\n buckets.set(path, {\n hash: node.hash,\n keys: Array.from(node.entries.keys()),\n });\n }\n return;\n }\n\n if (node.children) {\n for (const [char, child] of Object.entries(node.children)) {\n collectBuckets(child, path + char);\n }\n }\n };\n\n collectBuckets(root, '');\n\n return { root, buckets };\n }\n}\n","/**\n * CRDTMergeWorker - High-level API for CRDT merge operations in worker threads\n * Phase 1.04: CRDTMergeWorker Implementation\n *\n * Provides a clean interface for CPU-intensive CRDT merge operations.\n * Delegates actual work to worker threads via WorkerPool for large batches.\n */\n\nimport { join } from 'path';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n LWWMergePayload,\n LWWMergeResult,\n ORMapMergePayload,\n ORMapMergeResult,\n LWWMergeRecord,\n LWWExistingRecord,\n ORMapMergeItem,\n ORMapMergeTombstone,\n} from './crdt-types';\n\n// Threshold: use worker only if batch exceeds this count\nconst WORKER_THRESHOLD = 10;\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `crdt-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * Compare two timestamps (same logic as HLC.compare)\n */\nfunction compareTimestamps(\n a: { millis: number; counter: number; nodeId: string },\n b: { millis: number; counter: number; nodeId: string }\n): number {\n if (a.millis !== b.millis) {\n return a.millis - b.millis;\n }\n if (a.counter !== b.counter) {\n return a.counter - b.counter;\n }\n return a.nodeId.localeCompare(b.nodeId);\n}\n\n/**\n * CRDTMergeWorker provides methods for CRDT merge operations.\n * Automatically decides whether to use worker threads based on batch size.\n */\nexport class CRDTMergeWorker {\n private readonly pool: WorkerPool;\n\n /** Threshold for using worker (operations below this go to main thread) */\n static readonly BATCH_THRESHOLD = WORKER_THRESHOLD;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n }\n\n /**\n * Decide if batch should go to worker\n */\n shouldUseWorker(batchSize: number): boolean {\n return batchSize >= WORKER_THRESHOLD;\n }\n\n /**\n * Merge LWW records\n * @param payload - Records to merge and existing state\n * @returns Which records should be applied\n */\n async mergeLWW(payload: LWWMergePayload): Promise<LWWMergeResult> {\n if (!this.shouldUseWorker(payload.records.length)) {\n return this.mergeLWWInline(payload);\n }\n\n const task: WorkerTask<LWWMergePayload, LWWMergeResult> = {\n id: generateTaskId(),\n type: 'lww-merge' as WorkerTaskType,\n payload,\n priority: 'high', // Merge operations should be prioritized\n };\n\n return this.pool.submit(task);\n }\n\n /**\n * Merge ORMap items and tombstones\n * @param payload - Items, tombstones, and existing state\n * @returns Which items/tombstones should be applied\n */\n async mergeORMap(payload: ORMapMergePayload): Promise<ORMapMergeResult> {\n const totalOps = payload.items.length + payload.tombstones.length;\n\n if (!this.shouldUseWorker(totalOps)) {\n return this.mergeORMapInline(payload);\n }\n\n const task: WorkerTask<ORMapMergePayload, ORMapMergeResult> = {\n id: generateTaskId(),\n type: 'ormap-merge' as WorkerTaskType,\n payload,\n priority: 'high',\n };\n\n return this.pool.submit(task);\n }\n\n // ============ Inline implementations for small batches ============\n\n private mergeLWWInline(payload: LWWMergePayload): LWWMergeResult {\n const { records, existingState } = payload;\n\n // Build existing state map\n const existingMap = new Map<string, {\n value: unknown;\n timestamp: { millis: number; counter: number; nodeId: string };\n ttlMs?: number;\n }>();\n\n for (const existing of existingState) {\n existingMap.set(existing.key, {\n value: existing.value,\n timestamp: existing.timestamp,\n ttlMs: existing.ttlMs,\n });\n }\n\n const toApply: LWWMergeResult['toApply'] = [];\n const conflicts: string[] = [];\n let skipped = 0;\n\n for (const record of records) {\n const existing = existingMap.get(record.key);\n\n if (!existing) {\n toApply.push({\n key: record.key,\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n existingMap.set(record.key, {\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n continue;\n }\n\n const cmp = compareTimestamps(record.timestamp, existing.timestamp);\n\n // Detect conflict: same millis but different counter/nodeId (concurrent writes)\n const isConflict = record.timestamp.millis === existing.timestamp.millis &&\n (record.timestamp.counter !== existing.timestamp.counter ||\n record.timestamp.nodeId !== existing.timestamp.nodeId);\n\n if (cmp > 0) {\n // New record wins\n toApply.push({\n key: record.key,\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n existingMap.set(record.key, {\n value: record.value,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n });\n if (isConflict) {\n conflicts.push(record.key);\n }\n } else if (cmp === 0) {\n // Identical timestamps - skip but note conflict\n conflicts.push(record.key);\n skipped++;\n } else {\n // Old record - skip\n if (isConflict) {\n conflicts.push(record.key);\n }\n skipped++;\n }\n }\n\n return { toApply, skipped, conflicts };\n }\n\n private mergeORMapInline(payload: ORMapMergePayload): ORMapMergeResult {\n const { items, tombstones, existingTags, existingTombstones } = payload;\n\n const tagSet = new Set(existingTags);\n const tombstoneSet = new Set(existingTombstones);\n\n const itemsToApply: ORMapMergeResult['itemsToApply'] = [];\n const tombstonesToApply: string[] = [];\n const tagsToRemove: string[] = [];\n let itemsSkipped = 0;\n let tombstonesSkipped = 0;\n\n // Process tombstones first\n for (const tombstone of tombstones) {\n if (tombstoneSet.has(tombstone.tag)) {\n tombstonesSkipped++;\n continue;\n }\n\n tombstonesToApply.push(tombstone.tag);\n tombstoneSet.add(tombstone.tag);\n\n if (tagSet.has(tombstone.tag)) {\n tagsToRemove.push(tombstone.tag);\n tagSet.delete(tombstone.tag);\n }\n }\n\n // Process items\n for (const item of items) {\n if (tombstoneSet.has(item.tag)) {\n itemsSkipped++;\n continue;\n }\n\n if (tagSet.has(item.tag)) {\n itemsSkipped++;\n continue;\n }\n\n itemsToApply.push({\n key: item.key,\n value: item.value,\n timestamp: item.timestamp,\n tag: item.tag,\n ttlMs: item.ttlMs,\n });\n tagSet.add(item.tag);\n }\n\n return {\n itemsToApply,\n tombstonesToApply,\n tagsToRemove,\n itemsSkipped,\n tombstonesSkipped,\n };\n }\n}\n","/**\n * SerializationWorker - High-level API for serialization operations in worker threads\n * Phase 1.07: SerializationWorker Implementation\n *\n * Provides a clean interface for CPU-intensive serialization/deserialization.\n * Delegates actual work to worker threads via WorkerPool for large payloads.\n *\n * Uses base64 encoding to transfer binary data through postMessage.\n * This adds ~33% overhead but is necessary since Uint8Array cannot be\n * directly transferred through structured clone algorithm for our use case.\n */\n\nimport { join } from 'path';\nimport { serialize as coreSerialize, deserialize as coreDeserialize } from '@topgunbuild/core';\nimport { WorkerPool } from './WorkerPool';\nimport type { WorkerTask, WorkerTaskType } from './types';\nimport type {\n SerializeBatchPayload,\n SerializeBatchResult,\n DeserializeBatchPayload,\n DeserializeBatchResult,\n} from './serialization-types';\n\n// Threshold: use worker only if batch exceeds this count OR total size is large\nconst WORKER_BATCH_THRESHOLD = 10;\nconst WORKER_SIZE_THRESHOLD = 50 * 1024; // 50 KB estimated payload size\n\nlet taskIdCounter = 0;\n\nfunction generateTaskId(): string {\n return `ser-${Date.now()}-${++taskIdCounter}`;\n}\n\n/**\n * Convert Uint8Array to base64 string\n */\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Convert base64 string back to Uint8Array\n */\nfunction base64ToUint8Array(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Estimate serialized size of an object (rough heuristic)\n * MessagePack typically produces smaller output than JSON.stringify\n */\nfunction estimateSize(obj: unknown): number {\n if (obj === null || obj === undefined) return 1;\n if (typeof obj === 'boolean') return 1;\n if (typeof obj === 'number') return 9; // worst case: double\n if (typeof obj === 'string') return (obj as string).length + 5;\n if (Array.isArray(obj)) {\n let size = 5;\n for (const item of obj) {\n size += estimateSize(item);\n }\n return size;\n }\n if (typeof obj === 'object') {\n let size = 5;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n size += key.length + 5 + estimateSize(value);\n }\n return size;\n }\n return 10;\n}\n\n/**\n * SerializationWorker provides methods for serialization operations.\n * Automatically decides whether to use worker threads based on payload size.\n */\nexport class SerializationWorker {\n private readonly pool: WorkerPool;\n private readonly workerScript: string;\n\n /** Threshold for using worker (items below this go to main thread) */\n static readonly BATCH_THRESHOLD = WORKER_BATCH_THRESHOLD;\n /** Size threshold for using worker (bytes) */\n static readonly SIZE_THRESHOLD = WORKER_SIZE_THRESHOLD;\n\n constructor(pool: WorkerPool) {\n this.pool = pool;\n this.workerScript = this.resolveWorkerScript();\n }\n\n private resolveWorkerScript(): string {\n const jsPath = join(__dirname, 'worker-scripts', 'serialization.worker.js');\n const tsPath = join(__dirname, 'worker-scripts', 'serialization.worker.ts');\n\n try {\n require.resolve(jsPath);\n return jsPath;\n } catch {\n return tsPath;\n }\n }\n\n /**\n * Decide if batch should go to worker based on count or size\n */\n shouldUseWorker(items: unknown[]): boolean {\n if (items.length >= WORKER_BATCH_THRESHOLD) {\n return true;\n }\n\n // Estimate total size\n let totalSize = 0;\n for (const item of items) {\n totalSize += estimateSize(item);\n if (totalSize >= WORKER_SIZE_THRESHOLD) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Serialize multiple objects to MessagePack binary format.\n * Uses worker thread for large batches.\n *\n * @param items - Objects to serialize\n * @returns Array of Uint8Array containing serialized data\n */\n async serializeBatch(items: unknown[]): Promise<Uint8Array[]> {\n if (items.length === 0) {\n return [];\n }\n\n if (!this.shouldUseWorker(items)) {\n return this.serializeBatchInline(items);\n }\n\n const payload: SerializeBatchPayload = { items };\n const task: WorkerTask<SerializeBatchPayload, SerializeBatchResult> = {\n id: generateTaskId(),\n type: 'serialize' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n const result = await this.pool.submit(task);\n\n // Convert base64 strings back to Uint8Array\n return result.serialized.map(base64ToUint8Array);\n }\n\n /**\n * Deserialize multiple MessagePack binary payloads to objects.\n * Uses worker thread for large batches.\n *\n * @param items - Binary data to deserialize\n * @returns Array of deserialized objects\n */\n async deserializeBatch<T = unknown>(items: Uint8Array[]): Promise<T[]> {\n if (items.length === 0) {\n return [];\n }\n\n if (items.length < WORKER_BATCH_THRESHOLD) {\n return this.deserializeBatchInline(items);\n }\n\n // Convert to base64 for worker transfer\n const base64Items = items.map(uint8ArrayToBase64);\n\n const payload: DeserializeBatchPayload = { items: base64Items };\n const task: WorkerTask<DeserializeBatchPayload, DeserializeBatchResult> = {\n id: generateTaskId(),\n type: 'deserialize' as WorkerTaskType,\n payload,\n priority: 'normal',\n };\n\n const result = await this.pool.submit(task);\n return result.deserialized as T[];\n }\n\n /**\n * Serialize a single object (always inline, too small for worker)\n */\n serialize(data: unknown): Uint8Array {\n return coreSerialize(data);\n }\n\n /**\n * Deserialize a single payload (always inline, too small for worker)\n */\n deserialize<T = unknown>(data: Uint8Array | ArrayBuffer): T {\n return coreDeserialize(data) as T;\n }\n\n // ============ Inline implementations for small batches ============\n\n private serializeBatchInline(items: unknown[]): Uint8Array[] {\n const results: Uint8Array[] = [];\n for (const item of items) {\n results.push(coreSerialize(item));\n }\n return results;\n }\n\n private deserializeBatchInline<T>(items: Uint8Array[]): T[] {\n const results: T[] = [];\n for (const item of items) {\n results.push(coreDeserialize(item) as T);\n }\n return results;\n }\n}\n","/**\n * SharedMemoryManager - Zero-copy data transfer between main thread and workers\n *\n * Uses SharedArrayBuffer with Atomics for synchronization.\n * Provides slot-based allocation for concurrent operations.\n *\n * Phase 3.04: SharedArrayBuffer Integration\n */\n\nexport interface SharedMemoryConfig {\n /**\n * Size of shared buffer in bytes.\n * Default: 16MB\n */\n bufferSize?: number;\n\n /**\n * Number of slots for concurrent operations.\n * Each slot can hold one transfer.\n * Default: 256\n */\n slotCount?: number;\n\n /**\n * Reserved bytes per slot for metadata (length, status, etc).\n * Default: 16 (must be multiple of 8 for alignment)\n */\n metadataSize?: number;\n}\n\nexport interface SharedMemoryStats {\n /** Total buffer size in bytes */\n totalSize: number;\n /** Number of slots */\n slotCount: number;\n /** Size of each slot in bytes */\n slotSize: number;\n /** Currently allocated slots */\n allocatedSlots: number;\n /** Available slots */\n availableSlots: number;\n /** Peak concurrent allocations */\n peakUsage: number;\n /** Total allocations since creation */\n totalAllocations: number;\n /** Total releases since creation */\n totalReleases: number;\n}\n\nexport interface SharedSlot {\n /** Slot index */\n index: number;\n /** View into shared buffer for this slot's data area */\n dataView: Uint8Array;\n /** Maximum data size (excluding metadata) */\n maxDataSize: number;\n}\n\n/**\n * Slot status values (stored in first 4 bytes of slot metadata)\n */\nexport enum SlotStatus {\n FREE = 0, // Slot is available\n ALLOCATED = 1, // Slot is allocated, no data yet\n DATA_READY = 2, // Data written, ready for reading\n PROCESSING = 3, // Worker is processing\n RESULT_READY = 4, // Worker has written result\n ERROR = 255, // Error occurred\n}\n\n/**\n * Slot metadata layout (16 bytes):\n * - Bytes 0-3: Status (Int32 for Atomics)\n * - Bytes 4-7: Data length (Uint32)\n * - Bytes 8-15: Reserved for future use\n */\nconst DEFAULT_METADATA_SIZE = 16;\n\n/**\n * Manages shared memory for zero-copy data transfer between threads.\n *\n * Usage:\n * 1. Main thread allocates a slot\n * 2. Main thread writes data to slot\n * 3. Main thread sends slot index to worker via postMessage\n * 4. Worker reads data from shared memory (zero-copy)\n * 5. Worker writes result to slot\n * 6. Main thread reads result (zero-copy)\n * 7. Main thread releases slot\n */\nexport class SharedMemoryManager {\n private readonly buffer: SharedArrayBuffer;\n private readonly statusArray: Int32Array; // For Atomics operations\n private readonly slotSize: number;\n private readonly slotCount: number;\n private readonly metadataSize: number;\n private readonly freeSlots: Set<number>;\n\n // Stats\n private allocatedCount = 0;\n private peakUsage = 0;\n private totalAllocations = 0;\n private totalReleases = 0;\n\n constructor(config?: SharedMemoryConfig) {\n const bufferSize = config?.bufferSize ?? 16 * 1024 * 1024; // 16MB\n this.slotCount = config?.slotCount ?? 256;\n this.metadataSize = config?.metadataSize ?? DEFAULT_METADATA_SIZE;\n\n // Ensure metadata size is aligned to 8 bytes\n if (this.metadataSize % 8 !== 0) {\n throw new Error('metadataSize must be a multiple of 8');\n }\n\n // Calculate slot size\n this.slotSize = Math.floor(bufferSize / this.slotCount);\n\n // Ensure slot size is aligned to 8 bytes\n this.slotSize = Math.floor(this.slotSize / 8) * 8;\n\n if (this.slotSize <= this.metadataSize) {\n throw new Error('Buffer too small for given slot count');\n }\n\n // Allocate shared buffer\n const actualSize = this.slotSize * this.slotCount;\n this.buffer = new SharedArrayBuffer(actualSize);\n\n // Create Int32Array view for Atomics operations on status fields\n this.statusArray = new Int32Array(this.buffer);\n\n // Initialize free slots\n this.freeSlots = new Set();\n for (let i = 0; i < this.slotCount; i++) {\n this.freeSlots.add(i);\n // Initialize status to FREE\n Atomics.store(this.statusArray, this.getStatusOffset(i), SlotStatus.FREE);\n }\n }\n\n /**\n * Get offset in Int32Array for slot status.\n * Status is at the beginning of each slot's metadata.\n */\n private getStatusOffset(slotIndex: number): number {\n return (slotIndex * this.slotSize) / 4; // Int32 = 4 bytes\n }\n\n /**\n * Get byte offset for slot length field.\n */\n private getLengthOffset(slotIndex: number): number {\n return slotIndex * this.slotSize + 4; // After status (4 bytes)\n }\n\n /**\n * Get byte offset for slot data (after metadata).\n */\n private getDataOffset(slotIndex: number): number {\n return slotIndex * this.slotSize + this.metadataSize;\n }\n\n /**\n * Allocate a slot for data transfer.\n * Returns null if no slots available.\n */\n allocate(): SharedSlot | null {\n // Find free slot\n const iterator = this.freeSlots.values();\n const result = iterator.next();\n\n if (result.done) {\n return null; // No free slots\n }\n\n const index = result.value;\n this.freeSlots.delete(index);\n\n // Update stats\n this.allocatedCount++;\n this.totalAllocations++;\n this.peakUsage = Math.max(this.peakUsage, this.allocatedCount);\n\n // Set status to ALLOCATED using Atomics\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(index),\n SlotStatus.ALLOCATED\n );\n\n // Create data view\n const dataOffset = this.getDataOffset(index);\n const maxDataSize = this.slotSize - this.metadataSize;\n\n return {\n index,\n dataView: new Uint8Array(this.buffer, dataOffset, maxDataSize),\n maxDataSize,\n };\n }\n\n /**\n * Release a slot back to the pool.\n */\n release(slot: SharedSlot): void {\n if (this.freeSlots.has(slot.index)) {\n return; // Already free\n }\n\n // Set status to FREE\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(slot.index),\n SlotStatus.FREE\n );\n\n // Return to pool\n this.freeSlots.add(slot.index);\n this.allocatedCount--;\n this.totalReleases++;\n }\n\n /**\n * Write data to a slot and signal it's ready.\n * Returns false if data is too large.\n */\n writeData(slot: SharedSlot, data: Uint8Array): boolean {\n if (data.length > slot.maxDataSize) {\n return false; // Data too large\n }\n\n // Write length\n const lengthView = new DataView(\n this.buffer,\n this.getLengthOffset(slot.index),\n 4\n );\n lengthView.setUint32(0, data.length, true); // little-endian\n\n // Write data\n slot.dataView.set(data);\n\n // Signal data is ready (memory barrier via Atomics)\n Atomics.store(\n this.statusArray,\n this.getStatusOffset(slot.index),\n SlotStatus.DATA_READY\n );\n // Wake up any waiting workers\n Atomics.notify(this.statusArray, this.getStatusOffset(slot.index));\n\n return true;\n }\n\n /**\n * Read data length from a slot.\n */\n getDataLength(slotIndex: number): number {\n const lengthView = new DataView(\n this.buffer,\n this.getLengthOffset(slotIndex),\n 4\n );\n return lengthView.getUint32(0, true);\n }\n\n /**\n * Get data view for a slot (for reading).\n */\n getDataView(slotIndex: number): Uint8Array {\n const length = this.getDataLength(slotIndex);\n const dataOffset = this.getDataOffset(slotIndex);\n return new Uint8Array(this.buffer, dataOffset, length);\n }\n\n /**\n * Get slot status.\n */\n getStatus(slotIndex: number): SlotStatus {\n return Atomics.load(this.statusArray, this.getStatusOffset(slotIndex));\n }\n\n /**\n * Wait for a specific status with timeout.\n * Returns the actual status (may differ if timeout occurred).\n */\n waitForStatus(\n slotIndex: number,\n expectedStatus: SlotStatus,\n timeoutMs: number = 5000\n ): SlotStatus {\n const statusOffset = this.getStatusOffset(slotIndex);\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const status = Atomics.load(this.statusArray, statusOffset);\n\n if (status === expectedStatus || status === SlotStatus.ERROR) {\n return status;\n }\n\n // Wait with timeout\n const remaining = deadline - Date.now();\n if (remaining > 0) {\n Atomics.wait(\n this.statusArray,\n statusOffset,\n status,\n Math.min(remaining, 100)\n );\n }\n }\n\n return Atomics.load(this.statusArray, statusOffset);\n }\n\n /**\n * Wait for result and read it.\n * Returns null on timeout or error.\n */\n waitForResult(slot: SharedSlot, timeoutMs: number = 5000): Uint8Array | null {\n const status = this.waitForStatus(\n slot.index,\n SlotStatus.RESULT_READY,\n timeoutMs\n );\n\n if (status === SlotStatus.RESULT_READY) {\n // Read length\n const length = this.getDataLength(slot.index);\n\n // Copy result (we need to copy because slot will be reused)\n const result = new Uint8Array(length);\n result.set(slot.dataView.subarray(0, length));\n\n return result;\n }\n\n return null; // Timeout or error\n }\n\n /**\n * Get the SharedArrayBuffer for passing to workers.\n */\n getBuffer(): SharedArrayBuffer {\n return this.buffer;\n }\n\n /**\n * Get configuration needed by workers.\n */\n getWorkerConfig(): SharedWorkerConfig {\n return {\n sharedBuffer: this.buffer,\n slotSize: this.slotSize,\n slotCount: this.slotCount,\n metadataSize: this.metadataSize,\n };\n }\n\n /**\n * Get statistics.\n */\n getStats(): SharedMemoryStats {\n return {\n totalSize: this.buffer.byteLength,\n slotCount: this.slotCount,\n slotSize: this.slotSize,\n allocatedSlots: this.allocatedCount,\n availableSlots: this.freeSlots.size,\n peakUsage: this.peakUsage,\n totalAllocations: this.totalAllocations,\n totalReleases: this.totalReleases,\n };\n }\n\n /**\n * Check if SharedArrayBuffer is available in current environment.\n */\n static isAvailable(): boolean {\n try {\n new SharedArrayBuffer(1);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Shutdown and release resources.\n * Resets all slots to FREE status.\n */\n shutdown(): void {\n // Reset all slots to FREE\n for (let i = 0; i < this.slotCount; i++) {\n Atomics.store(this.statusArray, this.getStatusOffset(i), SlotStatus.FREE);\n }\n this.freeSlots.clear();\n for (let i = 0; i < this.slotCount; i++) {\n this.freeSlots.add(i);\n }\n this.allocatedCount = 0;\n }\n}\n\n/**\n * Configuration passed to workers for shared memory access.\n */\nexport interface SharedWorkerConfig {\n sharedBuffer: SharedArrayBuffer;\n slotSize: number;\n slotCount: number;\n metadataSize: number;\n}\n","/**\n * TaskletScheduler — Cooperative multitasking for long-running operations.\n *\n * Inspired by Hazelcast's Tasklet pattern, this scheduler allows long operations\n * to yield control back to the event loop periodically, preventing event loop\n * blocking and maintaining responsiveness.\n *\n * Key concepts:\n * - Tasklet: A unit of work that can be paused and resumed\n * - Time budget: Maximum time a tasklet can run before yielding\n * - Cooperative scheduling: Tasklets voluntarily yield when time budget is exhausted\n */\n\n/**\n * Progress state returned by a tasklet after each execution step.\n */\nexport type ProgressState =\n | 'DONE' // Tasklet completed all work\n | 'MADE_PROGRESS' // Tasklet made progress but has more work\n | 'NO_PROGRESS'; // Tasklet couldn't make progress (e.g., waiting for I/O)\n\n/**\n * Interface for a tasklet that can be scheduled.\n */\nexport interface Tasklet<T = void> {\n /** Unique name for logging/metrics */\n readonly name: string;\n\n /**\n * Execute one chunk of work.\n * Should check time budget and return appropriate state.\n */\n call(): ProgressState;\n\n /**\n * Get the result after tasklet is DONE.\n * Only valid when call() returns 'DONE'.\n */\n getResult(): T;\n\n /**\n * Called when tasklet is cancelled before completion.\n */\n onCancel?(): void;\n}\n\n/**\n * Configuration for TaskletScheduler.\n */\nexport interface TaskletSchedulerConfig {\n /** Default time budget per tasklet execution in ms (default: 5) */\n defaultTimeBudgetMs?: number;\n\n /** Maximum concurrent tasklets (default: 10) */\n maxConcurrent?: number;\n\n /** Interval between scheduler ticks in ms (default: 1) */\n tickIntervalMs?: number;\n\n /** Enable metrics collection (default: true) */\n metricsEnabled?: boolean;\n}\n\n/**\n * Metrics for monitoring scheduler performance.\n */\nexport interface TaskletSchedulerStats {\n /** Total tasklets scheduled */\n totalScheduled: number;\n\n /** Tasklets currently running */\n activeTasklets: number;\n\n /** Tasklets completed successfully */\n completedTasklets: number;\n\n /** Tasklets cancelled */\n cancelledTasklets: number;\n\n /** Total iterations across all tasklets */\n totalIterations: number;\n\n /** Average iterations per tasklet */\n avgIterationsPerTasklet: number;\n\n /** Tasklets that completed in a single iteration */\n singleIterationCompletions: number;\n\n /** Time spent in tasklet execution (ms) */\n totalExecutionTimeMs: number;\n}\n\n/**\n * Internal state for a running tasklet.\n */\ninterface TaskletState<T> {\n tasklet: Tasklet<T>;\n resolve: (result: T) => void;\n reject: (error: Error) => void;\n iterations: number;\n startTime: number;\n lastProgressTime: number;\n}\n\nconst DEFAULT_CONFIG: Required<TaskletSchedulerConfig> = {\n defaultTimeBudgetMs: 5,\n maxConcurrent: 10,\n tickIntervalMs: 1,\n metricsEnabled: true,\n};\n\n/**\n * TaskletScheduler manages cooperative multitasking for long-running operations.\n *\n * Usage:\n * ```typescript\n * const scheduler = new TaskletScheduler();\n *\n * // Schedule a tasklet\n * const result = await scheduler.schedule(new QueryExecutionTasklet(records, query));\n *\n * // Cancel all running tasklets\n * scheduler.cancelAll();\n *\n * // Shutdown scheduler\n * scheduler.shutdown();\n * ```\n */\nexport class TaskletScheduler {\n private readonly config: Required<TaskletSchedulerConfig>;\n private readonly activeTasklets: Map<string, TaskletState<any>> = new Map();\n private tickTimer: NodeJS.Immediate | null = null;\n private isRunning = false;\n private isShuttingDown = false;\n\n // Metrics\n private totalScheduled = 0;\n private completedTasklets = 0;\n private cancelledTasklets = 0;\n private totalIterations = 0;\n private singleIterationCompletions = 0;\n private totalExecutionTimeMs = 0;\n\n constructor(config?: TaskletSchedulerConfig) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Schedule a tasklet for execution.\n * Returns a promise that resolves when the tasklet completes.\n */\n schedule<T>(tasklet: Tasklet<T>): Promise<T> {\n if (this.isShuttingDown) {\n return Promise.reject(new Error('Scheduler is shutting down'));\n }\n\n return new Promise<T>((resolve, reject) => {\n const taskletId = `${tasklet.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n // Check concurrent limit\n if (this.activeTasklets.size >= this.config.maxConcurrent) {\n reject(new Error(`Max concurrent tasklets (${this.config.maxConcurrent}) reached`));\n return;\n }\n\n const state: TaskletState<T> = {\n tasklet,\n resolve,\n reject,\n iterations: 0,\n startTime: Date.now(),\n lastProgressTime: Date.now(),\n };\n\n this.activeTasklets.set(taskletId, state);\n this.totalScheduled++;\n\n // Start scheduler if not running\n if (!this.isRunning) {\n this.startScheduler();\n }\n });\n }\n\n /**\n * Run a tasklet synchronously (blocking).\n * Useful for small operations or when cooperative scheduling isn't needed.\n */\n runSync<T>(tasklet: Tasklet<T>): T {\n let state: ProgressState;\n let iterations = 0;\n const startTime = Date.now();\n\n do {\n state = tasklet.call();\n iterations++;\n } while (state === 'MADE_PROGRESS');\n\n if (state === 'NO_PROGRESS') {\n throw new Error(`Tasklet ${tasklet.name} made no progress`);\n }\n\n if (this.config.metricsEnabled) {\n this.totalIterations += iterations;\n this.totalExecutionTimeMs += Date.now() - startTime;\n if (iterations === 1) {\n this.singleIterationCompletions++;\n }\n }\n\n return tasklet.getResult();\n }\n\n /**\n * Cancel a specific tasklet by name pattern.\n * Returns the number of tasklets cancelled.\n */\n cancel(namePattern: string | RegExp): number {\n let cancelled = 0;\n const pattern = typeof namePattern === 'string'\n ? new RegExp(`^${namePattern}`)\n : namePattern;\n\n for (const [id, state] of this.activeTasklets) {\n if (pattern.test(state.tasklet.name)) {\n this.cancelTasklet(id, state);\n cancelled++;\n }\n }\n\n return cancelled;\n }\n\n /**\n * Cancel all running tasklets.\n */\n cancelAll(): number {\n let cancelled = 0;\n\n for (const [id, state] of this.activeTasklets) {\n this.cancelTasklet(id, state);\n cancelled++;\n }\n\n return cancelled;\n }\n\n /**\n * Get scheduler statistics.\n */\n getStats(): TaskletSchedulerStats {\n return {\n totalScheduled: this.totalScheduled,\n activeTasklets: this.activeTasklets.size,\n completedTasklets: this.completedTasklets,\n cancelledTasklets: this.cancelledTasklets,\n totalIterations: this.totalIterations,\n avgIterationsPerTasklet: this.completedTasklets > 0\n ? this.totalIterations / this.completedTasklets\n : 0,\n singleIterationCompletions: this.singleIterationCompletions,\n totalExecutionTimeMs: this.totalExecutionTimeMs,\n };\n }\n\n /**\n * Reset statistics.\n */\n resetStats(): void {\n this.totalScheduled = 0;\n this.completedTasklets = 0;\n this.cancelledTasklets = 0;\n this.totalIterations = 0;\n this.singleIterationCompletions = 0;\n this.totalExecutionTimeMs = 0;\n }\n\n /**\n * Shutdown the scheduler.\n * Cancels all running tasklets and stops the tick timer.\n */\n shutdown(): void {\n this.isShuttingDown = true;\n this.cancelAll();\n this.stopScheduler();\n }\n\n /**\n * Check if scheduler is running.\n */\n get running(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get number of active tasklets.\n */\n get activeCount(): number {\n return this.activeTasklets.size;\n }\n\n private startScheduler(): void {\n if (this.isRunning) return;\n this.isRunning = true;\n this.scheduleTick();\n }\n\n private stopScheduler(): void {\n this.isRunning = false;\n if (this.tickTimer) {\n clearImmediate(this.tickTimer);\n this.tickTimer = null;\n }\n }\n\n private scheduleTick(): void {\n if (!this.isRunning) return;\n\n // Use setImmediate for minimal delay while allowing I/O\n this.tickTimer = setImmediate(() => {\n this.tick();\n });\n }\n\n private tick(): void {\n if (!this.isRunning || this.activeTasklets.size === 0) {\n this.stopScheduler();\n return;\n }\n\n const tickStart = Date.now();\n const taskletIds = Array.from(this.activeTasklets.keys());\n\n for (const id of taskletIds) {\n const state = this.activeTasklets.get(id);\n if (!state) continue;\n\n try {\n const iterationStart = Date.now();\n const result = state.tasklet.call();\n const iterationTime = Date.now() - iterationStart;\n\n state.iterations++;\n state.lastProgressTime = Date.now();\n\n if (this.config.metricsEnabled) {\n this.totalIterations++;\n this.totalExecutionTimeMs += iterationTime;\n }\n\n if (result === 'DONE') {\n this.completeTasklet(id, state);\n } else if (result === 'NO_PROGRESS') {\n // Tasklet couldn't make progress, will retry next tick\n }\n // MADE_PROGRESS: continue in next tick\n } catch (error) {\n this.failTasklet(id, state, error as Error);\n }\n\n // Check if we've exceeded our time budget for this tick\n if (Date.now() - tickStart > this.config.defaultTimeBudgetMs * 2) {\n break; // Process remaining tasklets in next tick\n }\n }\n\n // Schedule next tick if there are still active tasklets\n if (this.activeTasklets.size > 0) {\n this.scheduleTick();\n } else {\n this.stopScheduler();\n }\n }\n\n private completeTasklet<T>(id: string, state: TaskletState<T>): void {\n this.activeTasklets.delete(id);\n this.completedTasklets++;\n\n if (state.iterations === 1) {\n this.singleIterationCompletions++;\n }\n\n try {\n const result = state.tasklet.getResult();\n state.resolve(result);\n } catch (error) {\n state.reject(error as Error);\n }\n }\n\n private failTasklet<T>(id: string, state: TaskletState<T>, error: Error): void {\n this.activeTasklets.delete(id);\n state.reject(error);\n }\n\n private cancelTasklet<T>(id: string, state: TaskletState<T>): void {\n this.activeTasklets.delete(id);\n this.cancelledTasklets++;\n\n if (state.tasklet.onCancel) {\n try {\n state.tasklet.onCancel();\n } catch {\n // Ignore cancel errors\n }\n }\n\n state.reject(new Error(`Tasklet ${state.tasklet.name} was cancelled`));\n }\n}\n","/**\n * IteratorTasklet — Base class for tasklets that iterate over collections.\n *\n * Provides cooperative iteration with time-budgeted chunks.\n * Subclasses implement processItem() to handle each item.\n */\n\nimport { Tasklet, ProgressState } from '../TaskletScheduler';\n\n/**\n * Configuration for iterator tasklets.\n */\nexport interface IteratorTaskletConfig {\n /** Time budget per iteration in ms (default: 5) */\n timeBudgetMs?: number;\n\n /** Maximum items to process per iteration (default: 1000) */\n maxItemsPerIteration?: number;\n}\n\nconst DEFAULT_CONFIG: Required<IteratorTaskletConfig> = {\n timeBudgetMs: 5,\n maxItemsPerIteration: 1000,\n};\n\n/**\n * Abstract base class for iterator-based tasklets.\n *\n * Usage:\n * ```typescript\n * class MyTasklet extends IteratorTasklet<[string, Record], Record[]> {\n * constructor(map: Map<string, Record>) {\n * super('my-tasklet', map.entries());\n * }\n *\n * protected processItem([key, record]: [string, Record]): void {\n * if (matchesCriteria(record)) {\n * this.results.push(record);\n * }\n * }\n *\n * getResult(): Record[] {\n * return this.results;\n * }\n * }\n * ```\n */\nexport abstract class IteratorTasklet<TItem, TResult> implements Tasklet<TResult> {\n abstract readonly name: string;\n\n protected readonly config: Required<IteratorTaskletConfig>;\n protected readonly iterator: Iterator<TItem>;\n protected itemsProcessed = 0;\n protected isDone = false;\n\n constructor(\n iterator: Iterator<TItem>,\n config?: IteratorTaskletConfig\n ) {\n this.iterator = iterator;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Process a single item from the iterator.\n * Override this in subclasses.\n */\n protected abstract processItem(item: TItem): void;\n\n /**\n * Get the final result after iteration completes.\n */\n abstract getResult(): TResult;\n\n /**\n * Execute one chunk of iteration.\n */\n call(): ProgressState {\n if (this.isDone) {\n return 'DONE';\n }\n\n const deadline = Date.now() + this.config.timeBudgetMs;\n let processedThisIteration = 0;\n\n while (\n Date.now() < deadline &&\n processedThisIteration < this.config.maxItemsPerIteration\n ) {\n const { value, done } = this.iterator.next();\n\n if (done) {\n this.isDone = true;\n return 'DONE';\n }\n\n this.processItem(value);\n this.itemsProcessed++;\n processedThisIteration++;\n }\n\n return 'MADE_PROGRESS';\n }\n\n /**\n * Called when tasklet is cancelled.\n */\n onCancel(): void {\n // Subclasses can override for cleanup\n }\n\n /**\n * Get number of items processed so far.\n */\n get processed(): number {\n return this.itemsProcessed;\n }\n}\n\n/**\n * Simple iterator tasklet that collects items matching a predicate.\n */\nexport class FilterTasklet<T> extends IteratorTasklet<T, T[]> {\n readonly name: string;\n protected readonly results: T[] = [];\n private readonly predicate: (item: T) => boolean;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n predicate: (item: T) => boolean,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.predicate = predicate;\n }\n\n protected processItem(item: T): void {\n if (this.predicate(item)) {\n this.results.push(item);\n }\n }\n\n getResult(): T[] {\n return this.results;\n }\n}\n\n/**\n * Iterator tasklet that transforms items.\n */\nexport class MapTasklet<TIn, TOut> extends IteratorTasklet<TIn, TOut[]> {\n readonly name: string;\n protected readonly results: TOut[] = [];\n private readonly mapper: (item: TIn) => TOut;\n\n constructor(\n name: string,\n iterator: Iterator<TIn>,\n mapper: (item: TIn) => TOut,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.mapper = mapper;\n }\n\n protected processItem(item: TIn): void {\n this.results.push(this.mapper(item));\n }\n\n getResult(): TOut[] {\n return this.results;\n }\n}\n\n/**\n * Iterator tasklet that applies a function to each item (side effects).\n */\nexport class ForEachTasklet<T> extends IteratorTasklet<T, number> {\n readonly name: string;\n private readonly action: (item: T) => void;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n action: (item: T) => void,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.action = action;\n }\n\n protected processItem(item: T): void {\n this.action(item);\n }\n\n getResult(): number {\n return this.itemsProcessed;\n }\n}\n\n/**\n * Iterator tasklet that reduces items to a single value.\n */\nexport class ReduceTasklet<T, TAccum> extends IteratorTasklet<T, TAccum> {\n readonly name: string;\n private accumulator: TAccum;\n private readonly reducer: (acc: TAccum, item: T) => TAccum;\n\n constructor(\n name: string,\n iterator: Iterator<T>,\n initialValue: TAccum,\n reducer: (acc: TAccum, item: T) => TAccum,\n config?: IteratorTaskletConfig\n ) {\n super(iterator, config);\n this.name = name;\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n\n protected processItem(item: T): void {\n this.accumulator = this.reducer(this.accumulator, item);\n }\n\n getResult(): TAccum {\n return this.accumulator;\n }\n}\n","import { EventEmitter } from 'events';\nimport {\n WriteConcern,\n WriteResult,\n PendingWrite,\n DEFAULT_WRITE_CONCERN_TIMEOUT,\n isWriteConcernAchieved,\n getHighestWriteConcernLevel,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface WriteAckManagerConfig {\n /** Default timeout for write acknowledgments (ms) */\n defaultTimeout?: number;\n}\n\nexport interface WriteAckStats {\n /** Number of pending writes */\n pending: number;\n /** Pending writes by Write Concern level */\n byLevel: Record<WriteConcern, number>;\n}\n\n/**\n * Manages pending write acknowledgments for different Write Concern levels.\n *\n * Flow:\n * 1. Operation received → registerPending()\n * 2. Level achieved → notifyLevel()\n * 3. Target level reached → resolve promise, emit 'resolved'\n * 4. Timeout → resolve with partial success, emit 'timeout'\n *\n * @example\n * ```typescript\n * const ackManager = new WriteAckManager();\n *\n * // Register pending write\n * const promise = ackManager.registerPending(opId, WriteConcern.PERSISTED, 5000);\n *\n * // As processing progresses, notify levels\n * ackManager.notifyLevel(opId, WriteConcern.MEMORY); // Validated\n * ackManager.notifyLevel(opId, WriteConcern.APPLIED); // CRDT merged\n * ackManager.notifyLevel(opId, WriteConcern.REPLICATED); // Broadcast sent\n * ackManager.notifyLevel(opId, WriteConcern.PERSISTED); // Storage written\n *\n * // Promise resolves when target level is reached\n * const result = await promise;\n * ```\n */\nexport class WriteAckManager extends EventEmitter {\n private pending: Map<string, PendingWrite> = new Map();\n private readonly defaultTimeout: number;\n\n constructor(config?: WriteAckManagerConfig) {\n super();\n this.defaultTimeout = config?.defaultTimeout ?? DEFAULT_WRITE_CONCERN_TIMEOUT;\n }\n\n /**\n * Register a pending write operation.\n * Returns a promise that resolves when target Write Concern is achieved.\n *\n * @param opId - Operation ID\n * @param writeConcern - Target Write Concern level\n * @param timeout - Optional timeout in ms (defaults to config or 5000ms)\n * @returns Promise that resolves with WriteResult\n */\n registerPending(\n opId: string,\n writeConcern: WriteConcern,\n timeout?: number\n ): Promise<WriteResult> {\n // FIRE_AND_FORGET resolves immediately\n if (writeConcern === WriteConcern.FIRE_AND_FORGET) {\n return Promise.resolve({\n success: true,\n opId,\n achievedLevel: WriteConcern.FIRE_AND_FORGET,\n latencyMs: 0,\n });\n }\n\n return new Promise((resolve, reject) => {\n const effectiveTimeout = timeout ?? this.defaultTimeout;\n const timestamp = Date.now();\n\n const pendingWrite: PendingWrite = {\n opId,\n writeConcern,\n timestamp,\n timeout: effectiveTimeout,\n resolve,\n reject,\n achievedLevels: new Set([WriteConcern.FIRE_AND_FORGET]),\n };\n\n // Set timeout\n pendingWrite.timeoutHandle = setTimeout(() => {\n this.handleTimeout(opId);\n }, effectiveTimeout);\n\n this.pending.set(opId, pendingWrite);\n\n logger.debug(\n { opId, writeConcern, timeout: effectiveTimeout },\n 'Registered pending write'\n );\n\n // MEMORY level is achieved immediately after registration\n // (operation is validated and queued for processing)\n if (writeConcern === WriteConcern.MEMORY) {\n this.notifyLevel(opId, WriteConcern.MEMORY);\n }\n });\n }\n\n /**\n * Notify that a Write Concern level has been achieved for an operation.\n *\n * @param opId - Operation ID\n * @param level - Write Concern level that was achieved\n */\n notifyLevel(opId: string, level: WriteConcern): void {\n const pending = this.pending.get(opId);\n if (!pending) {\n // Operation not tracked (might be FIRE_AND_FORGET or already resolved)\n return;\n }\n\n pending.achievedLevels.add(level);\n\n logger.debug(\n { opId, level, target: pending.writeConcern },\n 'Write Concern level achieved'\n );\n\n // Check if target level is achieved\n if (isWriteConcernAchieved(pending.achievedLevels, pending.writeConcern)) {\n this.resolvePending(opId, level);\n }\n }\n\n /**\n * Notify multiple operations that a Write Concern level has been achieved.\n * Useful for batch operations.\n *\n * @param opIds - Array of operation IDs\n * @param level - Write Concern level that was achieved\n */\n notifyLevelBatch(opIds: string[], level: WriteConcern): void {\n for (const opId of opIds) {\n this.notifyLevel(opId, level);\n }\n }\n\n /**\n * Check if an operation is still pending.\n *\n * @param opId - Operation ID\n * @returns true if operation is pending\n */\n isPending(opId: string): boolean {\n return this.pending.has(opId);\n }\n\n /**\n * Get the target Write Concern level for a pending operation.\n *\n * @param opId - Operation ID\n * @returns Target Write Concern level or undefined if not pending\n */\n getTargetLevel(opId: string): WriteConcern | undefined {\n return this.pending.get(opId)?.writeConcern;\n }\n\n /**\n * Get the highest achieved level for a pending operation.\n *\n * @param opId - Operation ID\n * @returns Highest achieved level or undefined if not pending\n */\n getAchievedLevel(opId: string): WriteConcern | undefined {\n const pending = this.pending.get(opId);\n if (!pending) return undefined;\n return getHighestWriteConcernLevel(pending.achievedLevels);\n }\n\n /**\n * Resolve a pending write with success.\n */\n private resolvePending(opId: string, achievedLevel: WriteConcern): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n // Clear timeout\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const latencyMs = Date.now() - pending.timestamp;\n\n const result: WriteResult = {\n success: true,\n opId,\n achievedLevel,\n latencyMs,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.debug(\n { opId, achievedLevel, latencyMs },\n 'Write resolved successfully'\n );\n\n this.emit('resolved', result);\n }\n\n /**\n * Handle timeout for a pending write.\n */\n private handleTimeout(opId: string): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n const latencyMs = Date.now() - pending.timestamp;\n\n // Resolve with partial success (achieved lower level than requested)\n const result: WriteResult = {\n success: false,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error: `Timeout: achieved ${highestAchieved}, requested ${pending.writeConcern}`,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.warn(\n { opId, requested: pending.writeConcern, achieved: highestAchieved, latencyMs },\n 'Write timed out'\n );\n\n this.emit('timeout', {\n opId,\n requested: pending.writeConcern,\n achieved: highestAchieved,\n latencyMs,\n });\n }\n\n /**\n * Fail a pending write with an error.\n *\n * @param opId - Operation ID\n * @param error - Error message\n */\n failPending(opId: string, error: string): void {\n const pending = this.pending.get(opId);\n if (!pending) return;\n\n // Clear timeout\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const latencyMs = Date.now() - pending.timestamp;\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n\n const result: WriteResult = {\n success: false,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error,\n };\n\n pending.resolve(result);\n this.pending.delete(opId);\n\n logger.error({ opId, error, latencyMs }, 'Write failed');\n\n this.emit('failed', result);\n }\n\n /**\n * Get pending writes statistics.\n */\n getStats(): WriteAckStats {\n const byLevel: Record<WriteConcern, number> = {\n [WriteConcern.FIRE_AND_FORGET]: 0,\n [WriteConcern.MEMORY]: 0,\n [WriteConcern.APPLIED]: 0,\n [WriteConcern.REPLICATED]: 0,\n [WriteConcern.PERSISTED]: 0,\n };\n\n for (const pending of this.pending.values()) {\n byLevel[pending.writeConcern]++;\n }\n\n return { pending: this.pending.size, byLevel };\n }\n\n /**\n * Get all pending operation IDs.\n */\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n /**\n * Clear all pending writes (for shutdown).\n * Rejects all pending promises with an error.\n */\n clear(): void {\n const count = this.pending.size;\n\n for (const pending of this.pending.values()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.reject(new Error('WriteAckManager cleared'));\n }\n\n this.pending.clear();\n\n if (count > 0) {\n logger.info({ count }, 'WriteAckManager cleared');\n }\n }\n\n /**\n * Graceful shutdown - resolves all pending writes with their current achieved level.\n */\n shutdown(): void {\n const count = this.pending.size;\n\n for (const [opId, pending] of this.pending.entries()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n\n const highestAchieved = getHighestWriteConcernLevel(pending.achievedLevels);\n const latencyMs = Date.now() - pending.timestamp;\n\n const result: WriteResult = {\n success: highestAchieved === pending.writeConcern,\n opId,\n achievedLevel: highestAchieved,\n latencyMs,\n error: highestAchieved !== pending.writeConcern\n ? `Shutdown: achieved ${highestAchieved}, requested ${pending.writeConcern}`\n : undefined,\n };\n\n pending.resolve(result);\n }\n\n this.pending.clear();\n\n if (count > 0) {\n logger.info({ count }, 'WriteAckManager shutdown');\n }\n }\n}\n","/**\n * ReplicationPipeline - Manages async replication with configurable consistency levels\n *\n * Phase 4 Task 04: Async Replication Pipeline\n *\n * Features:\n * - Three consistency levels: STRONG, QUORUM, EVENTUAL\n * - Async replication queue for high throughput\n * - Backpressure handling with queue limits\n * - Retry logic for failed replications\n * - Integration with LagTracker for monitoring\n * - Pluggable operation applier for storage integration\n */\n\nimport { EventEmitter } from 'events';\nimport {\n ConsistencyLevel,\n ReplicationConfig,\n ReplicationTask,\n ReplicationResult,\n ReplicationHealth,\n ReplicationLag,\n DEFAULT_REPLICATION_CONFIG,\n} from '@topgunbuild/core';\nimport { ClusterManager, ClusterMessage } from './ClusterManager';\nimport { PartitionService } from './PartitionService';\nimport { LagTracker } from './LagTracker';\nimport { logger } from '../utils/logger';\n\n/**\n * Callback to apply replicated operation to local storage\n * @param operation - The operation to apply\n * @param opId - Unique operation ID\n * @param sourceNode - Node that originated the operation\n * @returns Promise<boolean> - true if applied successfully\n */\nexport type OperationApplier = (\n operation: unknown,\n opId: string,\n sourceNode: string\n) => Promise<boolean>;\n\nexport interface PendingAck {\n opId: string;\n consistency: ConsistencyLevel;\n targetNodes: string[];\n ackedNodes: Set<string>;\n resolve: () => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n startTime: number;\n}\n\nexport interface ReplicationPipelineEvents {\n 'replicationComplete': (opId: string, ackedBy: string[]) => void;\n 'replicationFailed': (opId: string, error: Error) => void;\n 'queueOverflow': (nodeId: string) => void;\n 'error': (error: Error) => void;\n}\n\nexport class ReplicationTimeoutError extends Error {\n constructor(\n public readonly opId: string,\n public readonly targetNodes: string[],\n public readonly ackedNodes: string[]\n ) {\n super(\n `Replication timeout for operation ${opId}. Expected: ${targetNodes.join(', ')}, Acked: ${ackedNodes.join(', ')}`\n );\n this.name = 'ReplicationTimeoutError';\n }\n}\n\nexport class ReplicationPipeline extends EventEmitter {\n private readonly config: ReplicationConfig;\n private readonly clusterManager: ClusterManager;\n private readonly partitionService: PartitionService;\n private readonly lagTracker: LagTracker;\n private readonly nodeId: string;\n\n // Replication queues per node (for EVENTUAL mode)\n private replicationQueue: Map<string, ReplicationTask[]> = new Map();\n // Pending acknowledgments (for STRONG/QUORUM mode)\n private pendingAcks: Map<string, PendingAck> = new Map();\n // Queue processor timer\n private queueProcessorTimer: ReturnType<typeof setInterval> | null = null;\n // Operation applier callback (injected by ServerCoordinator)\n private operationApplier: OperationApplier | null = null;\n\n constructor(\n clusterManager: ClusterManager,\n partitionService: PartitionService,\n config: Partial<ReplicationConfig> = {}\n ) {\n super();\n this.clusterManager = clusterManager;\n this.partitionService = partitionService;\n this.nodeId = clusterManager.config.nodeId;\n this.config = {\n ...DEFAULT_REPLICATION_CONFIG,\n ...config,\n };\n this.lagTracker = new LagTracker();\n\n this.setupMessageHandlers();\n this.startQueueProcessor();\n }\n\n // ============================================\n // Configuration\n // ============================================\n\n /**\n * Set the operation applier callback\n * This is called when replicated operations are received from other nodes\n */\n public setOperationApplier(applier: OperationApplier): void {\n this.operationApplier = applier;\n }\n\n // ============================================\n // Replication API\n // ============================================\n\n /**\n * Replicate operation to backup nodes\n */\n public async replicate(\n operation: unknown,\n opId: string,\n key: string,\n options: { consistency?: ConsistencyLevel; timeout?: number } = {}\n ): Promise<ReplicationResult> {\n const consistency = options.consistency ?? this.config.defaultConsistency;\n const partitionId = this.partitionService.getPartitionId(key);\n const backups = this.partitionService.getBackups(partitionId);\n\n if (backups.length === 0) {\n // No replicas, always succeeds\n return { success: true, ackedBy: [this.nodeId] };\n }\n\n switch (consistency) {\n case ConsistencyLevel.STRONG:\n return this.replicateStrong(operation, opId, backups, options.timeout);\n\n case ConsistencyLevel.QUORUM:\n return this.replicateQuorum(operation, opId, backups, options.timeout);\n\n case ConsistencyLevel.EVENTUAL:\n return this.replicateEventual(operation, opId, backups);\n }\n }\n\n /**\n * STRONG: Wait for all replicas to acknowledge\n */\n private async replicateStrong(\n operation: unknown,\n opId: string,\n backups: string[],\n timeout?: number\n ): Promise<ReplicationResult> {\n const targetNodes = backups;\n\n return new Promise((resolve, reject) => {\n const pending: PendingAck = {\n opId,\n consistency: ConsistencyLevel.STRONG,\n targetNodes,\n ackedNodes: new Set(),\n resolve: () =>\n resolve({\n success: true,\n ackedBy: [this.nodeId, ...targetNodes],\n }),\n reject: (error) => reject(error),\n timeout: setTimeout(() => {\n this.pendingAcks.delete(opId);\n const ackedList = Array.from(pending.ackedNodes);\n reject(new ReplicationTimeoutError(opId, targetNodes, ackedList));\n }, timeout ?? this.config.ackTimeoutMs),\n startTime: Date.now(),\n };\n\n this.pendingAcks.set(opId, pending);\n\n // Track pending ops\n for (const nodeId of targetNodes) {\n this.lagTracker.incrementPending(nodeId);\n }\n\n // Send to all backups\n for (const nodeId of targetNodes) {\n this.sendReplication(nodeId, operation, opId, ConsistencyLevel.STRONG);\n }\n });\n }\n\n /**\n * QUORUM: Wait for majority of replicas\n */\n private async replicateQuorum(\n operation: unknown,\n opId: string,\n backups: string[],\n timeout?: number\n ): Promise<ReplicationResult> {\n const targetNodes = backups;\n const quorumSize = Math.floor(targetNodes.length / 2) + 1;\n\n return new Promise((resolve, reject) => {\n // Create pending ack with resolve that captures snapshot of ackedNodes\n const ackedNodes = new Set<string>();\n const pending: PendingAck = {\n opId,\n consistency: ConsistencyLevel.QUORUM,\n targetNodes,\n ackedNodes,\n resolve: () => {\n // Snapshot ackedNodes at resolve time to avoid race condition\n const ackedSnapshot = Array.from(ackedNodes);\n const ackedBy = [this.nodeId, ...ackedSnapshot];\n resolve({ success: true, ackedBy });\n },\n reject: (error) => reject(error),\n timeout: setTimeout(() => {\n this.pendingAcks.delete(opId);\n const ackedList = Array.from(ackedNodes);\n reject(new ReplicationTimeoutError(opId, targetNodes, ackedList));\n }, timeout ?? this.config.ackTimeoutMs),\n startTime: Date.now(),\n };\n\n this.pendingAcks.set(opId, pending);\n\n // Track pending ops\n for (const nodeId of targetNodes) {\n this.lagTracker.incrementPending(nodeId);\n }\n\n // Send to all backups\n for (const nodeId of targetNodes) {\n this.sendReplication(nodeId, operation, opId, ConsistencyLevel.QUORUM);\n }\n });\n }\n\n /**\n * EVENTUAL: Fire-and-forget with queue\n */\n private async replicateEventual(\n operation: unknown,\n opId: string,\n backups: string[]\n ): Promise<ReplicationResult> {\n // Add to replication queue for each backup\n for (const nodeId of backups) {\n this.enqueue(nodeId, {\n opId,\n operation,\n consistency: ConsistencyLevel.EVENTUAL,\n timestamp: Date.now(),\n retryCount: 0,\n });\n }\n\n // Return immediately\n return { success: true, ackedBy: [this.nodeId] };\n }\n\n // ============================================\n // Queue Management\n // ============================================\n\n /**\n * Add task to replication queue\n */\n private enqueue(nodeId: string, task: ReplicationTask): void {\n let queue = this.replicationQueue.get(nodeId);\n if (!queue) {\n queue = [];\n this.replicationQueue.set(nodeId, queue);\n }\n\n if (queue.length >= this.config.queueSizeLimit) {\n // Queue overflow - emit event and drop oldest\n this.emit('queueOverflow', nodeId);\n logger.warn({ nodeId, queueSize: queue.length }, 'Replication queue overflow, dropping oldest');\n queue.shift();\n }\n\n queue.push(task);\n this.lagTracker.incrementPending(nodeId);\n }\n\n /**\n * Start queue processor\n */\n private startQueueProcessor(): void {\n if (this.queueProcessorTimer) return;\n\n this.queueProcessorTimer = setInterval(() => {\n for (const nodeId of this.replicationQueue.keys()) {\n this.processQueue(nodeId).catch((err) => {\n logger.error({ nodeId, error: err }, 'Error processing replication queue');\n this.emit('error', err);\n });\n }\n }, this.config.batchIntervalMs);\n }\n\n /**\n * Stop queue processor\n */\n private stopQueueProcessor(): void {\n if (this.queueProcessorTimer) {\n clearInterval(this.queueProcessorTimer);\n this.queueProcessorTimer = null;\n }\n }\n\n /**\n * Process replication queue for a node\n */\n private async processQueue(nodeId: string): Promise<void> {\n const queue = this.replicationQueue.get(nodeId);\n if (!queue || queue.length === 0) return;\n\n // Batch up to config.batchSize operations\n const batch = queue.splice(0, this.config.batchSize);\n\n try {\n // Send batch to node\n this.clusterManager.send(nodeId, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_BATCH',\n payload: {\n operations: batch.map((t) => t.operation),\n opIds: batch.map((t) => t.opId),\n },\n },\n });\n\n // Update lag tracker with oldest timestamp in batch\n const oldestTimestamp = Math.min(...batch.map((t) => t.timestamp));\n this.lagTracker.update(nodeId, Date.now() - oldestTimestamp);\n\n logger.debug({ nodeId, batchSize: batch.length }, 'Sent replication batch');\n } catch (error) {\n // Requeue failed batch with retry increment\n for (const task of batch) {\n task.retryCount++;\n if (task.retryCount <= this.config.maxRetries) {\n queue.unshift(task); // Requeue at front\n } else {\n logger.warn({ nodeId, opId: task.opId, retries: task.retryCount }, 'Replication task exceeded max retries');\n this.emit('replicationFailed', task.opId, new Error('Max retries exceeded'));\n }\n }\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n /**\n * Send replication message to a node\n */\n private sendReplication(\n nodeId: string,\n operation: unknown,\n opId: string,\n consistency: ConsistencyLevel\n ): void {\n this.clusterManager.send(nodeId, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION',\n payload: {\n opId,\n operation,\n consistency,\n },\n },\n });\n }\n\n /**\n * Setup cluster message handlers\n */\n private setupMessageHandlers(): void {\n this.clusterManager.on('message', (msg: ClusterMessage) => {\n if (msg.payload?._replication) {\n const replication = msg.payload._replication;\n\n switch (replication.type) {\n case 'REPLICATION':\n this.handleReplication(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_BATCH':\n this.handleReplicationBatch(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_ACK':\n this.handleReplicationAck(msg.senderId, replication.payload);\n break;\n case 'REPLICATION_BATCH_ACK':\n this.handleReplicationBatchAck(msg.senderId, replication.payload);\n break;\n }\n }\n });\n }\n\n /**\n * Handle incoming replication request (on backup node)\n */\n private async handleReplication(\n sourceNode: string,\n payload: { opId: string; operation: unknown; consistency: ConsistencyLevel }\n ): Promise<void> {\n const { opId, operation, consistency } = payload;\n\n logger.debug({ sourceNode, opId, consistency }, 'Received replication');\n\n // Apply operation to local storage\n let success = true;\n if (this.operationApplier) {\n try {\n success = await this.operationApplier(operation, opId, sourceNode);\n } catch (error) {\n logger.error({ sourceNode, opId, error }, 'Failed to apply replicated operation');\n success = false;\n }\n } else {\n logger.warn({ sourceNode, opId }, 'No operation applier set, operation not applied');\n }\n\n // For STRONG/QUORUM, send acknowledgment\n if (consistency === ConsistencyLevel.STRONG || consistency === ConsistencyLevel.QUORUM) {\n this.clusterManager.send(sourceNode, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_ACK',\n payload: {\n opId,\n success,\n timestamp: Date.now(),\n },\n },\n });\n }\n }\n\n /**\n * Handle incoming batch replication (on backup node)\n */\n private async handleReplicationBatch(\n sourceNode: string,\n payload: { operations: unknown[]; opIds: string[] }\n ): Promise<void> {\n const { operations, opIds } = payload;\n\n logger.debug({ sourceNode, count: operations.length }, 'Received replication batch');\n\n // Apply operations to local storage\n let allSuccess = true;\n if (this.operationApplier) {\n for (let i = 0; i < operations.length; i++) {\n try {\n const success = await this.operationApplier(operations[i], opIds[i], sourceNode);\n if (!success) {\n allSuccess = false;\n }\n } catch (error) {\n logger.error({ sourceNode, opId: opIds[i], error }, 'Failed to apply replicated operation in batch');\n allSuccess = false;\n }\n }\n } else {\n logger.warn({ sourceNode, count: operations.length }, 'No operation applier set, batch not applied');\n }\n\n // Send batch acknowledgment\n this.clusterManager.send(sourceNode, 'OP_FORWARD', {\n _replication: {\n type: 'REPLICATION_BATCH_ACK',\n payload: {\n opIds,\n success: allSuccess,\n timestamp: Date.now(),\n },\n },\n });\n }\n\n /**\n * Handle replication acknowledgment (on owner node)\n */\n private handleReplicationAck(\n sourceNode: string,\n payload: { opId: string; success: boolean; timestamp: number }\n ): void {\n const { opId, success } = payload;\n\n // Update lag tracker\n this.lagTracker.recordAck(sourceNode);\n\n const pending = this.pendingAcks.get(opId);\n if (!pending) return; // No pending ack or already resolved\n\n if (!success) {\n logger.warn({ sourceNode, opId }, 'Replication rejected by backup');\n return;\n }\n\n pending.ackedNodes.add(sourceNode);\n\n // Update lag with round-trip time\n const lag = Date.now() - pending.startTime;\n this.lagTracker.update(sourceNode, lag);\n\n // Check if we've met the consistency requirement\n const ackedCount = pending.ackedNodes.size;\n const targetCount = pending.targetNodes.length;\n\n switch (pending.consistency) {\n case ConsistencyLevel.STRONG:\n if (ackedCount === targetCount) {\n clearTimeout(pending.timeout);\n this.pendingAcks.delete(opId);\n pending.resolve();\n this.emit('replicationComplete', opId, [this.nodeId, ...pending.ackedNodes]);\n }\n break;\n\n case ConsistencyLevel.QUORUM:\n const quorumSize = Math.floor(targetCount / 2) + 1;\n if (ackedCount >= quorumSize) {\n clearTimeout(pending.timeout);\n this.pendingAcks.delete(opId);\n pending.resolve();\n this.emit('replicationComplete', opId, [this.nodeId, ...pending.ackedNodes]);\n }\n break;\n }\n }\n\n /**\n * Handle batch acknowledgment (on owner node)\n */\n private handleReplicationBatchAck(\n sourceNode: string,\n payload: { opIds: string[]; success: boolean; timestamp: number }\n ): void {\n const { success } = payload;\n\n // Update lag tracker\n this.lagTracker.recordAck(sourceNode);\n\n if (!success) {\n logger.warn({ sourceNode, count: payload.opIds.length }, 'Batch replication rejected');\n }\n }\n\n // ============================================\n // Status and Metrics\n // ============================================\n\n /**\n * Get replication lag for a specific node\n */\n public getLag(nodeId: string): ReplicationLag {\n return this.lagTracker.getLag(nodeId);\n }\n\n /**\n * Get overall replication health\n */\n public getHealth(): ReplicationHealth {\n return this.lagTracker.getHealth();\n }\n\n /**\n * Get queue size for a specific node\n */\n public getQueueSize(nodeId: string): number {\n return this.replicationQueue.get(nodeId)?.length ?? 0;\n }\n\n /**\n * Get total pending operations across all nodes\n */\n public getTotalPending(): number {\n let total = 0;\n for (const queue of this.replicationQueue.values()) {\n total += queue.length;\n }\n return total + this.pendingAcks.size;\n }\n\n /**\n * Check if a node is considered synced (low lag)\n */\n public isSynced(nodeId: string, maxLagMs: number = 1000): boolean {\n const lag = this.lagTracker.getLag(nodeId);\n return lag.current < maxLagMs;\n }\n\n /**\n * Get LagTracker for advanced monitoring\n */\n public getLagTracker(): LagTracker {\n return this.lagTracker;\n }\n\n /**\n * Export metrics in Prometheus format\n */\n public toPrometheusMetrics(): string {\n const lines: string[] = [];\n\n // Queue sizes\n lines.push('# HELP topgun_replication_queue_size Pending operations in replication queue');\n lines.push('# TYPE topgun_replication_queue_size gauge');\n for (const [nodeId, queue] of this.replicationQueue) {\n lines.push(`topgun_replication_queue_size{node=\"${nodeId}\"} ${queue.length}`);\n }\n\n // Pending acks\n lines.push('');\n lines.push('# HELP topgun_replication_pending_acks Pending synchronous acknowledgments');\n lines.push('# TYPE topgun_replication_pending_acks gauge');\n lines.push(`topgun_replication_pending_acks ${this.pendingAcks.size}`);\n\n // Lag tracker metrics\n lines.push('');\n lines.push(this.lagTracker.toPrometheusMetrics());\n\n return lines.join('\\n');\n }\n\n /**\n * Cleanup resources\n */\n public close(): void {\n this.stopQueueProcessor();\n\n // Reject all pending acks\n for (const [opId, pending] of this.pendingAcks) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('ReplicationPipeline closed'));\n }\n this.pendingAcks.clear();\n\n // Clear queues\n this.replicationQueue.clear();\n\n // Clear lag tracker\n this.lagTracker.clear();\n }\n}\n","/**\n * LagTracker - Monitors replication lag across cluster nodes\n *\n * Phase 4 Task 04: Async Replication Pipeline\n *\n * Features:\n * - Tracks replication lag per node\n * - Maintains historical lag data for percentile calculations\n * - Identifies unhealthy and laggy nodes\n * - Provides health metrics for monitoring\n */\n\nimport { ReplicationLag, ReplicationHealth } from '@topgunbuild/core';\n\nexport interface LagInfo {\n current: number;\n history: number[];\n lastUpdate: number;\n pendingOps: number;\n}\n\nexport interface LagTrackerConfig {\n /** Number of lag samples to keep in history (default: 100) */\n historySize: number;\n /** Threshold in ms for considering a node laggy (default: 5000) */\n laggyThresholdMs: number;\n /** Threshold in ms for considering a node unhealthy (default: 30000) */\n unhealthyThresholdMs: number;\n}\n\nexport const DEFAULT_LAG_TRACKER_CONFIG: LagTrackerConfig = {\n historySize: 100,\n laggyThresholdMs: 5000,\n unhealthyThresholdMs: 30000,\n};\n\nexport class LagTracker {\n private readonly config: LagTrackerConfig;\n private lagByNode: Map<string, LagInfo> = new Map();\n\n constructor(config: Partial<LagTrackerConfig> = {}) {\n this.config = {\n ...DEFAULT_LAG_TRACKER_CONFIG,\n ...config,\n };\n }\n\n /**\n * Update lag measurement for a node\n */\n public update(nodeId: string, lagMs: number): void {\n let info = this.lagByNode.get(nodeId);\n if (!info) {\n info = {\n current: 0,\n history: [],\n lastUpdate: Date.now(),\n pendingOps: 0,\n };\n this.lagByNode.set(nodeId, info);\n }\n\n info.current = lagMs;\n info.history.push(lagMs);\n\n // Trim history to configured size\n if (info.history.length > this.config.historySize) {\n info.history.shift();\n }\n\n info.lastUpdate = Date.now();\n }\n\n /**\n * Record acknowledgment from a node (lag effectively becomes 0)\n */\n public recordAck(nodeId: string): void {\n const info = this.lagByNode.get(nodeId);\n if (info) {\n info.current = 0;\n info.lastUpdate = Date.now();\n if (info.pendingOps > 0) {\n info.pendingOps--;\n }\n }\n }\n\n /**\n * Increment pending operations counter for a node\n */\n public incrementPending(nodeId: string): void {\n let info = this.lagByNode.get(nodeId);\n if (!info) {\n info = {\n current: 0,\n history: [],\n lastUpdate: Date.now(),\n pendingOps: 0,\n };\n this.lagByNode.set(nodeId, info);\n }\n info.pendingOps++;\n }\n\n /**\n * Get lag statistics for a specific node\n */\n public getLag(nodeId: string): ReplicationLag {\n const info = this.lagByNode.get(nodeId);\n if (!info || info.history.length === 0) {\n return { current: 0, avg: 0, max: 0, percentile99: 0 };\n }\n\n const sorted = [...info.history].sort((a, b) => a - b);\n const avg = sorted.reduce((a, b) => a + b, 0) / sorted.length;\n const max = sorted[sorted.length - 1] || 0;\n\n // Calculate 99th percentile\n const p99Index = Math.floor(sorted.length * 0.99);\n const percentile99 = sorted[p99Index] || max;\n\n return {\n current: info.current,\n avg: Math.round(avg * 100) / 100, // Round to 2 decimal places\n max,\n percentile99,\n };\n }\n\n /**\n * Get pending operations count for a node\n */\n public getPendingOps(nodeId: string): number {\n const info = this.lagByNode.get(nodeId);\n return info?.pendingOps ?? 0;\n }\n\n /**\n * Get overall replication health status\n */\n public getHealth(): ReplicationHealth {\n const unhealthyNodes: string[] = [];\n const laggyNodes: string[] = [];\n let totalLag = 0;\n let nodeCount = 0;\n\n const now = Date.now();\n\n for (const [nodeId, info] of this.lagByNode) {\n const timeSinceUpdate = now - info.lastUpdate;\n\n // Check if node is unhealthy (no updates for too long)\n if (timeSinceUpdate > this.config.unhealthyThresholdMs) {\n unhealthyNodes.push(nodeId);\n }\n // Check if node is laggy (current lag exceeds threshold)\n else if (info.current > this.config.laggyThresholdMs) {\n laggyNodes.push(nodeId);\n }\n\n totalLag += info.current;\n nodeCount++;\n }\n\n const avgLagMs = nodeCount > 0 ? totalLag / nodeCount : 0;\n\n return {\n healthy: unhealthyNodes.length === 0,\n unhealthyNodes,\n laggyNodes,\n avgLagMs: Math.round(avgLagMs * 100) / 100,\n };\n }\n\n /**\n * Get average lag across all tracked nodes\n */\n public getAverageLag(): number {\n let total = 0;\n let count = 0;\n\n for (const info of this.lagByNode.values()) {\n total += info.current;\n count++;\n }\n\n return count > 0 ? total / count : 0;\n }\n\n /**\n * Check if a specific node is considered healthy\n */\n public isNodeHealthy(nodeId: string): boolean {\n const info = this.lagByNode.get(nodeId);\n if (!info) return true; // Unknown nodes are considered healthy\n\n const timeSinceUpdate = Date.now() - info.lastUpdate;\n return timeSinceUpdate < this.config.unhealthyThresholdMs;\n }\n\n /**\n * Check if a specific node is considered laggy\n */\n public isNodeLaggy(nodeId: string): boolean {\n const info = this.lagByNode.get(nodeId);\n if (!info) return false;\n\n return info.current > this.config.laggyThresholdMs;\n }\n\n /**\n * Remove a node from tracking\n */\n public removeNode(nodeId: string): void {\n this.lagByNode.delete(nodeId);\n }\n\n /**\n * Get all tracked node IDs\n */\n public getTrackedNodes(): string[] {\n return Array.from(this.lagByNode.keys());\n }\n\n /**\n * Get raw lag info for a node (for advanced monitoring)\n */\n public getRawLagInfo(nodeId: string): LagInfo | undefined {\n return this.lagByNode.get(nodeId);\n }\n\n /**\n * Clear all tracking data\n */\n public clear(): void {\n this.lagByNode.clear();\n }\n\n /**\n * Export metrics in Prometheus format\n */\n public toPrometheusMetrics(): string {\n const lines: string[] = [\n '# HELP topgun_replication_lag_ms Current replication lag in milliseconds',\n '# TYPE topgun_replication_lag_ms gauge',\n ];\n\n for (const [nodeId, info] of this.lagByNode) {\n lines.push(`topgun_replication_lag_ms{node=\"${nodeId}\"} ${info.current}`);\n }\n\n lines.push('');\n lines.push('# HELP topgun_replication_pending_ops Pending replication operations');\n lines.push('# TYPE topgun_replication_pending_ops gauge');\n\n for (const [nodeId, info] of this.lagByNode) {\n lines.push(`topgun_replication_pending_ops{node=\"${nodeId}\"} ${info.pendingOps}`);\n }\n\n const health = this.getHealth();\n lines.push('');\n lines.push('# HELP topgun_replication_healthy Cluster replication health (1=healthy, 0=unhealthy)');\n lines.push('# TYPE topgun_replication_healthy gauge');\n lines.push(`topgun_replication_healthy ${health.healthy ? 1 : 0}`);\n\n lines.push('');\n lines.push('# HELP topgun_replication_avg_lag_ms Average replication lag across all nodes');\n lines.push('# TYPE topgun_replication_avg_lag_ms gauge');\n lines.push(`topgun_replication_avg_lag_ms ${health.avgLagMs}`);\n\n return lines.join('\\n');\n }\n}\n","import { PNCounterImpl } from '@topgunbuild/core';\nimport type { PNCounterState, PNCounterStateObject } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\n/**\n * Server-side handler for PN Counter CRDT synchronization.\n *\n * Responsibilities:\n * - Store counter state in memory (with optional persistence)\n * - Merge incoming client states using CRDT semantics\n * - Broadcast updates to subscribed clients\n * - Handle initial state requests\n */\nexport class CounterHandler {\n private counters: Map<string, PNCounterImpl> = new Map();\n private subscriptions: Map<string, Set<string>> = new Map(); // counterName -> Set<clientId>\n\n constructor(private readonly nodeId: string = 'server') {}\n\n /**\n * Get or create a counter by name.\n */\n private getOrCreateCounter(name: string): PNCounterImpl {\n let counter = this.counters.get(name);\n if (!counter) {\n counter = new PNCounterImpl({ nodeId: this.nodeId });\n this.counters.set(name, counter);\n logger.debug({ name }, 'Created new counter');\n }\n return counter;\n }\n\n /**\n * Handle COUNTER_REQUEST - client wants initial state.\n * @returns Response message to send back to client\n */\n handleCounterRequest(\n clientId: string,\n name: string\n ): { type: string; payload: { name: string; state: PNCounterStateObject } } {\n const counter = this.getOrCreateCounter(name);\n\n // Subscribe client to this counter\n this.subscribe(clientId, name);\n\n const state = counter.getState();\n logger.debug({ clientId, name, value: counter.get() }, 'Counter request handled');\n\n return {\n type: 'COUNTER_RESPONSE',\n payload: {\n name,\n state: this.stateToObject(state),\n },\n };\n }\n\n /**\n * Handle COUNTER_SYNC - client sends their state to merge.\n * @returns Merged state and list of clients to broadcast to\n */\n handleCounterSync(\n clientId: string,\n name: string,\n stateObj: PNCounterStateObject\n ): {\n response: { type: string; payload: { name: string; state: PNCounterStateObject } };\n broadcastTo: string[];\n broadcastMessage: { type: string; payload: { name: string; state: PNCounterStateObject } };\n } {\n const counter = this.getOrCreateCounter(name);\n\n // Convert object to Map-based state\n const incomingState = this.objectToState(stateObj);\n\n // Merge client state into server counter\n counter.merge(incomingState);\n\n const mergedState = counter.getState();\n const mergedStateObj = this.stateToObject(mergedState);\n\n logger.debug(\n { clientId, name, value: counter.get() },\n 'Counter sync handled'\n );\n\n // Subscribe client to this counter (in case they weren't already)\n this.subscribe(clientId, name);\n\n // Get all other subscribed clients for broadcast\n const subscribers = this.subscriptions.get(name) || new Set();\n const broadcastTo = Array.from(subscribers).filter((id) => id !== clientId);\n\n return {\n // Response to the sending client\n response: {\n type: 'COUNTER_UPDATE',\n payload: {\n name,\n state: mergedStateObj,\n },\n },\n // Broadcast to other clients\n broadcastTo,\n broadcastMessage: {\n type: 'COUNTER_UPDATE',\n payload: {\n name,\n state: mergedStateObj,\n },\n },\n };\n }\n\n /**\n * Subscribe a client to counter updates.\n */\n subscribe(clientId: string, counterName: string): void {\n if (!this.subscriptions.has(counterName)) {\n this.subscriptions.set(counterName, new Set());\n }\n this.subscriptions.get(counterName)!.add(clientId);\n logger.debug({ clientId, counterName }, 'Client subscribed to counter');\n }\n\n /**\n * Unsubscribe a client from counter updates.\n */\n unsubscribe(clientId: string, counterName: string): void {\n const subs = this.subscriptions.get(counterName);\n if (subs) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscriptions.delete(counterName);\n }\n }\n }\n\n /**\n * Unsubscribe a client from all counters (e.g., on disconnect).\n */\n unsubscribeAll(clientId: string): void {\n for (const [counterName, subs] of this.subscriptions) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscriptions.delete(counterName);\n }\n }\n logger.debug({ clientId }, 'Client unsubscribed from all counters');\n }\n\n /**\n * Get current counter value (for monitoring/debugging).\n */\n getCounterValue(name: string): number {\n const counter = this.counters.get(name);\n return counter ? counter.get() : 0;\n }\n\n /**\n * Get all counter names.\n */\n getCounterNames(): string[] {\n return Array.from(this.counters.keys());\n }\n\n /**\n * Get number of subscribers for a counter.\n */\n getSubscriberCount(name: string): number {\n return this.subscriptions.get(name)?.size || 0;\n }\n\n /**\n * Convert Map-based state to plain object for serialization.\n */\n private stateToObject(state: PNCounterState): PNCounterStateObject {\n return {\n p: Object.fromEntries(state.positive),\n n: Object.fromEntries(state.negative),\n };\n }\n\n /**\n * Convert plain object to Map-based state.\n */\n private objectToState(obj: PNCounterStateObject): PNCounterState {\n return {\n positive: new Map(Object.entries(obj.p || {})),\n negative: new Map(Object.entries(obj.n || {})),\n };\n }\n}\n","import {\n HLC,\n LWWMap,\n EntryProcessorDef,\n EntryProcessorDefSchema,\n EntryProcessorResult,\n Timestamp,\n} from '@topgunbuild/core';\nimport { ProcessorSandbox, ProcessorSandboxConfig } from '../ProcessorSandbox';\nimport { logger } from '../utils/logger';\n\n/**\n * Configuration for the EntryProcessorHandler.\n */\nexport interface EntryProcessorHandlerConfig {\n /** HLC instance for timestamp generation */\n hlc: HLC;\n\n /** Optional sandbox configuration override */\n sandboxConfig?: Partial<ProcessorSandboxConfig>;\n}\n\n/**\n * Server-side handler for Entry Processor execution.\n *\n * Responsibilities:\n * - Validate incoming processor definitions\n * - Execute processors in sandboxed environment\n * - Update map state atomically\n * - Return results with new values for client cache sync\n */\nexport class EntryProcessorHandler {\n private sandbox: ProcessorSandbox;\n private hlc: HLC;\n\n constructor(config: EntryProcessorHandlerConfig) {\n this.hlc = config.hlc;\n this.sandbox = new ProcessorSandbox(config.sandboxConfig);\n }\n\n /**\n * Execute a processor on a single key atomically.\n *\n * @param map The LWWMap to operate on\n * @param key The key to process\n * @param processorDef The processor definition (will be validated)\n * @returns Result with success status, processor result, and new value\n */\n async executeOnKey<V, R>(\n map: LWWMap<string, V>,\n key: string,\n processorDef: unknown,\n ): Promise<{ result: EntryProcessorResult<R>; timestamp?: Timestamp }> {\n // Validate processor definition\n const parseResult = EntryProcessorDefSchema.safeParse(processorDef);\n if (!parseResult.success) {\n logger.warn(\n { key, error: parseResult.error.message },\n 'Invalid processor definition',\n );\n return {\n result: {\n success: false,\n error: `Invalid processor: ${parseResult.error.message}`,\n },\n };\n }\n\n const processor = parseResult.data as EntryProcessorDef<V, R>;\n\n // Get current value\n const currentValue = map.get(key);\n\n logger.debug(\n { key, processor: processor.name, hasValue: currentValue !== undefined },\n 'Executing entry processor',\n );\n\n // Execute in sandbox\n const sandboxResult = await this.sandbox.execute(\n processor,\n currentValue,\n key,\n );\n\n if (!sandboxResult.success) {\n logger.warn(\n { key, processor: processor.name, error: sandboxResult.error },\n 'Processor execution failed',\n );\n return { result: sandboxResult };\n }\n\n // Apply the change if value changed\n let timestamp: Timestamp | undefined;\n\n if (sandboxResult.newValue !== undefined) {\n // Set new value - map.set() generates timestamp internally\n const record = map.set(key, sandboxResult.newValue as V);\n timestamp = record.timestamp;\n\n logger.debug(\n { key, processor: processor.name, timestamp },\n 'Processor updated value',\n );\n } else if (currentValue !== undefined) {\n // undefined newValue means delete\n const tombstone = map.remove(key);\n timestamp = tombstone.timestamp;\n\n logger.debug(\n { key, processor: processor.name, timestamp },\n 'Processor deleted value',\n );\n }\n\n return {\n result: sandboxResult,\n timestamp,\n };\n }\n\n /**\n * Execute a processor on multiple keys.\n *\n * Each key is processed sequentially to ensure atomicity per-key.\n * For parallel execution across keys, use multiple calls.\n *\n * @param map The LWWMap to operate on\n * @param keys The keys to process\n * @param processorDef The processor definition\n * @returns Map of key -> result\n */\n async executeOnKeys<V, R>(\n map: LWWMap<string, V>,\n keys: string[],\n processorDef: unknown,\n ): Promise<{\n results: Map<string, EntryProcessorResult<R>>;\n timestamps: Map<string, Timestamp>;\n }> {\n const results = new Map<string, EntryProcessorResult<R>>();\n const timestamps = new Map<string, Timestamp>();\n\n // Validate once before processing\n const parseResult = EntryProcessorDefSchema.safeParse(processorDef);\n if (!parseResult.success) {\n const errorResult: EntryProcessorResult<R> = {\n success: false,\n error: `Invalid processor: ${parseResult.error.message}`,\n };\n for (const key of keys) {\n results.set(key, errorResult);\n }\n return { results, timestamps };\n }\n\n // Execute for each key\n for (const key of keys) {\n const { result, timestamp } = await this.executeOnKey<V, R>(\n map,\n key,\n processorDef,\n );\n results.set(key, result);\n if (timestamp) {\n timestamps.set(key, timestamp);\n }\n }\n\n return { results, timestamps };\n }\n\n /**\n * Execute a processor on all entries matching a predicate.\n *\n * WARNING: This can be expensive for large maps.\n *\n * @param map The LWWMap to operate on\n * @param processorDef The processor definition\n * @param predicateCode Optional predicate code to filter entries\n * @returns Map of key -> result for processed entries\n */\n async executeOnEntries<V, R>(\n map: LWWMap<string, V>,\n processorDef: unknown,\n predicateCode?: string,\n ): Promise<{\n results: Map<string, EntryProcessorResult<R>>;\n timestamps: Map<string, Timestamp>;\n }> {\n const results = new Map<string, EntryProcessorResult<R>>();\n const timestamps = new Map<string, Timestamp>();\n\n // Validate processor\n const parseResult = EntryProcessorDefSchema.safeParse(processorDef);\n if (!parseResult.success) {\n return { results, timestamps };\n }\n\n const entries = map.entries();\n\n for (const [key, value] of entries) {\n // Apply predicate if provided\n if (predicateCode) {\n const predicateResult = await this.sandbox.execute(\n {\n name: '_predicate',\n code: `return { value, result: (function() { ${predicateCode} })() };`,\n },\n value,\n key,\n );\n\n if (!predicateResult.success || !predicateResult.result) {\n continue; // Skip this entry\n }\n }\n\n const { result, timestamp } = await this.executeOnKey<V, R>(\n map,\n key,\n processorDef,\n );\n results.set(key, result);\n if (timestamp) {\n timestamps.set(key, timestamp);\n }\n }\n\n return { results, timestamps };\n }\n\n /**\n * Check if sandbox is in secure mode (using isolated-vm).\n */\n isSecureMode(): boolean {\n return this.sandbox.isSecureMode();\n }\n\n /**\n * Get sandbox cache statistics.\n */\n getCacheStats(): { isolates: number; scripts: number; fallbackScripts: number } {\n return this.sandbox.getCacheStats();\n }\n\n /**\n * Clear sandbox cache.\n */\n clearCache(processorName?: string): void {\n this.sandbox.clearCache(processorName);\n }\n\n /**\n * Dispose of the handler and its sandbox.\n */\n dispose(): void {\n this.sandbox.dispose();\n logger.debug('EntryProcessorHandler disposed');\n }\n}\n","import {\n EntryProcessorDef,\n EntryProcessorResult,\n validateProcessorCode,\n} from '@topgunbuild/core';\nimport { logger } from './utils/logger';\n\n// Types for isolated-vm (optional dependency)\ninterface IsolatedVmIsolate {\n isDisposed: boolean;\n createContext(): Promise<IsolatedVmContext>;\n compileScript(code: string): Promise<IsolatedVmScript>;\n dispose(): void;\n}\n\ninterface IsolatedVmContext {\n global: IsolatedVmReference;\n eval(code: string): Promise<void>;\n}\n\ninterface IsolatedVmScript {\n run(context: IsolatedVmContext, options: { timeout: number }): Promise<unknown>;\n}\n\ninterface IsolatedVmReference {\n set(key: string, value: unknown): Promise<void>;\n derefInto(): unknown;\n}\n\ninterface IsolatedVmModule {\n Isolate: new (options: { memoryLimit: number }) => IsolatedVmIsolate;\n}\n\n// Try to import isolated-vm, fall back to unsafe VM for development\nlet ivm: IsolatedVmModule | null = null;\ntry {\n ivm = require('isolated-vm');\n} catch {\n const isProduction = process.env.NODE_ENV === 'production';\n if (isProduction) {\n logger.error(\n 'SECURITY WARNING: isolated-vm not available in production! ' +\n 'Entry processors will run in less secure fallback mode. ' +\n 'Install isolated-vm for production environments: pnpm add isolated-vm'\n );\n } else {\n logger.warn('isolated-vm not available, falling back to less secure VM');\n }\n}\n\n/**\n * Configuration for the processor sandbox.\n */\nexport interface ProcessorSandboxConfig {\n /** Memory limit in MB per isolate */\n memoryLimitMb: number;\n\n /** Execution timeout in milliseconds */\n timeoutMs: number;\n\n /** Maximum number of cached isolates */\n maxCachedIsolates: number;\n\n /** Enable strict code validation */\n strictValidation: boolean;\n}\n\n/**\n * Default sandbox configuration.\n */\nexport const DEFAULT_SANDBOX_CONFIG: ProcessorSandboxConfig = {\n memoryLimitMb: 8,\n timeoutMs: 100,\n maxCachedIsolates: 100,\n strictValidation: true,\n};\n\n/**\n * Sandbox for executing entry processor code securely.\n *\n * Uses isolated-vm for production environments with:\n * - Memory limits to prevent memory bombs\n * - CPU limits via timeout to prevent infinite loops\n * - No I/O access (no require, fs, net, etc.)\n * - Minimal exposed globals (only value, key, args)\n *\n * Falls back to Node.js vm module for development/testing\n * when isolated-vm is not available.\n */\nexport class ProcessorSandbox {\n private config: ProcessorSandboxConfig;\n private isolateCache: Map<string, IsolatedVmIsolate> = new Map();\n private scriptCache: Map<string, IsolatedVmScript> = new Map();\n private fallbackScriptCache: Map<string, (value: unknown, key: string, args: unknown) => unknown> = new Map();\n private disposed = false;\n\n constructor(config: Partial<ProcessorSandboxConfig> = {}) {\n this.config = { ...DEFAULT_SANDBOX_CONFIG, ...config };\n }\n\n /**\n * Execute an entry processor in the sandbox.\n *\n * @param processor The processor definition (name, code, args)\n * @param value The current value for the key (or undefined)\n * @param key The key being processed\n * @returns Result containing success status, result, and new value\n */\n async execute<V, R>(\n processor: EntryProcessorDef<V, R>,\n value: V | undefined,\n key: string,\n ): Promise<EntryProcessorResult<R>> {\n if (this.disposed) {\n return {\n success: false,\n error: 'Sandbox has been disposed',\n };\n }\n\n // Validate code if strict validation is enabled\n if (this.config.strictValidation) {\n const validation = validateProcessorCode(processor.code);\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error,\n };\n }\n }\n\n // Use isolated-vm if available, otherwise fall back\n if (ivm) {\n return this.executeInIsolate(processor, value, key);\n } else {\n return this.executeInFallback(processor, value, key);\n }\n }\n\n /**\n * Execute processor in isolated-vm (secure production mode).\n */\n private async executeInIsolate<V, R>(\n processor: EntryProcessorDef<V, R>,\n value: V | undefined,\n key: string,\n ): Promise<EntryProcessorResult<R>> {\n if (!ivm) {\n return { success: false, error: 'isolated-vm not available' };\n }\n\n const isolate = this.getOrCreateIsolate(processor.name);\n\n try {\n const context = await isolate.createContext();\n const jail = context.global;\n\n // Set up minimal environment\n await jail.set('global', jail.derefInto());\n\n // Set input values as JSON strings to avoid reference issues\n await context.eval(`\n var value = ${JSON.stringify(value)};\n var key = ${JSON.stringify(key)};\n var args = ${JSON.stringify(processor.args)};\n `);\n\n // Wrap user code in a function\n const wrappedCode = `\n (function() {\n ${processor.code}\n })()\n `;\n\n // Get or compile script\n const script = await this.getOrCompileScript(\n processor.name,\n wrappedCode,\n isolate,\n );\n\n // Execute with timeout\n const result = await script.run(context, {\n timeout: this.config.timeoutMs,\n });\n\n // Validate result format\n const parsed = result as { value: V | undefined; result?: R };\n\n if (typeof parsed !== 'object' || parsed === null) {\n return {\n success: false,\n error: 'Processor must return { value, result? } object',\n };\n }\n\n return {\n success: true,\n result: parsed.result,\n newValue: parsed.value,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n // Detect timeout errors\n if (message.includes('Script execution timed out')) {\n return {\n success: false,\n error: 'Processor execution timed out',\n };\n }\n\n return {\n success: false,\n error: message,\n };\n }\n }\n\n /**\n * Execute processor in fallback VM (less secure, for development).\n */\n private async executeInFallback<V, R>(\n processor: EntryProcessorDef<V, R>,\n value: V | undefined,\n key: string,\n ): Promise<EntryProcessorResult<R>> {\n try {\n // Skip caching for resolvers - they embed context in the code string\n const isResolver = processor.name.startsWith('resolver:');\n let fn = isResolver ? undefined : this.fallbackScriptCache.get(processor.name);\n\n if (!fn) {\n // Create function from code\n const wrappedCode = `\n return (function(value, key, args) {\n ${processor.code}\n })\n `;\n fn = new Function(wrappedCode)() as (value: unknown, key: string, args: unknown) => unknown;\n if (!isResolver) {\n this.fallbackScriptCache.set(processor.name, fn);\n }\n }\n\n // Execute with timeout using Promise.race\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('Processor execution timed out')), this.config.timeoutMs);\n });\n\n const executionPromise = Promise.resolve().then(() => fn(value, key, processor.args));\n\n const result = await Promise.race([executionPromise, timeoutPromise]) as { value: V | undefined; result?: R };\n\n // Validate result format\n if (typeof result !== 'object' || result === null) {\n return {\n success: false,\n error: 'Processor must return { value, result? } object',\n };\n }\n\n return {\n success: true,\n result: result.result,\n newValue: result.value,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: message,\n };\n }\n }\n\n /**\n * Get or create an isolate for a processor.\n */\n private getOrCreateIsolate(name: string): IsolatedVmIsolate {\n if (!ivm) {\n throw new Error('isolated-vm not available');\n }\n\n let isolate = this.isolateCache.get(name);\n\n if (!isolate || isolate.isDisposed) {\n // Evict oldest if at capacity\n if (this.isolateCache.size >= this.config.maxCachedIsolates) {\n const oldest = this.isolateCache.keys().next().value;\n if (oldest) {\n const oldIsolate = this.isolateCache.get(oldest);\n if (oldIsolate && !oldIsolate.isDisposed) {\n oldIsolate.dispose();\n }\n this.isolateCache.delete(oldest);\n this.scriptCache.delete(oldest);\n }\n }\n\n isolate = new ivm.Isolate({\n memoryLimit: this.config.memoryLimitMb,\n });\n this.isolateCache.set(name, isolate);\n }\n\n return isolate;\n }\n\n /**\n * Get or compile a script for a processor.\n */\n private async getOrCompileScript(\n name: string,\n code: string,\n isolate: IsolatedVmIsolate,\n ): Promise<IsolatedVmScript> {\n let script = this.scriptCache.get(name);\n\n if (!script) {\n script = await isolate.compileScript(code);\n this.scriptCache.set(name, script);\n }\n\n return script;\n }\n\n /**\n * Clear script cache for a specific processor (e.g., when code changes).\n */\n clearCache(processorName?: string): void {\n if (processorName) {\n const isolate = this.isolateCache.get(processorName);\n if (isolate && !isolate.isDisposed) {\n isolate.dispose();\n }\n this.isolateCache.delete(processorName);\n this.scriptCache.delete(processorName);\n this.fallbackScriptCache.delete(processorName);\n } else {\n // Clear all caches\n for (const isolate of this.isolateCache.values()) {\n if (!isolate.isDisposed) {\n isolate.dispose();\n }\n }\n this.isolateCache.clear();\n this.scriptCache.clear();\n this.fallbackScriptCache.clear();\n }\n }\n\n /**\n * Check if using secure isolated-vm mode.\n */\n isSecureMode(): boolean {\n return ivm !== null;\n }\n\n /**\n * Get current cache sizes.\n */\n getCacheStats(): { isolates: number; scripts: number; fallbackScripts: number } {\n return {\n isolates: this.isolateCache.size,\n scripts: this.scriptCache.size,\n fallbackScripts: this.fallbackScriptCache.size,\n };\n }\n\n /**\n * Dispose of all isolates and clear caches.\n */\n dispose(): void {\n if (this.disposed) return;\n\n this.disposed = true;\n this.clearCache();\n logger.debug('ProcessorSandbox disposed');\n }\n}\n","import {\n ConflictResolverDef,\n MergeContext,\n MergeResult,\n BuiltInResolvers,\n ConflictResolverDefSchema,\n validateResolverCode,\n MergeRejection,\n Timestamp,\n} from '@topgunbuild/core';\nimport { ProcessorSandbox } from './ProcessorSandbox';\nimport { logger } from './utils/logger';\n\n/**\n * Entry for storing registered resolvers.\n */\ninterface ResolverEntry<V = unknown> {\n resolver: ConflictResolverDef<V>;\n compiledFn?: (ctx: MergeContext<V>) => Promise<MergeResult<V>>;\n registeredBy?: string; // Client ID that registered this resolver\n}\n\n/**\n * Configuration for ConflictResolverService.\n */\nexport interface ConflictResolverServiceConfig {\n /** Maximum resolvers per map */\n maxResolversPerMap: number;\n\n /** Enable sandboxed code execution (requires isolated-vm) */\n enableSandboxedResolvers: boolean;\n\n /** Default timeout for resolver execution in milliseconds */\n resolverTimeoutMs: number;\n}\n\n/**\n * Default service configuration.\n */\nexport const DEFAULT_CONFLICT_RESOLVER_CONFIG: ConflictResolverServiceConfig = {\n maxResolversPerMap: 100,\n enableSandboxedResolvers: true,\n resolverTimeoutMs: 100,\n};\n\n/**\n * Service for managing and executing conflict resolvers.\n *\n * Resolvers are executed in priority order (highest first).\n * The first resolver that returns a non-'local' action wins.\n * If all resolvers return 'local', LWW is used as fallback.\n *\n * ## Design Decisions\n *\n * ### In-Memory Storage\n * Resolvers are stored in memory only (not persisted to database).\n * This is intentional - resolvers represent application logic that should\n * be registered by clients on connection. Benefits:\n * - Simpler architecture without resolver schema migrations\n * - Clients control their own conflict resolution logic\n * - Natural cleanup when client disconnects\n *\n * ### Permission Model\n * Resolver registration requires PUT permission on the target map.\n * This aligns with the principle that if you can write to a map,\n * you can define how your writes are resolved. For stricter control,\n * implement custom permission checks in ServerCoordinator.\n *\n * ### Deletion Handling\n * Deletions (tombstones with null value) are passed through resolvers\n * with `remoteValue: null`. This allows resolvers like IMMUTABLE or\n * OWNER_ONLY to protect against unauthorized deletions.\n */\nexport class ConflictResolverService {\n private resolvers: Map<string, ResolverEntry[]> = new Map();\n private sandbox: ProcessorSandbox;\n private config: ConflictResolverServiceConfig;\n private onRejectionCallback?: (rejection: MergeRejection) => void;\n private disposed = false;\n\n constructor(\n sandbox: ProcessorSandbox,\n config: Partial<ConflictResolverServiceConfig> = {},\n ) {\n this.sandbox = sandbox;\n this.config = { ...DEFAULT_CONFLICT_RESOLVER_CONFIG, ...config };\n }\n\n /**\n * Set callback for merge rejections.\n */\n onRejection(callback: (rejection: MergeRejection) => void): void {\n this.onRejectionCallback = callback;\n }\n\n /**\n * Register a resolver for a map.\n *\n * @param mapName The map this resolver applies to\n * @param resolver The resolver definition\n * @param registeredBy Optional client ID that registered this resolver\n */\n register<V>(\n mapName: string,\n resolver: ConflictResolverDef<V>,\n registeredBy?: string,\n ): void {\n if (this.disposed) {\n throw new Error('ConflictResolverService has been disposed');\n }\n\n // Validate resolver if it has code\n if (resolver.code) {\n // Validate against schema\n const parsed = ConflictResolverDefSchema.safeParse({\n name: resolver.name,\n code: resolver.code,\n priority: resolver.priority,\n keyPattern: resolver.keyPattern,\n });\n\n if (!parsed.success) {\n throw new Error(`Invalid resolver definition: ${parsed.error.message}`);\n }\n\n // Validate code for forbidden patterns\n const validation = validateResolverCode(resolver.code);\n if (!validation.valid) {\n throw new Error(`Invalid resolver code: ${validation.error}`);\n }\n }\n\n const entries = this.resolvers.get(mapName) ?? [];\n\n // Check max resolvers limit\n if (entries.length >= this.config.maxResolversPerMap) {\n throw new Error(\n `Maximum resolvers per map (${this.config.maxResolversPerMap}) exceeded`,\n );\n }\n\n // Remove existing with same name\n const filtered = entries.filter((e) => e.resolver.name !== resolver.name);\n\n // Create entry\n const entry: ResolverEntry<V> = {\n resolver,\n registeredBy,\n };\n\n // Pre-compile sandboxed code if needed\n if (resolver.code && !resolver.fn && this.config.enableSandboxedResolvers) {\n entry.compiledFn = this.compileSandboxed<V>(resolver.name, resolver.code);\n }\n\n filtered.push(entry as ResolverEntry);\n\n // Sort by priority (highest first)\n filtered.sort(\n (a, b) => (b.resolver.priority ?? 50) - (a.resolver.priority ?? 50),\n );\n\n this.resolvers.set(mapName, filtered);\n\n logger.debug(\n `Registered resolver '${resolver.name}' for map '${mapName}' with priority ${resolver.priority ?? 50}`,\n );\n }\n\n /**\n * Unregister a resolver.\n *\n * @param mapName The map name\n * @param resolverName The resolver name to unregister\n * @param clientId Optional - only unregister if registered by this client\n */\n unregister(\n mapName: string,\n resolverName: string,\n clientId?: string,\n ): boolean {\n const entries = this.resolvers.get(mapName);\n if (!entries) return false;\n\n const entryIndex = entries.findIndex(\n (e) =>\n e.resolver.name === resolverName &&\n (!clientId || e.registeredBy === clientId),\n );\n\n if (entryIndex === -1) return false;\n\n entries.splice(entryIndex, 1);\n\n if (entries.length === 0) {\n this.resolvers.delete(mapName);\n }\n\n logger.debug(`Unregistered resolver '${resolverName}' from map '${mapName}'`);\n return true;\n }\n\n /**\n * Resolve a merge conflict using registered resolvers.\n *\n * @param context The merge context\n * @returns The merge result\n */\n async resolve<V>(context: MergeContext<V>): Promise<MergeResult<V>> {\n if (this.disposed) {\n return { action: 'accept', value: context.remoteValue };\n }\n\n const entries = this.resolvers.get(context.mapName) ?? [];\n\n // Always add LWW as fallback (lowest priority)\n const allEntries: ResolverEntry[] = [\n ...entries,\n { resolver: BuiltInResolvers.LWW() },\n ];\n\n for (const entry of allEntries) {\n const { resolver } = entry;\n\n // Check key pattern if specified\n if (resolver.keyPattern && !this.matchKeyPattern(context.key, resolver.keyPattern)) {\n continue;\n }\n\n try {\n let result: MergeResult<V>;\n\n if (resolver.fn) {\n // Native function execution - cast context to unknown since resolvers are stored with unknown type\n const fn = resolver.fn as (ctx: MergeContext<V>) => MergeResult<V> | Promise<MergeResult<V>>;\n const maybePromise = fn(context);\n result = maybePromise instanceof Promise ? await maybePromise : maybePromise;\n } else if (entry.compiledFn) {\n // Sandboxed code execution\n const compiledFn = entry.compiledFn as (ctx: MergeContext<V>) => Promise<MergeResult<V>>;\n result = await compiledFn(context);\n } else {\n // Skip resolvers without executable code\n continue;\n }\n\n // Only 'local' allows falling through to next resolver\n if (result.action !== 'local') {\n // Log rejections\n if (result.action === 'reject') {\n logger.debug(\n `Resolver '${resolver.name}' rejected merge for key '${context.key}' in map '${context.mapName}': ${result.reason}`,\n );\n\n // Emit rejection event\n if (this.onRejectionCallback) {\n this.onRejectionCallback({\n mapName: context.mapName,\n key: context.key,\n attemptedValue: context.remoteValue,\n reason: result.reason,\n timestamp: context.remoteTimestamp,\n nodeId: context.remoteNodeId,\n });\n }\n }\n\n return result;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Resolver '${resolver.name}' threw error: ${message}`);\n // Continue to next resolver on error\n }\n }\n\n // Fallback: accept remote value (LWW should have handled this, but just in case)\n return { action: 'accept', value: context.remoteValue };\n }\n\n /**\n * List registered resolvers.\n *\n * @param mapName Optional - filter by map name\n */\n list(mapName?: string): Array<{\n mapName: string;\n name: string;\n priority?: number;\n keyPattern?: string;\n registeredBy?: string;\n }> {\n const result: Array<{\n mapName: string;\n name: string;\n priority?: number;\n keyPattern?: string;\n registeredBy?: string;\n }> = [];\n\n if (mapName) {\n const entries = this.resolvers.get(mapName) ?? [];\n for (const entry of entries) {\n result.push({\n mapName,\n name: entry.resolver.name,\n priority: entry.resolver.priority,\n keyPattern: entry.resolver.keyPattern,\n registeredBy: entry.registeredBy,\n });\n }\n } else {\n for (const [map, entries] of this.resolvers.entries()) {\n for (const entry of entries) {\n result.push({\n mapName: map,\n name: entry.resolver.name,\n priority: entry.resolver.priority,\n keyPattern: entry.resolver.keyPattern,\n registeredBy: entry.registeredBy,\n });\n }\n }\n }\n\n return result;\n }\n\n /**\n * Check if a map has any registered resolvers.\n */\n hasResolvers(mapName: string): boolean {\n const entries = this.resolvers.get(mapName);\n return entries !== undefined && entries.length > 0;\n }\n\n /**\n * Get the number of registered resolvers.\n */\n get size(): number {\n let count = 0;\n for (const entries of this.resolvers.values()) {\n count += entries.length;\n }\n return count;\n }\n\n /**\n * Clear all registered resolvers.\n *\n * @param mapName Optional - only clear resolvers for specific map\n */\n clear(mapName?: string): void {\n if (mapName) {\n this.resolvers.delete(mapName);\n } else {\n this.resolvers.clear();\n }\n }\n\n /**\n * Clear resolvers registered by a specific client.\n */\n clearByClient(clientId: string): number {\n let removed = 0;\n\n for (const [mapName, entries] of this.resolvers.entries()) {\n const before = entries.length;\n const filtered = entries.filter((e) => e.registeredBy !== clientId);\n removed += before - filtered.length;\n\n if (filtered.length === 0) {\n this.resolvers.delete(mapName);\n } else if (filtered.length !== before) {\n this.resolvers.set(mapName, filtered);\n }\n }\n\n return removed;\n }\n\n /**\n * Dispose the service.\n */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.resolvers.clear();\n logger.debug('ConflictResolverService disposed');\n }\n\n /**\n * Match a key against a glob-like pattern.\n * Supports * (any chars) and ? (single char).\n */\n private matchKeyPattern(key: string, pattern: string): boolean {\n // Convert glob pattern to regex\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars\n .replace(/\\*/g, '.*') // * -> .*\n .replace(/\\?/g, '.'); // ? -> .\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(key);\n }\n\n /**\n * Compile sandboxed resolver code.\n */\n private compileSandboxed<V>(\n name: string,\n code: string,\n ): (ctx: MergeContext<V>) => Promise<MergeResult<V>> {\n return async (ctx: MergeContext<V>) => {\n // Build the resolver code wrapper\n const wrappedCode = `\n const context = {\n mapName: ${JSON.stringify(ctx.mapName)},\n key: ${JSON.stringify(ctx.key)},\n localValue: ${JSON.stringify(ctx.localValue)},\n remoteValue: ${JSON.stringify(ctx.remoteValue)},\n localTimestamp: ${JSON.stringify(ctx.localTimestamp)},\n remoteTimestamp: ${JSON.stringify(ctx.remoteTimestamp)},\n remoteNodeId: ${JSON.stringify(ctx.remoteNodeId)},\n auth: ${JSON.stringify(ctx.auth)},\n };\n\n function resolve(context) {\n ${code}\n }\n\n const result = resolve(context);\n return { value: result, result };\n `;\n\n // Execute using ProcessorSandbox\n const result = await this.sandbox.execute(\n {\n name: `resolver:${name}`,\n code: wrappedCode,\n },\n null, // value parameter unused for resolvers\n 'resolver',\n );\n\n if (!result.success) {\n throw new Error(result.error || 'Resolver execution failed');\n }\n\n // Extract result from sandbox\n const resolverResult = (result as { result?: MergeResult<V> }).result;\n\n if (!resolverResult || typeof resolverResult !== 'object') {\n throw new Error('Resolver must return a result object');\n }\n\n // Validate result format\n const action = (resolverResult as MergeResult<V>).action;\n if (!['accept', 'reject', 'merge', 'local'].includes(action)) {\n throw new Error(`Invalid resolver action: ${action}`);\n }\n\n return resolverResult as MergeResult<V>;\n };\n }\n}\n","import {\n ConflictResolverDef,\n MergeContext,\n MergeResult,\n MergeRejection,\n LWWMap,\n LWWRecord,\n} from '@topgunbuild/core';\nimport {\n ConflictResolverService,\n ConflictResolverServiceConfig,\n} from '../ConflictResolverService';\nimport { ProcessorSandbox, ProcessorSandboxConfig } from '../ProcessorSandbox';\nimport { logger } from '../utils/logger';\n\n/**\n * Configuration for ConflictResolverHandler.\n */\nexport interface ConflictResolverHandlerConfig {\n /** Node ID for identifying server-side resolvers */\n nodeId: string;\n\n /** Optional sandbox configuration override */\n sandboxConfig?: Partial<ProcessorSandboxConfig>;\n\n /** Optional resolver service configuration */\n resolverConfig?: Partial<ConflictResolverServiceConfig>;\n}\n\n/**\n * Result of merge operation with resolver.\n */\nexport interface MergeWithResolverResult<V> {\n /** Whether the merge was applied */\n applied: boolean;\n\n /** The merge result details */\n result: MergeResult<V>;\n\n /** The final record if applied */\n record?: LWWRecord<V>;\n\n /** Rejection details if rejected */\n rejection?: MergeRejection;\n}\n\n/**\n * Server-side handler for Conflict Resolver operations.\n *\n * Responsibilities:\n * - Manage conflict resolver registrations\n * - Execute resolvers during merge operations\n * - Provide merge rejection notifications\n */\nexport class ConflictResolverHandler {\n private sandbox: ProcessorSandbox;\n private resolverService: ConflictResolverService;\n /** Reserved for future use (server-side resolver identification) */\n private readonly nodeId: string;\n private rejectionListeners: Set<(rejection: MergeRejection) => void> =\n new Set();\n\n constructor(config: ConflictResolverHandlerConfig) {\n this.nodeId = config.nodeId;\n this.sandbox = new ProcessorSandbox(config.sandboxConfig);\n this.resolverService = new ConflictResolverService(\n this.sandbox,\n config.resolverConfig,\n );\n\n // Wire up rejection handler\n this.resolverService.onRejection((rejection) => {\n for (const listener of this.rejectionListeners) {\n try {\n listener(rejection);\n } catch (e) {\n logger.error({ error: e }, 'Error in rejection listener');\n }\n }\n });\n }\n\n /**\n * Register a conflict resolver for a map.\n *\n * @param mapName The map name\n * @param resolver The resolver definition\n * @param clientId Optional client ID that registered this resolver\n */\n registerResolver<V>(\n mapName: string,\n resolver: ConflictResolverDef<V>,\n clientId?: string,\n ): void {\n this.resolverService.register(mapName, resolver, clientId);\n logger.info(\n {\n mapName,\n resolverName: resolver.name,\n priority: resolver.priority,\n clientId,\n },\n 'Resolver registered',\n );\n }\n\n /**\n * Unregister a conflict resolver.\n *\n * @param mapName The map name\n * @param resolverName The resolver name\n * @param clientId Optional - only unregister if registered by this client\n */\n unregisterResolver(\n mapName: string,\n resolverName: string,\n clientId?: string,\n ): boolean {\n const removed = this.resolverService.unregister(\n mapName,\n resolverName,\n clientId,\n );\n if (removed) {\n logger.info({ mapName, resolverName, clientId }, 'Resolver unregistered');\n }\n return removed;\n }\n\n /**\n * List registered resolvers.\n *\n * @param mapName Optional - filter by map name\n */\n listResolvers(mapName?: string): Array<{\n mapName: string;\n name: string;\n priority?: number;\n keyPattern?: string;\n }> {\n return this.resolverService.list(mapName);\n }\n\n /**\n * Apply a merge with conflict resolution.\n *\n * Deletions (tombstones) are also passed through resolvers to allow\n * protection via IMMUTABLE, OWNER_ONLY, or similar resolvers.\n * If no custom resolvers are registered, deletions use standard LWW.\n *\n * @param map The LWWMap to merge into\n * @param mapName The map name (for resolver lookup)\n * @param key The key being merged\n * @param record The incoming record\n * @param remoteNodeId The source node ID\n * @param auth Optional authentication context\n */\n async mergeWithResolver<V>(\n map: LWWMap<string, V>,\n mapName: string,\n key: string,\n record: LWWRecord<V>,\n remoteNodeId: string,\n auth?: MergeContext['auth'],\n ): Promise<MergeWithResolverResult<V>> {\n const isDeletion = record.value === null;\n const localRecord = map.getRecord(key);\n\n // Build merge context (works for both updates and deletions)\n const context: MergeContext<V> = {\n mapName,\n key,\n localValue: localRecord?.value ?? undefined,\n // For deletions, remoteValue is null - resolvers can check this\n remoteValue: record.value as V,\n localTimestamp: localRecord?.timestamp,\n remoteTimestamp: record.timestamp,\n remoteNodeId,\n auth,\n readEntry: (k: string) => map.get(k) as V | undefined,\n };\n\n // Resolve conflict (applies to both updates and deletions)\n const result = await this.resolverService.resolve(context);\n\n // Apply result\n switch (result.action) {\n case 'accept':\n case 'merge': {\n // For deletions, use the original null value regardless of merge result\n // For updates, use the resolved value\n const finalValue = isDeletion ? null : result.value;\n const finalRecord: LWWRecord<V> = {\n value: finalValue as V,\n timestamp: record.timestamp,\n ttlMs: record.ttlMs,\n };\n map.merge(key, finalRecord);\n return { applied: true, result, record: finalRecord };\n }\n\n case 'reject': {\n const rejection: MergeRejection = {\n mapName,\n key,\n attemptedValue: record.value,\n reason: result.reason,\n timestamp: record.timestamp,\n nodeId: remoteNodeId,\n };\n return { applied: false, result, rejection };\n }\n\n case 'local':\n default:\n return { applied: false, result };\n }\n }\n\n /**\n * Check if a map has custom resolvers registered.\n */\n hasResolvers(mapName: string): boolean {\n return this.resolverService.hasResolvers(mapName);\n }\n\n /**\n * Add a listener for merge rejections.\n */\n onRejection(listener: (rejection: MergeRejection) => void): () => void {\n this.rejectionListeners.add(listener);\n return () => this.rejectionListeners.delete(listener);\n }\n\n /**\n * Clear resolvers registered by a specific client.\n */\n clearByClient(clientId: string): number {\n return this.resolverService.clearByClient(clientId);\n }\n\n /**\n * Get the number of registered resolvers.\n */\n get resolverCount(): number {\n return this.resolverService.size;\n }\n\n /**\n * Check if sandbox is in secure mode.\n */\n isSecureMode(): boolean {\n return this.sandbox.isSecureMode();\n }\n\n /**\n * Dispose of the handler.\n */\n dispose(): void {\n this.resolverService.dispose();\n this.sandbox.dispose();\n this.rejectionListeners.clear();\n logger.debug('ConflictResolverHandler disposed');\n }\n}\n","import { Pool } from 'pg';\nimport {\n EventJournalImpl,\n JournalEvent,\n JournalEventInput,\n EventJournalConfig,\n DEFAULT_EVENT_JOURNAL_CONFIG,\n} from '@topgunbuild/core';\nimport { logger } from './utils/logger';\n\n/**\n * Export options for streaming journal events.\n */\nexport interface ExportOptions {\n /** Start from this sequence (inclusive) */\n fromSequence?: bigint;\n /** End at this sequence (inclusive) */\n toSequence?: bigint;\n /** Filter by map name */\n mapName?: string;\n /** Filter by event types */\n types?: ('PUT' | 'UPDATE' | 'DELETE')[];\n}\n\n/**\n * Configuration for EventJournalService.\n */\nexport interface EventJournalServiceConfig extends EventJournalConfig {\n /** PostgreSQL connection pool */\n pool: Pool;\n /** Table name for journal storage */\n tableName?: string;\n /** Batch size for persistence */\n persistBatchSize?: number;\n /** Interval for periodic persistence (ms) */\n persistIntervalMs?: number;\n}\n\n/**\n * Default configuration for EventJournalService.\n */\nexport const DEFAULT_JOURNAL_SERVICE_CONFIG: Omit<EventJournalServiceConfig, 'pool'> = {\n ...DEFAULT_EVENT_JOURNAL_CONFIG,\n tableName: 'event_journal',\n persistBatchSize: 100,\n persistIntervalMs: 1000,\n};\n\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction validateTableName(name: string): void {\n if (!TABLE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid table name \"${name}\". Table name must start with a letter or underscore and contain only alphanumeric characters and underscores.`\n );\n }\n}\n\n/**\n * Server-side Event Journal Service with PostgreSQL persistence.\n * Extends EventJournalImpl to add durable storage.\n */\nexport class EventJournalService extends EventJournalImpl {\n private readonly pool: Pool;\n private readonly tableName: string;\n private readonly persistBatchSize: number;\n private readonly persistIntervalMs: number;\n private pendingPersist: JournalEvent[] = [];\n private persistTimer?: ReturnType<typeof setInterval>;\n private isPersisting: boolean = false;\n private isInitialized: boolean = false;\n private isLoadingFromStorage: boolean = false;\n\n constructor(config: EventJournalServiceConfig) {\n super(config);\n this.pool = config.pool;\n this.tableName = config.tableName ?? DEFAULT_JOURNAL_SERVICE_CONFIG.tableName!;\n this.persistBatchSize = config.persistBatchSize ?? DEFAULT_JOURNAL_SERVICE_CONFIG.persistBatchSize!;\n this.persistIntervalMs = config.persistIntervalMs ?? DEFAULT_JOURNAL_SERVICE_CONFIG.persistIntervalMs!;\n\n validateTableName(this.tableName);\n\n // Subscribe to events for persistence\n this.subscribe((event) => {\n // Skip persistence for events being loaded from storage\n if (this.isLoadingFromStorage) return;\n\n if (event.sequence >= 0n && this.getConfig().persistent) {\n this.pendingPersist.push(event);\n\n if (this.pendingPersist.length >= this.persistBatchSize) {\n this.persistToStorage().catch((err) => {\n logger.error({ err }, 'Failed to persist journal events');\n });\n }\n }\n });\n\n // Start periodic persistence\n this.startPersistTimer();\n }\n\n /**\n * Initialize the journal service, creating table if needed.\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n const client = await this.pool.connect();\n try {\n await client.query(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n sequence BIGINT PRIMARY KEY,\n type VARCHAR(10) NOT NULL CHECK (type IN ('PUT', 'UPDATE', 'DELETE')),\n map_name VARCHAR(255) NOT NULL,\n key VARCHAR(1024) NOT NULL,\n value JSONB,\n previous_value JSONB,\n timestamp JSONB NOT NULL,\n node_id VARCHAR(64) NOT NULL,\n metadata JSONB,\n created_at TIMESTAMPTZ DEFAULT NOW()\n );\n `);\n\n // Create indexes for common queries\n await client.query(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_map_name\n ON ${this.tableName}(map_name);\n `);\n await client.query(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_key\n ON ${this.tableName}(map_name, key);\n `);\n await client.query(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_created_at\n ON ${this.tableName}(created_at);\n `);\n await client.query(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_node_id\n ON ${this.tableName}(node_id);\n `);\n\n this.isInitialized = true;\n logger.info({ tableName: this.tableName }, 'EventJournalService initialized');\n } finally {\n client.release();\n }\n }\n\n /**\n * Persist pending events to PostgreSQL.\n */\n async persistToStorage(): Promise<void> {\n if (this.pendingPersist.length === 0 || this.isPersisting) return;\n\n this.isPersisting = true;\n const batch = this.pendingPersist.splice(0, this.persistBatchSize);\n\n try {\n if (batch.length === 0) return;\n\n // Build parameterized query for batch insert\n const values: any[] = [];\n const placeholders: string[] = [];\n\n batch.forEach((e, i) => {\n const offset = i * 9;\n placeholders.push(\n `($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6}, $${offset + 7}, $${offset + 8}, $${offset + 9})`\n );\n values.push(\n e.sequence.toString(),\n e.type,\n e.mapName,\n e.key,\n e.value !== undefined ? JSON.stringify(e.value) : null,\n e.previousValue !== undefined ? JSON.stringify(e.previousValue) : null,\n JSON.stringify(e.timestamp),\n e.nodeId,\n e.metadata ? JSON.stringify(e.metadata) : null\n );\n });\n\n await this.pool.query(\n `INSERT INTO ${this.tableName}\n (sequence, type, map_name, key, value, previous_value, timestamp, node_id, metadata)\n VALUES ${placeholders.join(', ')}\n ON CONFLICT (sequence) DO NOTHING`,\n values\n );\n\n logger.debug({ count: batch.length }, 'Persisted journal events');\n } catch (error) {\n // Re-queue failed events\n this.pendingPersist.unshift(...batch);\n throw error;\n } finally {\n this.isPersisting = false;\n }\n }\n\n /**\n * Load journal events from PostgreSQL on startup.\n */\n async loadFromStorage(): Promise<void> {\n const config = this.getConfig();\n const result = await this.pool.query(\n `SELECT sequence, type, map_name, key, value, previous_value, timestamp, node_id, metadata\n FROM ${this.tableName}\n ORDER BY sequence DESC\n LIMIT $1`,\n [config.capacity]\n );\n\n // Load in reverse order (oldest first)\n const events = result.rows.reverse();\n\n // Set flag to prevent re-persisting loaded events\n this.isLoadingFromStorage = true;\n try {\n for (const row of events) {\n this.append({\n type: row.type,\n mapName: row.map_name,\n key: row.key,\n value: row.value,\n previousValue: row.previous_value,\n timestamp: typeof row.timestamp === 'string' ? JSON.parse(row.timestamp) : row.timestamp,\n nodeId: row.node_id,\n metadata: row.metadata,\n });\n }\n } finally {\n this.isLoadingFromStorage = false;\n }\n\n logger.info({ count: events.length }, 'Loaded journal events from storage');\n }\n\n /**\n * Export events as NDJSON stream.\n */\n exportStream(options: ExportOptions = {}): ReadableStream<string> {\n const self = this;\n\n return new ReadableStream({\n start(controller) {\n const startSeq = options.fromSequence ?? self.getOldestSequence();\n const endSeq = options.toSequence ?? self.getLatestSequence();\n\n for (let seq = startSeq; seq <= endSeq; seq++) {\n const events = self.readFrom(seq, 1);\n if (events.length > 0) {\n const event = events[0];\n\n // Apply filters\n if (options.mapName && event.mapName !== options.mapName) continue;\n if (options.types && !options.types.includes(event.type)) continue;\n\n // Convert bigint to string for JSON serialization\n const serializable = {\n ...event,\n sequence: event.sequence.toString(),\n };\n controller.enqueue(JSON.stringify(serializable) + '\\n');\n }\n }\n\n controller.close();\n },\n });\n }\n\n /**\n * Get events for a specific map.\n */\n getMapEvents(mapName: string, fromSeq?: bigint): JournalEvent[] {\n const events = this.readFrom(fromSeq ?? this.getOldestSequence(), this.getConfig().capacity);\n return events.filter((e) => e.mapName === mapName);\n }\n\n /**\n * Query events from PostgreSQL with filters.\n */\n async queryFromStorage(options: {\n mapName?: string;\n key?: string;\n types?: ('PUT' | 'UPDATE' | 'DELETE')[];\n fromSequence?: bigint;\n toSequence?: bigint;\n fromDate?: Date;\n toDate?: Date;\n limit?: number;\n offset?: number;\n } = {}): Promise<JournalEvent[]> {\n const conditions: string[] = [];\n const params: any[] = [];\n let paramIndex = 1;\n\n if (options.mapName) {\n conditions.push(`map_name = $${paramIndex++}`);\n params.push(options.mapName);\n }\n if (options.key) {\n conditions.push(`key = $${paramIndex++}`);\n params.push(options.key);\n }\n if (options.types && options.types.length > 0) {\n conditions.push(`type = ANY($${paramIndex++})`);\n params.push(options.types);\n }\n if (options.fromSequence !== undefined) {\n conditions.push(`sequence >= $${paramIndex++}`);\n params.push(options.fromSequence.toString());\n }\n if (options.toSequence !== undefined) {\n conditions.push(`sequence <= $${paramIndex++}`);\n params.push(options.toSequence.toString());\n }\n if (options.fromDate) {\n conditions.push(`created_at >= $${paramIndex++}`);\n params.push(options.fromDate);\n }\n if (options.toDate) {\n conditions.push(`created_at <= $${paramIndex++}`);\n params.push(options.toDate);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = options.limit ?? 100;\n const offset = options.offset ?? 0;\n\n const result = await this.pool.query(\n `SELECT sequence, type, map_name, key, value, previous_value, timestamp, node_id, metadata\n FROM ${this.tableName}\n ${whereClause}\n ORDER BY sequence ASC\n LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,\n [...params, limit, offset]\n );\n\n return result.rows.map((row) => ({\n sequence: BigInt(row.sequence),\n type: row.type,\n mapName: row.map_name,\n key: row.key,\n value: row.value,\n previousValue: row.previous_value,\n timestamp: typeof row.timestamp === 'string' ? JSON.parse(row.timestamp) : row.timestamp,\n nodeId: row.node_id,\n metadata: row.metadata,\n }));\n }\n\n /**\n * Count events matching filters.\n */\n async countFromStorage(options: {\n mapName?: string;\n types?: ('PUT' | 'UPDATE' | 'DELETE')[];\n fromDate?: Date;\n toDate?: Date;\n } = {}): Promise<number> {\n const conditions: string[] = [];\n const params: any[] = [];\n let paramIndex = 1;\n\n if (options.mapName) {\n conditions.push(`map_name = $${paramIndex++}`);\n params.push(options.mapName);\n }\n if (options.types && options.types.length > 0) {\n conditions.push(`type = ANY($${paramIndex++})`);\n params.push(options.types);\n }\n if (options.fromDate) {\n conditions.push(`created_at >= $${paramIndex++}`);\n params.push(options.fromDate);\n }\n if (options.toDate) {\n conditions.push(`created_at <= $${paramIndex++}`);\n params.push(options.toDate);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n const result = await this.pool.query(\n `SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`,\n params\n );\n\n return parseInt(result.rows[0].count, 10);\n }\n\n /**\n * Cleanup old events based on retention policy.\n */\n async cleanupOldEvents(retentionDays: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM ${this.tableName}\n WHERE created_at < NOW() - ($1 || ' days')::INTERVAL\n RETURNING sequence`,\n [retentionDays]\n );\n\n const count = result.rowCount ?? 0;\n if (count > 0) {\n logger.info({ deletedCount: count, retentionDays }, 'Cleaned up old journal events');\n }\n\n return count;\n }\n\n /**\n * Start the periodic persistence timer.\n */\n private startPersistTimer(): void {\n this.persistTimer = setInterval(() => {\n if (this.pendingPersist.length > 0) {\n this.persistToStorage().catch((err) => {\n logger.error({ err }, 'Periodic persist failed');\n });\n }\n }, this.persistIntervalMs);\n }\n\n /**\n * Stop the periodic persistence timer.\n */\n private stopPersistTimer(): void {\n if (this.persistTimer) {\n clearInterval(this.persistTimer);\n this.persistTimer = undefined;\n }\n }\n\n /**\n * Dispose resources and persist remaining events.\n */\n override dispose(): void {\n this.stopPersistTimer();\n\n // Final persist\n if (this.pendingPersist.length > 0) {\n this.persistToStorage().catch((err) => {\n logger.error({ err }, 'Final persist failed on dispose');\n });\n }\n\n super.dispose();\n }\n\n /**\n * Get pending persist count (for monitoring).\n */\n getPendingPersistCount(): number {\n return this.pendingPersist.length;\n }\n}\n","import { Pool, PoolConfig } from 'pg';\nimport { LWWRecord } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue } from './IServerStorage';\n\nexport interface PostgresAdapterOptions {\n tableName?: string;\n}\n\nconst DEFAULT_TABLE_NAME = 'topgun_maps';\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction validateTableName(name: string): void {\n if (!TABLE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid table name \"${name}\". Table name must start with a letter or underscore and contain only alphanumeric characters and underscores.`\n );\n }\n}\n\nexport class PostgresAdapter implements IServerStorage {\n private pool: Pool;\n private tableName: string;\n\n constructor(configOrPool: PoolConfig | Pool, options?: PostgresAdapterOptions) {\n if (configOrPool instanceof Pool || (configOrPool as any).connect) {\n this.pool = configOrPool as Pool;\n } else {\n this.pool = new Pool(configOrPool as PoolConfig);\n }\n\n const tableName = options?.tableName ?? DEFAULT_TABLE_NAME;\n validateTableName(tableName);\n this.tableName = tableName;\n }\n\n async initialize(): Promise<void> {\n const client = await this.pool.connect();\n try {\n // Create a generic table for storing key-value pairs per map\n // schema: map_name (text), key (text), value (jsonb), timestamp_millis (bigint), timestamp_counter (int), node_id (text), is_deleted (boolean)\n await client.query(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n map_name TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSONB,\n ts_millis BIGINT NOT NULL,\n ts_counter INTEGER NOT NULL,\n ts_node_id TEXT NOT NULL,\n is_deleted BOOLEAN DEFAULT FALSE,\n PRIMARY KEY (map_name, key)\n );\n `);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n const res = await this.pool.query(\n `SELECT value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = $2`,\n [mapName, key]\n );\n\n if (res.rows.length === 0) return undefined;\n\n const row = res.rows[0];\n return this.mapRowToRecord(row);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const result = new Map<string, StorageValue<any>>();\n if (keys.length === 0) return result;\n\n const res = await this.pool.query(\n `SELECT key, value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = ANY($2)`,\n [mapName, keys]\n );\n\n for (const row of res.rows) {\n result.set(row.key, this.mapRowToRecord(row));\n }\n\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n const res = await this.pool.query(\n `SELECT key FROM ${this.tableName} WHERE map_name = $1`,\n [mapName]\n );\n return res.rows.map(row => row.key);\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n let value: any;\n let tsMillis: number;\n let tsCounter: number;\n let tsNodeId: string;\n let isDeleted: boolean;\n\n if (this.isORMapValue(record)) {\n // Store ORMap data\n // We use a special marker in ts_node_id to distinguish ORMap data from LWW data\n value = record;\n tsMillis = 0;\n tsCounter = 0;\n tsNodeId = '__ORMAP__';\n isDeleted = false;\n } else {\n // LWWRecord\n const lww = record as LWWRecord<any>;\n value = lww.value;\n tsMillis = lww.timestamp.millis;\n tsCounter = lww.timestamp.counter;\n tsNodeId = lww.timestamp.nodeId;\n isDeleted = lww.value === null;\n }\n\n await this.pool.query(\n `INSERT INTO ${this.tableName} (map_name, key, value, ts_millis, ts_counter, ts_node_id, is_deleted)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (map_name, key) DO UPDATE SET\n value = EXCLUDED.value,\n ts_millis = EXCLUDED.ts_millis,\n ts_counter = EXCLUDED.ts_counter,\n ts_node_id = EXCLUDED.ts_node_id,\n is_deleted = EXCLUDED.is_deleted`,\n [\n mapName,\n key,\n JSON.stringify(value),\n tsMillis,\n tsCounter,\n tsNodeId,\n isDeleted\n ]\n );\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n // Note: For high performance, this should use UNNEST or multi-row INSERT.\n // Keeping loop for simplicity in MVP alignment.\n for (const [key, record] of records) {\n await this.store(mapName, key, record); \n }\n await client.query('COMMIT');\n } catch (e) {\n await client.query('ROLLBACK');\n throw e;\n } finally {\n client.release();\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n await this.pool.query(`DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = $2`, [mapName, key]);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.pool.query(\n `DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = ANY($2)`, \n [mapName, keys]\n );\n }\n\n private mapRowToRecord(row: any): StorageValue<any> {\n if (row.ts_node_id === '__ORMAP__') {\n // It's an ORMap value (ORMapValue or ORMapTombstones)\n return row.value as StorageValue<any>;\n }\n\n // It's LWWRecord\n return {\n value: row.is_deleted ? null : row.value,\n timestamp: {\n millis: Number(row.ts_millis),\n counter: row.ts_counter,\n nodeId: row.ts_node_id\n }\n };\n }\n\n private isORMapValue(record: any): boolean {\n return (record && typeof record === 'object' && (record.type === 'OR' || record.type === 'OR_TOMBSTONES'));\n }\n}\n","import { IServerStorage, StorageValue } from './IServerStorage';\n\n/**\n * In-memory implementation of IServerStorage.\n * Useful for development, testing, and demos without requiring a database.\n *\n * Note: Data is lost when the server restarts.\n */\nexport class MemoryServerAdapter implements IServerStorage {\n // Map<mapName, Map<key, value>>\n private storage = new Map<string, Map<string, StorageValue<any>>>();\n\n async initialize(): Promise<void> {\n // No-op for in-memory storage\n console.log('[MemoryServerAdapter] Initialized in-memory storage');\n }\n\n async close(): Promise<void> {\n this.storage.clear();\n console.log('[MemoryServerAdapter] Storage cleared and closed');\n }\n\n private getMap(mapName: string): Map<string, StorageValue<any>> {\n let map = this.storage.get(mapName);\n if (!map) {\n map = new Map();\n this.storage.set(mapName, map);\n }\n return map;\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n return this.getMap(mapName).get(key);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const map = this.getMap(mapName);\n const result = new Map<string, StorageValue<any>>();\n for (const key of keys) {\n const value = map.get(key);\n if (value !== undefined) {\n result.set(key, value);\n }\n }\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n return Array.from(this.getMap(mapName).keys());\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n this.getMap(mapName).set(key, record);\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const map = this.getMap(mapName);\n for (const [key, value] of records) {\n map.set(key, value);\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n this.getMap(mapName).delete(key);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n const map = this.getMap(mapName);\n for (const key of keys) {\n map.delete(key);\n }\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\nexport class TimestampInterceptor implements IInterceptor {\n name = 'TimestampInterceptor';\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp> {\n // Only apply to PUT operations with LWW records\n if (op.opType === 'PUT' && op.record && op.record.value) {\n // Modifying the value to include server timestamp\n // This assumes value is an object where we can add properties\n if (typeof op.record.value === 'object' && op.record.value !== null && !Array.isArray(op.record.value)) {\n const newValue = {\n ...op.record.value,\n _serverTimestamp: Date.now()\n };\n logger.debug({ key: op.key, mapName: op.mapName, interceptor: this.name }, 'Added timestamp');\n return {\n ...op,\n record: {\n ...op.record,\n value: newValue\n }\n };\n }\n }\n return op;\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\ninterface RateLimitConfig {\n windowMs: number;\n maxOps: number;\n}\n\ninterface ClientLimit {\n count: number;\n resetTime: number;\n}\n\nexport class RateLimitInterceptor implements IInterceptor {\n name = 'RateLimitInterceptor';\n \n private limits = new Map<string, ClientLimit>();\n private config: RateLimitConfig;\n\n constructor(config: RateLimitConfig = { windowMs: 1000, maxOps: 50 }) {\n this.config = config;\n }\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp | null> {\n // Rate limit based on clientId\n const clientId = context.clientId;\n const now = Date.now();\n \n let limit = this.limits.get(clientId);\n \n if (!limit || now > limit.resetTime) {\n limit = {\n count: 0,\n resetTime: now + this.config.windowMs\n };\n this.limits.set(clientId, limit);\n }\n\n limit.count++;\n\n if (limit.count > this.config.maxOps) {\n logger.warn({ clientId, opId: op.id, count: limit.count }, 'Rate limit exceeded');\n throw new Error('Rate limit exceeded');\n }\n\n return op;\n }\n\n // Cleanup old entries periodically? \n // For now we rely on resetTime check, but map grows. \n // Simple cleanup on reset logic:\n // In a real system, we'd use Redis or a proper cache with TTL.\n // Here we can just prune occasionally or relying on connection disconnect?\n \n // Optimization: Cleanup on disconnect\n async onDisconnect(context: any) {\n this.limits.delete(context.clientId);\n }\n}\n\n","/**\n * Native Module Statistics\n *\n * Provides information about available native optimizations.\n *\n * Phase 3.05: Integration\n */\n\nimport { isUsingNativeHash } from '@topgunbuild/core';\nimport {\n SharedMemoryManager,\n type SharedMemoryStats,\n} from '../workers/SharedMemoryManager';\n\n/**\n * Native module availability status\n */\nexport interface NativeModuleStatus {\n /** Native xxHash64 is available and being used */\n nativeHash: boolean;\n /** SharedArrayBuffer is available */\n sharedArrayBuffer: boolean;\n}\n\n/**\n * Comprehensive native statistics\n */\nexport interface NativeStats {\n /** Module availability status */\n modules: NativeModuleStatus;\n /** Shared memory statistics (if enabled) */\n sharedMemory: SharedMemoryStats | null;\n /** Summary of what's being used */\n summary: string;\n}\n\n/**\n * Check which native modules are available.\n */\nexport function getNativeModuleStatus(): NativeModuleStatus {\n return {\n nativeHash: isUsingNativeHash(),\n sharedArrayBuffer: SharedMemoryManager.isAvailable(),\n };\n}\n\n/**\n * Get native statistics including shared memory.\n *\n * @param sharedMemoryManager - Optional SharedMemoryManager instance\n */\nexport function getNativeStats(\n sharedMemoryManager?: SharedMemoryManager\n): NativeStats {\n const modules = getNativeModuleStatus();\n\n const summaryParts: string[] = [];\n\n if (modules.nativeHash) {\n summaryParts.push('native xxHash64');\n } else {\n summaryParts.push('FNV-1a (JS fallback)');\n }\n\n if (modules.sharedArrayBuffer) {\n summaryParts.push('SharedArrayBuffer available');\n } else {\n summaryParts.push('SharedArrayBuffer unavailable');\n }\n\n return {\n modules,\n sharedMemory: sharedMemoryManager?.getStats() ?? null,\n summary: summaryParts.join(', '),\n };\n}\n\n/**\n * Log native module status to console.\n */\nexport function logNativeStatus(): void {\n const status = getNativeModuleStatus();\n\n console.log('[TopGun] Native Module Status:');\n console.log(` - Hash: ${status.nativeHash ? 'native xxHash64' : 'FNV-1a (JS)'}`);\n console.log(\n ` - SharedArrayBuffer: ${status.sharedArrayBuffer ? 'available' : 'unavailable'}`\n );\n}\n","/**\n * ClusterCoordinator - Unified cluster integration layer\n *\n * Phase 4 Task 06: System Integration\n *\n * Coordinates all cluster components:\n * - ClusterManager: P2P WebSocket mesh\n * - PartitionService: Consistent hashing & routing\n * - MigrationManager: Gradual rebalancing\n * - ReplicationPipeline: Async replication with consistency levels\n * - LagTracker: Replication health monitoring\n */\n\nimport { EventEmitter } from 'events';\nimport { ClusterManager, ClusterConfig } from './ClusterManager';\nimport { PartitionService, PartitionServiceConfig, PartitionDistribution } from './PartitionService';\nimport { MigrationManager } from './MigrationManager';\nimport { ReplicationPipeline } from './ReplicationPipeline';\nimport { LagTracker } from './LagTracker';\nimport {\n MigrationConfig,\n MigrationStatus,\n MigrationMetrics,\n ReplicationConfig,\n ReplicationHealth,\n ReplicationLag,\n ReplicationResult,\n ConsistencyLevel,\n PartitionMap,\n PartitionChange,\n DEFAULT_MIGRATION_CONFIG,\n DEFAULT_REPLICATION_CONFIG,\n} from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\n// ============================================\n// Unified Cluster Configuration\n// ============================================\n\nexport interface ClusterCoordinatorConfig {\n /** Cluster node configuration */\n cluster: ClusterConfig;\n\n /** Enable gradual partition rebalancing (default: true) */\n gradualRebalancing: boolean;\n\n /** Migration configuration for gradual rebalancing */\n migration: Partial<MigrationConfig>;\n\n /** Replication configuration */\n replication: Partial<ReplicationConfig>;\n\n /** Enable async replication pipeline (default: true) */\n replicationEnabled: boolean;\n\n /** Data collector callback for migrations */\n dataCollector?: (partitionId: number) => Promise<Uint8Array[]>;\n\n /** Data storer callback for incoming migrations */\n dataStorer?: (partitionId: number, data: Uint8Array[]) => Promise<void>;\n}\n\nexport const DEFAULT_CLUSTER_COORDINATOR_CONFIG: Omit<ClusterCoordinatorConfig, 'cluster'> = {\n gradualRebalancing: true,\n migration: DEFAULT_MIGRATION_CONFIG,\n replication: DEFAULT_REPLICATION_CONFIG,\n replicationEnabled: true,\n};\n\n// ============================================\n// Cluster Coordinator Events\n// ============================================\n\nexport interface ClusterCoordinatorEvents {\n 'started': () => void;\n 'stopped': () => void;\n 'member:joined': (nodeId: string) => void;\n 'member:left': (nodeId: string) => void;\n 'partition:rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;\n 'partition:moved': (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => void;\n 'migration:started': (partitionId: number, targetNode: string) => void;\n 'migration:completed': (partitionId: number) => void;\n 'migration:failed': (partitionId: number, error: Error) => void;\n 'replication:unhealthy': (nodeId: string) => void;\n 'replication:healthy': (nodeId: string) => void;\n 'error': (error: Error) => void;\n}\n\n// ============================================\n// Cluster Coordinator\n// ============================================\n\nexport class ClusterCoordinator extends EventEmitter {\n private readonly config: ClusterCoordinatorConfig;\n\n // Core components\n private clusterManager: ClusterManager;\n private partitionService: PartitionService;\n private replicationPipeline: ReplicationPipeline | null = null;\n private lagTracker: LagTracker;\n\n // State\n private started: boolean = false;\n private actualPort: number = 0;\n\n constructor(config: ClusterCoordinatorConfig) {\n super();\n this.config = {\n ...DEFAULT_CLUSTER_COORDINATOR_CONFIG,\n ...config,\n };\n\n // Initialize core components\n this.clusterManager = new ClusterManager(this.config.cluster);\n this.lagTracker = new LagTracker();\n\n // Initialize partition service with or without gradual rebalancing\n const partitionServiceConfig: Partial<PartitionServiceConfig> = {\n gradualRebalancing: this.config.gradualRebalancing,\n migration: this.config.migration,\n };\n this.partitionService = new PartitionService(this.clusterManager, partitionServiceConfig);\n\n // Initialize replication pipeline if enabled\n if (this.config.replicationEnabled) {\n this.replicationPipeline = new ReplicationPipeline(\n this.clusterManager,\n this.partitionService,\n this.config.replication\n );\n }\n\n this.setupEventHandlers();\n }\n\n // ============================================\n // Lifecycle Methods\n // ============================================\n\n /**\n * Start the cluster coordinator\n */\n public async start(): Promise<number> {\n if (this.started) {\n return this.actualPort;\n }\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'Starting ClusterCoordinator');\n\n // Start cluster manager\n this.actualPort = await this.clusterManager.start();\n\n // Configure migration data handlers if provided\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager && this.config.dataCollector) {\n migrationManager.setDataCollector(this.config.dataCollector);\n }\n if (migrationManager && this.config.dataStorer) {\n migrationManager.setDataStorer(this.config.dataStorer);\n }\n\n this.started = true;\n this.emit('started');\n\n logger.info({ nodeId: this.config.cluster.nodeId, port: this.actualPort }, 'ClusterCoordinator started');\n return this.actualPort;\n }\n\n /**\n * Stop the cluster coordinator\n */\n public async stop(): Promise<void> {\n if (!this.started) return;\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'Stopping ClusterCoordinator');\n\n // Cancel any active migrations\n await this.partitionService.cancelMigrations();\n\n // Close replication pipeline\n this.replicationPipeline?.close();\n\n // Stop cluster manager\n this.clusterManager.stop();\n\n this.started = false;\n this.emit('stopped');\n\n logger.info({ nodeId: this.config.cluster.nodeId }, 'ClusterCoordinator stopped');\n }\n\n // ============================================\n // Cluster Information\n // ============================================\n\n /**\n * Get local node ID\n */\n public getNodeId(): string {\n return this.config.cluster.nodeId;\n }\n\n /**\n * Get cluster port\n */\n public getPort(): number {\n return this.actualPort;\n }\n\n /**\n * Get all cluster members\n */\n public getMembers(): string[] {\n return this.clusterManager.getMembers();\n }\n\n /**\n * Check if this is the local node\n */\n public isLocal(nodeId: string): boolean {\n return this.clusterManager.isLocal(nodeId);\n }\n\n /**\n * Check if coordinator is started\n */\n public isStarted(): boolean {\n return this.started;\n }\n\n // ============================================\n // Partition Operations\n // ============================================\n\n /**\n * Get current partition map\n */\n public getPartitionMap(): PartitionMap {\n return this.partitionService.getPartitionMap();\n }\n\n /**\n * Get partition map version\n */\n public getPartitionMapVersion(): number {\n return this.partitionService.getMapVersion();\n }\n\n /**\n * Get partition ID for a key\n */\n public getPartitionId(key: string): number {\n return this.partitionService.getPartitionId(key);\n }\n\n /**\n * Get owner node for a key\n */\n public getOwner(key: string): string {\n return this.partitionService.getOwner(key);\n }\n\n /**\n * Check if this node owns the key\n */\n public isLocalOwner(key: string): boolean {\n return this.partitionService.isLocalOwner(key);\n }\n\n /**\n * Check if this node is a backup for the key\n */\n public isLocalBackup(key: string): boolean {\n return this.partitionService.isLocalBackup(key);\n }\n\n /**\n * Get backup nodes for a partition\n */\n public getBackups(partitionId: number): string[] {\n return this.partitionService.getBackups(partitionId);\n }\n\n /**\n * Check if partition is currently migrating\n */\n public isMigrating(partitionId: number): boolean {\n return this.partitionService.isMigrating(partitionId);\n }\n\n /**\n * Check if any rebalancing is in progress\n */\n public isRebalancing(): boolean {\n return this.partitionService.isRebalancing();\n }\n\n // ============================================\n // Migration Operations\n // ============================================\n\n /**\n * Get migration status\n */\n public getMigrationStatus(): MigrationStatus | null {\n return this.partitionService.getMigrationStatus();\n }\n\n /**\n * Get migration metrics\n */\n public getMigrationMetrics(): MigrationMetrics | null {\n return this.partitionService.getMigrationManager()?.getMetrics() ?? null;\n }\n\n /**\n * Cancel all active migrations\n */\n public async cancelMigrations(): Promise<void> {\n await this.partitionService.cancelMigrations();\n }\n\n /**\n * Set data collector for migrations\n */\n public setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void {\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.setDataCollector(collector);\n }\n }\n\n /**\n * Set data storer for incoming migrations\n */\n public setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void {\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.setDataStorer(storer);\n }\n }\n\n // ============================================\n // Replication Operations\n // ============================================\n\n /**\n * Replicate an operation to backup nodes\n */\n public async replicate(\n operation: unknown,\n opId: string,\n key: string,\n options: { consistency?: ConsistencyLevel; timeout?: number } = {}\n ): Promise<ReplicationResult> {\n if (!this.replicationPipeline) {\n return { success: true, ackedBy: [] };\n }\n return this.replicationPipeline.replicate(operation, opId, key, options);\n }\n\n /**\n * Get replication health status\n */\n public getReplicationHealth(): ReplicationHealth {\n return this.lagTracker.getHealth();\n }\n\n /**\n * Get replication lag for a specific node\n */\n public getReplicationLag(nodeId: string): ReplicationLag {\n return this.lagTracker.getLag(nodeId);\n }\n\n /**\n * Check if a node is healthy for replication\n */\n public isNodeHealthy(nodeId: string): boolean {\n return this.lagTracker.isNodeHealthy(nodeId);\n }\n\n /**\n * Check if a node is laggy\n */\n public isNodeLaggy(nodeId: string): boolean {\n return this.lagTracker.isNodeLaggy(nodeId);\n }\n\n // ============================================\n // Cluster Communication\n // ============================================\n\n /**\n * Send message to a specific node\n */\n public send(nodeId: string, message: unknown): void {\n this.clusterManager.sendToNode(nodeId, message);\n }\n\n /**\n * Broadcast message to all nodes\n */\n public broadcast(message: unknown): void {\n for (const nodeId of this.clusterManager.getMembers()) {\n if (!this.clusterManager.isLocal(nodeId)) {\n this.clusterManager.sendToNode(nodeId, message);\n }\n }\n }\n\n // ============================================\n // Component Access\n // ============================================\n\n /**\n * Get underlying ClusterManager\n */\n public getClusterManager(): ClusterManager {\n return this.clusterManager;\n }\n\n /**\n * Get underlying PartitionService\n */\n public getPartitionService(): PartitionService {\n return this.partitionService;\n }\n\n /**\n * Get underlying ReplicationPipeline\n */\n public getReplicationPipeline(): ReplicationPipeline | null {\n return this.replicationPipeline;\n }\n\n /**\n * Get underlying LagTracker\n */\n public getLagTracker(): LagTracker {\n return this.lagTracker;\n }\n\n // ============================================\n // Metrics Export\n // ============================================\n\n /**\n * Get all metrics in Prometheus format\n */\n public getPrometheusMetrics(): string {\n const lines: string[] = [];\n\n // Cluster info\n lines.push('# HELP topgun_cluster_members Number of cluster members');\n lines.push('# TYPE topgun_cluster_members gauge');\n lines.push(`topgun_cluster_members ${this.clusterManager.getMembers().length}`);\n\n lines.push('');\n lines.push('# HELP topgun_cluster_started Cluster started status (1=started, 0=stopped)');\n lines.push('# TYPE topgun_cluster_started gauge');\n lines.push(`topgun_cluster_started ${this.started ? 1 : 0}`);\n\n // Partition map info\n lines.push('');\n lines.push('# HELP topgun_partition_map_version Current partition map version');\n lines.push('# TYPE topgun_partition_map_version gauge');\n lines.push(`topgun_partition_map_version ${this.partitionService.getMapVersion()}`);\n\n // Migration metrics\n const migrationMetrics = this.getMigrationMetrics();\n if (migrationMetrics) {\n lines.push('');\n lines.push('# HELP topgun_migrations_started Total migrations started');\n lines.push('# TYPE topgun_migrations_started counter');\n lines.push(`topgun_migrations_started ${migrationMetrics.migrationsStarted}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_completed Total migrations completed');\n lines.push('# TYPE topgun_migrations_completed counter');\n lines.push(`topgun_migrations_completed ${migrationMetrics.migrationsCompleted}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_failed Total migrations failed');\n lines.push('# TYPE topgun_migrations_failed counter');\n lines.push(`topgun_migrations_failed ${migrationMetrics.migrationsFailed}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_active Currently active migrations');\n lines.push('# TYPE topgun_migrations_active gauge');\n lines.push(`topgun_migrations_active ${migrationMetrics.activeMigrations}`);\n\n lines.push('');\n lines.push('# HELP topgun_migrations_queued Queued migrations');\n lines.push('# TYPE topgun_migrations_queued gauge');\n lines.push(`topgun_migrations_queued ${migrationMetrics.queuedMigrations}`);\n }\n\n // Replication metrics from LagTracker\n lines.push('');\n lines.push(this.lagTracker.toPrometheusMetrics());\n\n return lines.join('\\n');\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private setupEventHandlers(): void {\n // ClusterManager events\n this.clusterManager.on('memberJoined', (nodeId: string) => {\n logger.info({ nodeId }, 'Cluster member joined');\n this.emit('member:joined', nodeId);\n });\n\n this.clusterManager.on('memberLeft', (nodeId: string) => {\n logger.info({ nodeId }, 'Cluster member left');\n this.lagTracker.removeNode(nodeId);\n this.emit('member:left', nodeId);\n });\n\n // PartitionService events\n this.partitionService.on('rebalanced', (map: PartitionMap, changes: PartitionChange[]) => {\n logger.info({ version: map.version, changesCount: changes.length }, 'Partition map rebalanced');\n this.emit('partition:rebalanced', map, changes);\n });\n\n this.partitionService.on('partitionMoved', (info: { partitionId: number; previousOwner: string; newOwner: string; version: number }) => {\n this.emit('partition:moved', info);\n });\n\n // MigrationManager events (if gradual rebalancing enabled)\n const migrationManager = this.partitionService.getMigrationManager();\n if (migrationManager) {\n migrationManager.on('migrationStarted', (partitionId: number, targetNode: string) => {\n this.emit('migration:started', partitionId, targetNode);\n });\n\n migrationManager.on('migrationComplete', (partitionId: number) => {\n this.emit('migration:completed', partitionId);\n });\n\n migrationManager.on('migrationFailed', (partitionId: number, error: Error) => {\n this.emit('migration:failed', partitionId, error);\n });\n }\n\n // ReplicationPipeline events\n if (this.replicationPipeline) {\n this.replicationPipeline.on('ackReceived', (nodeId: string) => {\n this.lagTracker.recordAck(nodeId);\n });\n\n this.replicationPipeline.on('replicationSent', (nodeId: string) => {\n this.lagTracker.incrementPending(nodeId);\n });\n }\n }\n}\n","import {\n LWWMap,\n LWWRecord,\n MergeContext,\n MergeResult,\n MergeRejection,\n Timestamp,\n HLC,\n} from '@topgunbuild/core';\nimport { ConflictResolverService } from './ConflictResolverService';\n\n/**\n * Configuration for MapWithResolver.\n */\nexport interface MapWithResolverConfig {\n /** Map name */\n name: string;\n\n /** Node ID for HLC */\n nodeId: string;\n\n /** Conflict resolver service */\n resolverService: ConflictResolverService;\n\n /** Callback for merge rejections */\n onRejection?: (rejection: MergeRejection) => void;\n}\n\n/**\n * Result of setWithResolver operation.\n */\nexport interface SetWithResolverResult<V> {\n /** Whether the value was applied */\n applied: boolean;\n\n /** The merge result */\n result: MergeResult<V>;\n\n /** The final record if applied */\n record?: LWWRecord<V>;\n}\n\n/**\n * Extended LWWMap that supports custom conflict resolvers.\n *\n * This wrapper delegates merge operations to ConflictResolverService,\n * allowing custom business logic to intercept and modify merge behavior.\n */\nexport class MapWithResolver<K extends string, V> {\n private map: LWWMap<K, V>;\n private resolverService: ConflictResolverService;\n private mapName: string;\n private hlc: HLC;\n private onRejection?: (rejection: MergeRejection) => void;\n\n constructor(config: MapWithResolverConfig) {\n this.mapName = config.name;\n this.hlc = new HLC(config.nodeId);\n this.map = new LWWMap<K, V>(this.hlc);\n this.resolverService = config.resolverService;\n this.onRejection = config.onRejection;\n }\n\n /**\n * Get the map name.\n */\n get name(): string {\n return this.mapName;\n }\n\n /**\n * Get the underlying LWWMap.\n */\n get rawMap(): LWWMap<K, V> {\n return this.map;\n }\n\n /**\n * Get a value by key.\n */\n get(key: K): V | undefined {\n return this.map.get(key);\n }\n\n /**\n * Get the full record for a key.\n */\n getRecord(key: K): LWWRecord<V> | undefined {\n return this.map.getRecord(key);\n }\n\n /**\n * Get the timestamp for a key.\n */\n getTimestamp(key: K): Timestamp | undefined {\n return this.map.getRecord(key)?.timestamp;\n }\n\n /**\n * Set a value locally (no resolver).\n * Use for server-initiated writes.\n */\n set(key: K, value: V, ttlMs?: number): LWWRecord<V> {\n return this.map.set(key, value, ttlMs);\n }\n\n /**\n * Set a value with conflict resolution.\n * Use for client-initiated writes.\n *\n * @param key The key to set\n * @param value The new value\n * @param timestamp The client's timestamp\n * @param remoteNodeId The client's node ID\n * @param auth Optional authentication context\n * @returns Result containing applied status and merge result\n */\n async setWithResolver(\n key: K,\n value: V,\n timestamp: Timestamp,\n remoteNodeId: string,\n auth?: MergeContext['auth'],\n ): Promise<SetWithResolverResult<V>> {\n // Build merge context\n const context: MergeContext<V> = {\n mapName: this.mapName,\n key,\n localValue: this.map.get(key),\n remoteValue: value,\n localTimestamp: this.getTimestamp(key),\n remoteTimestamp: timestamp,\n remoteNodeId,\n auth,\n readEntry: (k: string) => this.map.get(k as K),\n };\n\n // Resolve conflict\n const result = await this.resolverService.resolve(context);\n\n // Apply result\n switch (result.action) {\n case 'accept': {\n // Create record with the accepted value\n const record: LWWRecord<V> = {\n value: result.value,\n timestamp,\n };\n // Use internal merge to properly update HLC and Merkle tree\n this.map.merge(key, record);\n return { applied: true, result, record };\n }\n\n case 'merge': {\n // Create record with the merged value\n const record: LWWRecord<V> = {\n value: result.value,\n timestamp,\n };\n this.map.merge(key, record);\n return { applied: true, result, record };\n }\n\n case 'reject': {\n // Emit rejection event\n if (this.onRejection) {\n this.onRejection({\n mapName: this.mapName,\n key,\n attemptedValue: value,\n reason: result.reason,\n timestamp,\n nodeId: remoteNodeId,\n });\n }\n return { applied: false, result };\n }\n\n case 'local': {\n // Keep current value, don't update\n return { applied: false, result };\n }\n\n default:\n // Shouldn't happen, but fallback to accept\n const record: LWWRecord<V> = {\n value: (result as MergeResult<V> & { action: 'accept' }).value ?? value,\n timestamp,\n };\n this.map.merge(key, record);\n return { applied: true, result, record };\n }\n }\n\n /**\n * Remove a key.\n */\n remove(key: K): LWWRecord<V> {\n return this.map.remove(key);\n }\n\n /**\n * Standard merge without resolver (for sync operations).\n */\n merge(key: K, record: LWWRecord<V>): boolean {\n return this.map.merge(key, record);\n }\n\n /**\n * Merge with resolver support.\n * Equivalent to setWithResolver but takes a full record.\n */\n async mergeWithResolver(\n key: K,\n record: LWWRecord<V>,\n remoteNodeId: string,\n auth?: MergeContext['auth'],\n ): Promise<SetWithResolverResult<V>> {\n if (record.value === null) {\n // Tombstone - apply directly without resolver\n const applied = this.map.merge(key, record);\n return {\n applied,\n result: applied\n ? { action: 'accept', value: record.value as V }\n : { action: 'local' },\n record: applied ? record : undefined,\n };\n }\n\n return this.setWithResolver(\n key,\n record.value,\n record.timestamp,\n remoteNodeId,\n auth,\n );\n }\n\n /**\n * Clear all data.\n */\n clear(): void {\n this.map.clear();\n }\n\n /**\n * Get map size.\n */\n get size(): number {\n return this.map.size;\n }\n\n /**\n * Iterate over entries.\n */\n entries(): IterableIterator<[K, V]> {\n return this.map.entries();\n }\n\n /**\n * Get all keys.\n */\n allKeys(): IterableIterator<K> {\n return this.map.allKeys();\n }\n\n /**\n * Subscribe to changes.\n */\n onChange(callback: () => void): () => void {\n return this.map.onChange(callback);\n }\n\n /**\n * Get MerkleTree for sync.\n */\n getMerkleTree() {\n return this.map.getMerkleTree();\n }\n\n /**\n * Prune old tombstones.\n */\n prune(olderThan: Timestamp): K[] {\n return this.map.prune(olderThan);\n }\n}\n","/**\n * IndexConfig Types\n *\n * Configuration types for server-side index management.\n * Used to define indexes per map at server startup.\n *\n * @module config/IndexConfig\n */\n\n/**\n * Definition of a single index on a map.\n */\nexport interface IndexDefinition {\n /** Attribute name (supports dot notation for nested attributes, e.g., \"user.email\") */\n attribute: string;\n\n /** Index type */\n type: 'hash' | 'navigable';\n\n /**\n * Comparator type for navigable indexes.\n * Defaults to natural ordering (string/number).\n */\n comparator?: 'number' | 'string' | 'date';\n}\n\n/**\n * Index configuration for a specific map.\n */\nexport interface MapIndexConfig {\n /** Map name */\n mapName: string;\n\n /** Indexes to create on this map */\n indexes: IndexDefinition[];\n}\n\n/**\n * Server-wide index configuration.\n */\nexport interface ServerIndexConfig {\n /**\n * Auto-create indexes based on query patterns.\n * When enabled, the server will analyze query patterns and suggest/create indexes.\n * Default: false\n */\n autoIndex?: boolean;\n\n /**\n * Maximum number of auto-created indexes per map.\n * Prevents unbounded memory growth from auto-indexing.\n * Default: 10\n */\n maxAutoIndexesPerMap?: number;\n\n /**\n * Pre-configured indexes per map.\n * These indexes are created at map initialization.\n */\n maps?: MapIndexConfig[];\n\n /**\n * Whether to log index usage statistics.\n * Default: false\n */\n logStats?: boolean;\n\n /**\n * Interval in milliseconds for logging index statistics.\n * Only used if logStats is true.\n * Default: 60000 (1 minute)\n */\n statsLogInterval?: number;\n}\n\n/**\n * Default index configuration.\n */\nexport const DEFAULT_INDEX_CONFIG: ServerIndexConfig = {\n autoIndex: false,\n maxAutoIndexesPerMap: 10,\n maps: [],\n logStats: false,\n statsLogInterval: 60000,\n};\n\n/**\n * Validate a ServerIndexConfig object.\n *\n * @param config - Config to validate\n * @returns Array of validation errors (empty if valid)\n */\nexport function validateIndexConfig(config: ServerIndexConfig): string[] {\n const errors: string[] = [];\n\n if (config.maxAutoIndexesPerMap !== undefined) {\n if (\n typeof config.maxAutoIndexesPerMap !== 'number' ||\n config.maxAutoIndexesPerMap < 1\n ) {\n errors.push('maxAutoIndexesPerMap must be a positive number');\n }\n }\n\n if (config.statsLogInterval !== undefined) {\n if (\n typeof config.statsLogInterval !== 'number' ||\n config.statsLogInterval < 1000\n ) {\n errors.push('statsLogInterval must be at least 1000ms');\n }\n }\n\n if (config.maps) {\n const mapNames = new Set<string>();\n\n for (const mapConfig of config.maps) {\n if (!mapConfig.mapName || typeof mapConfig.mapName !== 'string') {\n errors.push('Each map config must have a valid mapName');\n continue;\n }\n\n if (mapNames.has(mapConfig.mapName)) {\n errors.push(`Duplicate map config for: ${mapConfig.mapName}`);\n }\n mapNames.add(mapConfig.mapName);\n\n if (!Array.isArray(mapConfig.indexes)) {\n errors.push(`Map ${mapConfig.mapName}: indexes must be an array`);\n continue;\n }\n\n const attrNames = new Set<string>();\n for (const indexDef of mapConfig.indexes) {\n if (!indexDef.attribute || typeof indexDef.attribute !== 'string') {\n errors.push(`Map ${mapConfig.mapName}: index must have valid attribute`);\n continue;\n }\n\n if (!['hash', 'navigable'].includes(indexDef.type)) {\n errors.push(\n `Map ${mapConfig.mapName}: index type must be 'hash' or 'navigable'`\n );\n }\n\n if (\n indexDef.comparator &&\n !['number', 'string', 'date'].includes(indexDef.comparator)\n ) {\n errors.push(\n `Map ${mapConfig.mapName}: comparator must be 'number', 'string', or 'date'`\n );\n }\n\n // Warn about duplicate attribute indexes (same type)\n const key = `${indexDef.attribute}:${indexDef.type}`;\n if (attrNames.has(key)) {\n errors.push(\n `Map ${mapConfig.mapName}: duplicate ${indexDef.type} index on ${indexDef.attribute}`\n );\n }\n attrNames.add(key);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Merge user config with defaults.\n *\n * @param userConfig - User-provided config\n * @returns Merged config with defaults\n */\nexport function mergeWithDefaults(\n userConfig: Partial<ServerIndexConfig>\n): ServerIndexConfig {\n return {\n ...DEFAULT_INDEX_CONFIG,\n ...userConfig,\n maps: userConfig.maps ?? DEFAULT_INDEX_CONFIG.maps,\n };\n}\n","/**\n * MapFactory Implementation\n *\n * Factory for creating LWWMap or IndexedLWWMap based on configuration.\n * Used by ServerCoordinator to create maps with proper indexes.\n *\n * @module config/MapFactory\n */\n\nimport {\n HLC,\n LWWMap,\n ORMap,\n IndexedLWWMap,\n IndexedORMap,\n simpleAttribute,\n type Attribute,\n} from '@topgunbuild/core';\nimport type {\n ServerIndexConfig,\n MapIndexConfig,\n IndexDefinition,\n} from './IndexConfig';\nimport { mergeWithDefaults } from './IndexConfig';\n\n/**\n * Factory for creating indexed or regular CRDT maps.\n */\nexport class MapFactory {\n private readonly config: ServerIndexConfig;\n private readonly mapConfigs: Map<string, MapIndexConfig>;\n\n /**\n * Create a MapFactory.\n *\n * @param config - Server index configuration\n */\n constructor(config?: Partial<ServerIndexConfig>) {\n this.config = mergeWithDefaults(config ?? {});\n\n // Build lookup map for quick access\n this.mapConfigs = new Map();\n for (const mapConfig of this.config.maps ?? []) {\n this.mapConfigs.set(mapConfig.mapName, mapConfig);\n }\n }\n\n /**\n * Create an LWWMap or IndexedLWWMap based on configuration.\n *\n * @param mapName - Name of the map\n * @param hlc - Hybrid Logical Clock instance\n * @returns LWWMap or IndexedLWWMap depending on configuration\n */\n createLWWMap<V>(mapName: string, hlc: HLC): LWWMap<string, V> | IndexedLWWMap<string, V> {\n const mapConfig = this.mapConfigs.get(mapName);\n\n // No indexes configured - return regular LWWMap\n if (!mapConfig || mapConfig.indexes.length === 0) {\n return new LWWMap<string, V>(hlc);\n }\n\n // Create IndexedLWWMap with configured indexes\n const map = new IndexedLWWMap<string, V>(hlc);\n\n for (const indexDef of mapConfig.indexes) {\n this.addIndexToLWWMap(map, indexDef);\n }\n\n return map;\n }\n\n /**\n * Create an ORMap or IndexedORMap based on configuration.\n *\n * @param mapName - Name of the map\n * @param hlc - Hybrid Logical Clock instance\n * @returns ORMap or IndexedORMap depending on configuration\n */\n createORMap<V>(mapName: string, hlc: HLC): ORMap<string, V> | IndexedORMap<string, V> {\n const mapConfig = this.mapConfigs.get(mapName);\n\n // No indexes configured - return regular ORMap\n if (!mapConfig || mapConfig.indexes.length === 0) {\n return new ORMap<string, V>(hlc);\n }\n\n // Create IndexedORMap with configured indexes\n const map = new IndexedORMap<string, V>(hlc);\n\n for (const indexDef of mapConfig.indexes) {\n this.addIndexToORMap(map, indexDef);\n }\n\n return map;\n }\n\n /**\n * Add an index to an IndexedLWWMap based on definition.\n */\n private addIndexToLWWMap<V>(\n map: IndexedLWWMap<string, V>,\n indexDef: IndexDefinition\n ): void {\n const attribute = this.createAttribute<V>(indexDef.attribute);\n\n if (indexDef.type === 'hash') {\n map.addHashIndex(attribute);\n } else if (indexDef.type === 'navigable') {\n // For navigable indexes, we need to ensure type safety\n const navAttribute = attribute as Attribute<V, string | number>;\n const comparator = this.createComparator(indexDef.comparator);\n map.addNavigableIndex(navAttribute, comparator);\n }\n }\n\n /**\n * Add an index to an IndexedORMap based on definition.\n */\n private addIndexToORMap<V>(\n map: IndexedORMap<string, V>,\n indexDef: IndexDefinition\n ): void {\n const attribute = this.createAttribute<V>(indexDef.attribute);\n\n if (indexDef.type === 'hash') {\n map.addHashIndex(attribute);\n } else if (indexDef.type === 'navigable') {\n const navAttribute = attribute as Attribute<V, string | number>;\n const comparator = this.createComparator(indexDef.comparator);\n map.addNavigableIndex(navAttribute, comparator);\n }\n }\n\n /**\n * Create an Attribute for extracting values from records.\n * Supports dot notation for nested paths.\n */\n private createAttribute<V>(path: string): Attribute<V, unknown> {\n return simpleAttribute(path, (record: V) => {\n return this.getNestedValue(record, path);\n });\n }\n\n /**\n * Get a nested value from an object using dot notation.\n *\n * @param obj - Object to extract value from\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @returns Value at the path or undefined\n */\n private getNestedValue(obj: unknown, path: string): unknown {\n if (obj === null || obj === undefined) {\n return undefined;\n }\n\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === undefined || current === null) {\n return undefined;\n }\n if (typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n }\n\n /**\n * Create a comparator function for navigable indexes.\n */\n private createComparator(\n type?: 'number' | 'string' | 'date'\n ): ((a: string | number, b: string | number) => number) | undefined {\n switch (type) {\n case 'number':\n return (a, b) => {\n const numA = typeof a === 'number' ? a : parseFloat(String(a));\n const numB = typeof b === 'number' ? b : parseFloat(String(b));\n return numA - numB;\n };\n\n case 'date':\n return (a, b) => {\n const dateA = new Date(a as string | number).getTime();\n const dateB = new Date(b as string | number).getTime();\n return dateA - dateB;\n };\n\n case 'string':\n return (a, b) => {\n const strA = String(a);\n const strB = String(b);\n return strA.localeCompare(strB);\n };\n\n default:\n // Use default comparator (natural ordering)\n return undefined;\n }\n }\n\n /**\n * Check if a map should be indexed based on configuration.\n *\n * @param mapName - Name of the map\n * @returns true if map has index configuration\n */\n hasIndexConfig(mapName: string): boolean {\n const config = this.mapConfigs.get(mapName);\n return config !== undefined && config.indexes.length > 0;\n }\n\n /**\n * Get index configuration for a map.\n *\n * @param mapName - Name of the map\n * @returns Map index config or undefined\n */\n getMapConfig(mapName: string): MapIndexConfig | undefined {\n return this.mapConfigs.get(mapName);\n }\n\n /**\n * Get all configured map names.\n *\n * @returns Array of map names with index configuration\n */\n getConfiguredMaps(): string[] {\n return Array.from(this.mapConfigs.keys());\n }\n\n /**\n * Get the full server index configuration.\n */\n getConfig(): ServerIndexConfig {\n return this.config;\n }\n}\n"],"mappings":"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","names":["import_fs","import_ws","import_core","import_core","pino","import_events","import_events","import_events","import_core","import_core","import_events","import_events","promises","import_ws","import_core","DEFAULT_CONFIG","DEFAULT_MAX_SIZE","DEFAULT_MAX_SIZE","DEFAULT_MAX_SIZE","DEFAULT_CONFIG","import_path","import_core","coreHashString","h","WORKER_THRESHOLD","taskIdCounter","generateTaskId","import_path","import_core","taskIdCounter","generateTaskId","coreSerialize","coreDeserialize","DEFAULT_CONFIG","DEFAULT_CONFIG","import_events","import_core","import_events","import_core","import_core","import_core","import_core","import_core","import_core","createHttpsServer","createHttpServer","TABLE_NAME_REGEX","validateTableName","import_core","import_events","import_core","import_core","record","import_core"]}