UNPKG

@rivetkit/redis

Version:

_Lightweight Libraries for Backends_

1 lines 23.7 kB
{"version":3,"sources":["/Users/nathan/rivetkit/packages/drivers/redis/dist/chunk-MYPT6L3T.cjs","../src/actor.ts","../src/coordinate/actor-peer.ts","../src/coordinate/log.ts"],"names":["logger"],"mappings":"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","file":"/Users/nathan/rivetkit/packages/drivers/redis/dist/chunk-MYPT6L3T.cjs","sourcesContent":[null,"import type {\n\tGenericConnGlobalState,\n\tRegistryConfig,\n\tRunConfig,\n} from \"@rivetkit/core\";\nimport type {\n\tActorDriver,\n\tAnyActorInstance,\n\tManagerDriver,\n} from \"@rivetkit/core/driver-helpers\";\nimport invariant from \"invariant\";\nimport type Redis from \"ioredis\";\nimport type { RedisDriverConfig } from \"./config\";\nimport { ActorPeer } from \"./coordinate/actor-peer\";\nimport type { Node } from \"./coordinate/node/mod\";\nimport type { GlobalState } from \"./coordinate/types\";\nimport { KEYS } from \"./keys\";\nimport { logger } from \"./log\";\n\n// Define AnyClient locally since it's not exported\ntype AnyClient = any;\n\nexport interface DriverContext {\n\tredis: Redis;\n\tkeyPrefix: string;\n}\n\n/**\n * Redis implementation of the Actor Driver\n */\nexport class RedisActorDriver implements ActorDriver {\n\t#globalState: GlobalState;\n\t#redis: Redis;\n\t#driverConfig: RedisDriverConfig;\n\n\tconstructor(\n\t\tglobalState: GlobalState,\n\t\tredis: Redis,\n\t\tdriverConfig: RedisDriverConfig,\n\t) {\n\t\tthis.#globalState = globalState;\n\t\tthis.#redis = redis;\n\t\tthis.#driverConfig = driverConfig;\n\t}\n\n\tasync loadActor(actorId: string): Promise<AnyActorInstance> {\n\t\tconst actor = await ActorPeer.getLeaderActor(this.#globalState, actorId);\n\t\tinvariant(actor, `Actor ${actorId} is not the leader on this node`);\n\t\treturn actor;\n\t}\n\n\tgetGenericConnGlobalState(actorId: string): GenericConnGlobalState {\n\t\tconst peer = ActorPeer.getLeaderActorPeer(this.#globalState, actorId);\n\t\tinvariant(peer, `Actor ${actorId} is not the leader on this node`);\n\t\treturn peer.genericConnGlobalState;\n\t}\n\n\tgetContext(_actorId: string): DriverContext {\n\t\treturn { redis: this.#redis, keyPrefix: this.#driverConfig.keyPrefix };\n\t}\n\n\tasync readPersistedData(actorId: string): Promise<Uint8Array | undefined> {\n\t\tconst data = await this.#redis.getBuffer(\n\t\t\tKEYS.ACTOR.persistedData(this.#driverConfig.keyPrefix, actorId),\n\t\t);\n\t\tif (data !== null) return data;\n\t\treturn undefined;\n\t}\n\n\tasync writePersistedData(actorId: string, data: Uint8Array): Promise<void> {\n\t\tawait this.#redis.set(\n\t\t\tKEYS.ACTOR.persistedData(this.#driverConfig.keyPrefix, actorId),\n\t\t\tBuffer.from(data),\n\t\t);\n\t}\n\n\tasync setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {\n\t\tlogger().warn(\n\t\t\t\"redis driver currently does not support scheduling. alarms are currently implemented with setTimeout and will not survive sleeping.replaced with setTimeout.\",\n\t\t\t{ issue: \"https://github.com/rivet-gg/rivetkit/issues/1095\" },\n\t\t);\n\t\tconst delay = Math.max(timestamp - Date.now(), 0);\n\t\tsetTimeout(() => {\n\t\t\tactor.onAlarm();\n\t\t}, delay);\n\t}\n\n\tgetDatabase(actorId: string): Promise<unknown | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n}\n","import {\n\ttype ActorKey,\n\ttype AnyActorInstance,\n\tcreateGenericConnDrivers,\n\tGenericConnGlobalState,\n\ttype Registry,\n\ttype RegistryConfig,\n\ttype RunConfig,\n} from \"@rivetkit/core\";\nimport type { ActorDriver } from \"@rivetkit/core/driver-helpers\";\nimport type { CoordinateDriverConfig } from \"./config\";\nimport type { CoordinateDriver } from \"./driver\";\nimport { logger } from \"./log\";\nimport type { GlobalState } from \"./types\";\n\nexport class ActorPeer {\n\t#registryConfig: RegistryConfig;\n\t#runConfig: RunConfig;\n\t#driverConfig: CoordinateDriverConfig;\n\t#coordinateDriver: CoordinateDriver;\n\t#actorDriver: ActorDriver;\n\t#inlineClient: any; // Client type\n\t#globalState: GlobalState;\n\t#actorId: string;\n\t#actorName?: string;\n\t#actorKey?: ActorKey;\n\t#isDisposed = false;\n\n\t/** Connections that hold a reference to this actor. If this set is empty, the actor should be shut down. */\n\t#referenceConnections = new Set<string>();\n\n\t/** Node ID that's the leader for this actor. */\n\t#leaderNodeId?: string;\n\n\t/** Holds the insantiated actor class if is leader. */\n\t#loadedActor?: AnyActorInstance;\n\n\t/** Promise that resolves when the actor has fully started (only for leaders) */\n\t#loadedActorStartingPromise?: Promise<void>;\n\n\t#heartbeatTimeout?: NodeJS.Timeout;\n\n\t// TODO: Only create this when becomse leader\n\treadonly genericConnGlobalState = new GenericConnGlobalState();\n\n\tget #isLeader() {\n\t\treturn this.#leaderNodeId === this.#globalState.nodeId;\n\t}\n\n\tget isLeader() {\n\t\treturn this.#isLeader;\n\t}\n\n\tget leaderNodeId() {\n\t\tif (!this.#leaderNodeId) throw new Error(\"Not found leader node ID yet\");\n\t\treturn this.#leaderNodeId;\n\t}\n\n\tget loadedActor() {\n\t\treturn this.#loadedActor;\n\t}\n\n\tget loadedActorStartingPromise() {\n\t\treturn this.#loadedActorStartingPromise;\n\t}\n\n\tconstructor(\n\t\tregistryConfig: RegistryConfig,\n\t\trunConfig: RunConfig,\n\t\tdriverConfig: CoordinateDriverConfig,\n\t\tCoordinateDriver: CoordinateDriver,\n\t\tactorDriver: ActorDriver,\n\t\tinlineClient: any, // Client type\n\t\tglobalState: GlobalState,\n\t\tactorId: string,\n\t) {\n\t\tthis.#registryConfig = registryConfig;\n\t\tthis.#runConfig = runConfig;\n\t\tthis.#driverConfig = driverConfig;\n\t\tthis.#coordinateDriver = CoordinateDriver;\n\t\tthis.#actorDriver = actorDriver;\n\t\tthis.#inlineClient = inlineClient;\n\t\tthis.#globalState = globalState;\n\t\tthis.#actorId = actorId;\n\t}\n\n\t/** Acquires a `ActorPeer` for a connection and includes the connection ID in the references. */\n\tstatic async acquire(\n\t\tregistryConfig: RegistryConfig,\n\t\trunConfig: RunConfig,\n\t\tdriverConfig: CoordinateDriverConfig,\n\t\tactorDriver: ActorDriver,\n\t\tinlineClient: any, // Client type\n\t\tCoordinateDriver: CoordinateDriver,\n\t\tglobalState: GlobalState,\n\t\tactorId: string,\n\t\tconnId: string,\n\t): Promise<ActorPeer> {\n\t\tlet peer = globalState.actorPeers.get(actorId);\n\n\t\t// Create peer if needed\n\t\tif (!peer) {\n\t\t\tpeer = new ActorPeer(\n\t\t\t\tregistryConfig,\n\t\t\t\trunConfig,\n\t\t\t\tdriverConfig,\n\t\t\t\tCoordinateDriver,\n\t\t\t\tactorDriver,\n\t\t\t\tinlineClient,\n\t\t\t\tglobalState,\n\t\t\t\tactorId,\n\t\t\t);\n\t\t\tglobalState.actorPeers.set(actorId, peer);\n\t\t\tawait peer.#start();\n\t\t}\n\n\t\tpeer.#referenceConnections.add(connId);\n\n\t\tlogger().debug(\"added actor reference\", {\n\t\t\tactorId,\n\t\t\tconnId,\n\t\t\tnewReferenceCount: peer.#referenceConnections.size,\n\t\t});\n\n\t\treturn peer;\n\t}\n\n\tstatic getLeaderActorPeer(\n\t\tglobalState: GlobalState,\n\t\tactorId: string,\n\t): ActorPeer | undefined {\n\t\tconst peer = globalState.actorPeers.get(actorId);\n\t\tif (!peer) return undefined;\n\n\t\tif (peer.#isLeader) {\n\t\t\treturn peer;\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tstatic async getLeaderActor(\n\t\tglobalState: GlobalState,\n\t\tactorId: string,\n\t): Promise<AnyActorInstance | undefined> {\n\t\tconst peer = ActorPeer.getLeaderActorPeer(globalState, actorId);\n\t\tif (!peer) return undefined;\n\n\t\t// Wait for actor to be ready if it's still starting\n\t\tif (peer.loadedActorStartingPromise) {\n\t\t\tawait peer.loadedActorStartingPromise;\n\t\t}\n\n\t\tconst actor = peer.loadedActor;\n\t\tif (!actor)\n\t\t\tthrow new Error(\"Actor is leader, but loadedActor is undefined\");\n\t\treturn actor;\n\t}\n\n\tasync #start() {\n\t\t// TODO: Handle errors graecfully\n\t\t// TODO: See redlock\n\n\t\t// TODO: renew lease\n\t\t// TODO: receive messages for new connections\n\t\t// TODO: listen for health check on connections\n\t\t// TODO: Close sub on connection close\n\t\t// TODO: Use a global Redis connection\n\t\t// TODO: Add TTL to connections\n\t\t// TODO: Maybe use queue for leader instead of pubsub so the P2P is durable\n\t\t// TODO: Remove actor from globalState\n\n\t\t// TODO: Add back NX\n\t\t// Acquire lease\n\n\t\t// TODO: Do this in 1 round trip with a Lua script\n\n\t\t// Acquire initial information\n\t\tconst { actor } = await this.#coordinateDriver.startActorAndAcquireLease(\n\t\t\tthis.#actorId,\n\t\t\tthis.#globalState.nodeId,\n\t\t\tthis.#driverConfig.actorPeer.leaseDuration,\n\t\t);\n\t\t// Log\n\t\tlogger().debug(\"starting actor peer\", {\n\t\t\tactor,\n\t\t});\n\n\t\t// Validate actor exists\n\t\tif (!actor) {\n\t\t\tthrow new Error(\"Actor does not exist\");\n\t\t}\n\n\t\t// Parse tags\n\t\tthis.#actorName = actor.name;\n\t\tthis.#actorKey = actor.key;\n\n\t\t// Handle leadership\n\t\tthis.#leaderNodeId = actor.leaderNodeId;\n\t\tif (actor.leaderNodeId === this.#globalState.nodeId) {\n\t\t\tlogger().debug(\"actor peer is leader\", {\n\t\t\t\tactorId: this.#actorId,\n\t\t\t\tleaderNodeId: actor.leaderNodeId,\n\t\t\t});\n\n\t\t\tawait this.#convertToLeader();\n\t\t} else {\n\t\t\tlogger().debug(\"actor peer is follower\", {\n\t\t\t\tactorId: this.#actorId,\n\t\t\t\tleaderNodeId: actor.leaderNodeId,\n\t\t\t});\n\n\t\t\tthis.#leaderNodeId = actor.leaderNodeId;\n\t\t}\n\n\t\t// Schedule first heartbeat\n\t\tthis.#scheduleHeartbeat();\n\t}\n\n\tasync #heartbeat() {\n\t\tif (this.#isDisposed) return;\n\n\t\t// Execute heartbeat\n\t\tif (this.#isLeader) {\n\t\t\tawait this.#extendLease();\n\t\t} else {\n\t\t\tawait this.#attemptAcquireLease();\n\t\t}\n\n\t\tthis.#scheduleHeartbeat();\n\t}\n\n\t#scheduleHeartbeat() {\n\t\t// Schedule next heartbeat (leadership status may have changed)\n\t\tlet hbTimeout: number;\n\t\tif (this.#isLeader) {\n\t\t\thbTimeout =\n\t\t\t\tthis.#driverConfig.actorPeer.leaseDuration -\n\t\t\t\tthis.#driverConfig.actorPeer.renewLeaseGrace;\n\t\t} else {\n\t\t\t// TODO: Add jitter\n\t\t\thbTimeout =\n\t\t\t\tthis.#driverConfig.actorPeer.checkLeaseInterval +\n\t\t\t\tMath.random() * this.#driverConfig.actorPeer.checkLeaseJitter;\n\t\t}\n\t\tif (hbTimeout < 0)\n\t\t\tthrow new Error(\"Actor peer heartbeat timeout is negative, check config\");\n\t\tthis.#heartbeatTimeout = setTimeout(this.#heartbeat.bind(this), hbTimeout);\n\t}\n\n\tasync #convertToLeader() {\n\t\tif (!this.#actorName || !this.#actorKey)\n\t\t\tthrow new Error(\"missing name or key\");\n\n\t\tlogger().debug(\"peer acquired leadership\", { actorId: this.#actorId });\n\n\t\t// Build actor\n\t\tconst actorName = this.#actorName;\n\t\tconst definition = this.#registryConfig.use[actorName];\n\t\tif (!definition)\n\t\t\tthrow new Error(`no actor definition for name ${definition}`);\n\n\t\t// Create leader actor\n\t\tconst actor = definition.instantiate();\n\t\tthis.#loadedActor = actor;\n\n\t\t// Create promise to track actor startup\n\t\tthis.#loadedActorStartingPromise = (async () => {\n\t\t\t// Start actor\n\t\t\tconst connDrivers = createGenericConnDrivers(this.genericConnGlobalState);\n\t\t\tawait actor.start(\n\t\t\t\tconnDrivers,\n\t\t\t\tthis.#actorDriver,\n\t\t\t\tthis.#inlineClient,\n\t\t\t\tthis.#actorId,\n\t\t\t\tthis.#actorName!,\n\t\t\t\tthis.#actorKey!,\n\t\t\t\t\"unknown\",\n\t\t\t);\n\t\t})();\n\n\t\t// Wait for actor to start\n\t\tawait this.#loadedActorStartingPromise;\n\t}\n\n\t/**\n\t * Extends the lease if the current leader. Called on an interval for leaders leader.\n\t *\n\t * If the lease has expired for any reason (e.g. connection latency or database purged), this will automatically shut down the actor.\n\t */\n\tasync #extendLease() {\n\t\tconst { leaseValid } = await this.#coordinateDriver.extendLease(\n\t\t\tthis.#actorId,\n\t\t\tthis.#globalState.nodeId,\n\t\t\tthis.#driverConfig.actorPeer.leaseDuration,\n\t\t);\n\t\tif (leaseValid) {\n\t\t\tlogger().trace(\"lease is valid\", { actorId: this.#actorId });\n\t\t} else {\n\t\t\tlogger().debug(\"lease is invalid\", { actorId: this.#actorId });\n\n\t\t\t// Shut down. SInce the lease is already lost, no need to clear it.\n\t\t\tawait this.#dispose(false);\n\t\t}\n\t}\n\n\t/**\n\t * Attempts to acquire a lease (aka checks if the leader's lease has expired). Called on an interval for followers.\n\t */\n\tasync #attemptAcquireLease() {\n\t\tconst { newLeaderNodeId } =\n\t\t\tawait this.#coordinateDriver.attemptAcquireLease(\n\t\t\t\tthis.#actorId,\n\t\t\t\tthis.#globalState.nodeId,\n\t\t\t\tthis.#driverConfig.actorPeer.leaseDuration,\n\t\t\t);\n\n\t\t// Check if the lease was successfully acquired and promoted to leader\n\t\tconst isPromoted =\n\t\t\t!this.#isLeader && newLeaderNodeId === this.#globalState.nodeId;\n\n\t\t// Check if leader changed (and we're not the new leader)\n\t\tconst leaderChanged = this.#leaderNodeId !== newLeaderNodeId && !isPromoted;\n\n\t\t// Save leader\n\t\tthis.#leaderNodeId = newLeaderNodeId;\n\n\t\t// If leader changed, close all WebSockets for this actor\n\t\tif (leaderChanged) {\n\t\t\tthis.#closeAllWebSockets();\n\t\t}\n\n\t\t// Promote as leader if needed\n\t\tif (isPromoted) {\n\t\t\tif (!this.#isLeader) throw new Error(\"assert: should be promoted\");\n\n\t\t\tawait this.#convertToLeader();\n\t\t}\n\t}\n\n\tasync removeConnectionReference(connId: string) {\n\t\tconst removed = this.#referenceConnections.delete(connId);\n\n\t\tif (removed) {\n\t\t\tlogger().debug(\"removed actor reference\", {\n\t\t\t\tactorId: this.#actorId,\n\t\t\t\tconnId,\n\t\t\t\tnewReferenceCount: this.#referenceConnections.size,\n\t\t\t});\n\t\t} else {\n\t\t\tlogger().warn(\"removed reference to actor that didn't exist\", {\n\t\t\t\tactorId: this.#actorId,\n\t\t\t\tconnId,\n\t\t\t});\n\t\t}\n\n\t\tif (this.#referenceConnections.size === 0) {\n\t\t\tawait this.#dispose(true);\n\t\t}\n\t}\n\n\t#closeAllWebSockets() {\n\t\tlogger().info(\"closing all websockets due to leader change\", {\n\t\t\tactorId: this.#actorId,\n\t\t});\n\n\t\t// Close all relay WebSockets (used by openWebSocket)\n\t\tconst relayWebSockets = (this.#globalState as any).relayWebSockets as\n\t\t\t| Map<string, any>\n\t\t\t| undefined;\n\t\tif (relayWebSockets) {\n\t\t\tfor (const [wsId, ws] of relayWebSockets) {\n\t\t\t\t// Check if this WebSocket belongs to this actor\n\t\t\t\tif (ws.actorId === this.#actorId) {\n\t\t\t\t\tws._handleClose(1001, \"Actor leader changed\");\n\t\t\t\t\trelayWebSockets.delete(wsId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Close all follower WebSockets (used by proxyWebSocket)\n\t\tconst followerWebSockets = (this.#globalState as any).followerWebSockets as\n\t\t\t| Map<string, any>\n\t\t\t| undefined;\n\t\tif (followerWebSockets) {\n\t\t\tfor (const [wsId, wsData] of followerWebSockets) {\n\t\t\t\t// Check if this WebSocket belongs to this actor\n\t\t\t\tif (wsData.actorId === this.#actorId) {\n\t\t\t\t\twsData.ws.close(1001, \"Actor leader changed\");\n\t\t\t\t\tfollowerWebSockets.delete(wsId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Close all leader WebSockets (WebSockets we're handling as leader)\n\t\tconst leaderWebSockets = (this.#globalState as any).leaderWebSockets as\n\t\t\t| Map<string, any>\n\t\t\t| undefined;\n\t\tif (leaderWebSockets) {\n\t\t\tfor (const [wsId, wsData] of leaderWebSockets) {\n\t\t\t\tif (wsData.actorId === this.#actorId) {\n\t\t\t\t\t// Send close to follower\n\t\t\t\t\tif (wsData.wsContext && wsData.wsContext.close) {\n\t\t\t\t\t\twsData.wsContext.close(1001, \"Actor leader changed\");\n\t\t\t\t\t}\n\t\t\t\t\tleaderWebSockets.delete(wsId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync #dispose(releaseLease: boolean) {\n\t\tif (this.#isDisposed) return;\n\t\tthis.#isDisposed = true;\n\n\t\tlogger().info(\"actor shutting down\", { actorId: this.#actorId });\n\n\t\t// IMPORTANT: Do this before anything async\n\t\tclearTimeout(this.#heartbeatTimeout);\n\t\tthis.#globalState.actorPeers.delete(this.#actorId);\n\n\t\t// Stop actor\n\t\t//\n\t\t// We wait for this to finish to ensure that state is persisted safely to storage\n\t\tif (this.#isLeader && this.#loadedActor) {\n\t\t\tawait this.#loadedActor.stop();\n\t\t}\n\n\t\t// Clear the lease if needed\n\t\tif (this.#isLeader && releaseLease) {\n\t\t\tawait this.#coordinateDriver.releaseLease(\n\t\t\t\tthis.#actorId,\n\t\t\t\tthis.#globalState.nodeId,\n\t\t\t);\n\t\t}\n\n\t\tlogger().info(\"actor shutdown success\", { actorId: this.#actorId });\n\t}\n}\n","import { getLogger } from \"@rivetkit/core/log\";\n\nexport const logger = () => getLogger(\"redis-coordinate\");\n"]}