@topgunbuild/server 0.2.0-alpha → 0.2.1

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/PartitionService.ts","../src/cluster/LockManager.ts","../src/security/SecurityManager.ts","../src/monitoring/MetricsService.ts","../src/system/SystemManager.ts","../src/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts"],"sourcesContent":["export * from './ServerCoordinator';\nexport * from './storage';\nexport * from './security/SecurityManager';\nexport * from './utils/logger';\nexport * from './interceptor/IInterceptor';\nexport { TimestampInterceptor } from './interceptor/TimestampInterceptor';\nexport { RateLimitInterceptor } from './interceptor/RateLimitInterceptor';\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 { WebSocketServer, WebSocket } from 'ws';\nimport { HLC, LWWMap, ORMap, MerkleTree, serialize, deserialize, PermissionPolicy, Principal, PermissionType, Timestamp, LWWRecord, ORMapRecord, MessageSchema } 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';\n\ninterface ClientConnection {\n id: string;\n socket: WebSocket;\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}\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 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 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 // 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 this.wss = new WebSocketServer({ server: this.httpServer });\n this.wss.on('connection', (ws) => this.handleConnection(ws));\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 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.socket.send(serialize(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 /** 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 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 client.socket.send(shutdownMsg);\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. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 4. 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 // 5. 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 logger.info('Server Coordinator shutdown complete.');\n }\n\n private async handleConnection(ws: WebSocket) {\n // Client ID is temporary until auth\n const clientId = crypto.randomUUID();\n logger.info({ clientId }, 'Client connected (pending auth)');\n\n const connection: ClientConnection = {\n id: clientId,\n socket: ws,\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 // 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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: 'Invalid message format', details: (parseResult.error as any).errors }\n }));\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 client.socket.send(serialize({ type: 'AUTH_ACK' }));\n return; // Stop processing this message\n } catch (e) {\n logger.error({ clientId: client.id, err: e }, 'Auth failed');\n client.socket.send(serialize({ type: 'AUTH_FAIL', error: 'Invalid token' }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${mapName}` }\n }));\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.socket.send(serialize({\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.socket.send(serialize({\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 logger.info({ clientId: client.id, count: ops.length }, 'Received batch');\n\n let lastProcessedId: string | null = null;\n let rejectedCount = 0;\n\n for (const op of ops) {\n // OpLogEntry has { mapName, key, record, ... }\n\n // Check Permission for each op\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\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 // processLocalOp expects { mapName, key, record, orRecord, orTag, opType }\n\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOp({\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 }, false, client.id);\n\n if (op.id) {\n lastProcessedId = op.id;\n }\n } catch (err) {\n rejectedCount++;\n logger.warn({ clientId: client.id, mapName: op.mapName, err }, 'Op rejected in batch');\n // We do NOT update lastProcessedId for failed op\n }\n } else {\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 // For forwarded ops, we optimistically assume success for batch ACK purposes?\n // Or we should only ACK what we processed locally?\n // Batch ACK usually implies \"accepted\". Forwarding = accepted for processing.\n if (op.id) {\n lastProcessedId = op.id;\n }\n }\n }\n\n if (lastProcessedId !== null) {\n client.socket.send(serialize({\n type: 'OP_ACK',\n payload: { lastId: lastProcessedId }\n }));\n }\n\n if (rejectedCount > 0) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Partial batch failure: ${rejectedCount} ops denied` }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }));\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.socket.send(serialize({\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 501, message: `Merkle Sync not supported for ORMap ${message.mapName}` }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\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.socket.send(serialize({\n type: 'SYNC_RESP_LEAF',\n payload: { mapName, path, records: diffRecords }\n }));\n } else {\n client.socket.send(serialize({\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.socket.send(serialize({\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 }));\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 503, message: `Lock owner ${owner} is unavailable` }\n }));\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\n return;\n }\n\n try {\n this.topicManager.subscribe(client.id, topic);\n } catch (e: any) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }));\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.socket.send(serialize({\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: `Map ${message.mapName} is not an ORMap` }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for ORMAP_SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }));\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\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.socket.send(serialize({\n type: 'ORMAP_SYNC_RESP_LEAF',\n payload: { mapName, path, entries }\n }));\n } else {\n // Not a leaf - send bucket hashes\n client.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\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 private broadcast(message: any, excludeClientId?: string) {\n const isServerEvent = message.type === 'SERVER_EVENT';\n\n if (isServerEvent) {\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1 && client.isAuthenticated && client.principal) {\n const payload = message.payload;\n const mapName = payload.mapName;\n\n // Shallow clone payload\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.socket.send(serialize({ ...message, payload: newPayload }));\n }\n }\n } else {\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.socket.send(msgData);\n }\n }\n }\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.socket.send(serialize({\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.socket.send(serialize({\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.socket.send(serialize({\n type: 'QUERY_RESP',\n payload: { queryId, results: filteredResults }\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.socket.send(serialize({\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 // 0. Prepare Context\n let context: OpContext = {\n clientId: originalSenderId || 'unknown',\n isAuthenticated: false, // We might need to fetch this if local\n fromCluster,\n originalSenderId\n };\n\n if (!fromCluster && originalSenderId) {\n const client = this.clients.get(originalSenderId);\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\n };\n }\n }\n\n // 1. Interceptors: onBeforeOp\n let currentOp: ServerOp | null = op;\n try {\n for (const interceptor of this.interceptors) {\n if (interceptor.onBeforeOp) {\n if (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; // Silent drop\n }\n }\n }\n }\n } catch (err: any) {\n // Find which interceptor failed? We don't know easily unless we tracked loop index.\n // But logging err is good enough.\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op');\n throw err; // Re-throw to caller\n }\n\n if (!currentOp) return; // Should be caught above but safe check\n\n op = currentOp;\n\n // Apply to server state (Owner or Forwarded)\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 // Common fields\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 // ORMap\n oldRecord = map.getRecords(op.key); // Logic for \"old record\" in ORMap is complex for query.\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\n // Prepare storage: full state of key\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n } else if (op.opType === 'OR_REMOVE') {\n map.applyTombstone(op.orTag);\n eventPayload.eventType = 'OR_REMOVE';\n eventPayload.orTag = op.orTag;\n\n // OR_REMOVE modifies the key's records implicitly (filters them out)\n // So we should update the key state too?\n // Yes, if we remove a tag, the getRecords(key) result changes.\n // But we don't know which key held the tag easily unless we search or client provided it.\n // Client provided `op.key`.\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n\n // Also persist tombstones\n tombstonesToStore = {\n type: 'OR_TOMBSTONES',\n tags: map.getTombstones()\n };\n }\n }\n\n // Live Query Evaluation (Local)\n this.queryRegistry.processChange(op.mapName, map, op.key, op.record || op.orRecord, oldRecord);\n\n const mapSize = (map instanceof ORMap) ? map.totalRecords : map.size;\n this.metricsService.setMapSize(op.mapName, mapSize);\n\n // Persist to storage (Only Owner persists)\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 // 1. Broadcast EVENT to other clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 2. Broadcast EVENT/REPLICATION to Cluster\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 // 4. Interceptors: onAfterOp\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 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 if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(serialize(pongMessage));\n }\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","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 * 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';\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}\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\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\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 // 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 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 this.emit('memberJoined', remoteNodeId);\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 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","import { ClusterManager } from './ClusterManager';\nimport { hashString } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport class PartitionService {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = 271;\n private readonly BACKUP_COUNT = 1; // Standard Hazelcast default\n\n constructor(cluster: ClusterManager) {\n this.cluster = cluster;\n this.cluster.on('memberJoined', () => this.rebalance());\n this.cluster.on('memberLeft', () => this.rebalance());\n \n // Initial rebalance\n this.rebalance();\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 private rebalance() {\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 }, 'Rebalancing partitions');\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 this.partitions.set(i, { owner, backups });\n }\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, 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 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\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 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 { 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"],"mappings":"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","names":["import_fs","import_ws","import_core","import_core","pino","import_core","import_events","createHttpsServer","createHttpServer"]}
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/PartitionService.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/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts","../src/utils/nativeStats.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","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 } 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';\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\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 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 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. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 5. 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 // ============ 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 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 * 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. Broadcast EVENT to other clients\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 5. Broadcast to cluster\n this.broadcastToCluster(eventPayload);\n\n // 6. 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. Collect event for batched broadcast (instead of immediate broadcast)\n batchedEvents.push(eventPayload);\n\n // 5. Broadcast to cluster\n this.broadcastToCluster(eventPayload);\n\n // 6. 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';\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}\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\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\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 // 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 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 this.emit('memberJoined', remoteNodeId);\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 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","import { ClusterManager } from './ClusterManager';\nimport { hashString } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport class PartitionService {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = 271;\n private readonly BACKUP_COUNT = 1; // Standard Hazelcast default\n\n constructor(cluster: ClusterManager) {\n this.cluster = cluster;\n this.cluster.on('memberJoined', () => this.rebalance());\n this.cluster.on('memberLeft', () => this.rebalance());\n \n // Initial rebalance\n this.rebalance();\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 private rebalance() {\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 }, 'Rebalancing partitions');\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 this.partitions.set(i, { owner, backups });\n }\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 */\nexport const coalescingPresets = {\n /**\n * Conservative defaults - good for low-latency workloads.\n * Minimizes batching delay at the cost of more network calls.\n * Use for: gaming, real-time chat, interactive applications.\n */\n conservative: {\n maxBatchSize: 100,\n maxDelayMs: 5,\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: 8,\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 */\n highThroughput: {\n maxBatchSize: 500,\n maxDelayMs: 10,\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: 15,\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';\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 let hash = 0x811c9dc5;\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\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","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"],"mappings":"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","names":["import_fs","import_ws","import_core","import_core","pino","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","h","WORKER_THRESHOLD","taskIdCounter","generateTaskId","import_path","import_core","taskIdCounter","generateTaskId","coreSerialize","coreDeserialize","DEFAULT_CONFIG","DEFAULT_CONFIG","import_events","import_core","createHttpsServer","createHttpServer","import_core"]}