| 101 | * Uses Lua scripts for atomic operations to prevent race conditions. |
| 102 | */ |
| 103 | export class RedisRoomManager implements IRoomManager { |
| 104 | private redis: RedisClientType |
| 105 | private _io: Server |
| 106 | private isConnected = false |
| 107 | private removeUserScriptSha: string | null = null |
| 108 | private updateActivityScriptSha: string | null = null |
| 109 | |
| 110 | constructor(io: Server, redisUrl: string) { |
| 111 | this._io = io |
| 112 | this.redis = createClient({ |
| 113 | url: redisUrl, |
| 114 | }) |
| 115 | |
| 116 | this.redis.on('error', (err) => { |
| 117 | logger.error('Redis client error:', err) |
| 118 | }) |
| 119 | |
| 120 | this.redis.on('reconnecting', () => { |
| 121 | logger.warn('Redis client reconnecting...') |
| 122 | this.isConnected = false |
| 123 | }) |
| 124 | |
| 125 | this.redis.on('ready', () => { |
| 126 | logger.info('Redis client ready') |
| 127 | this.isConnected = true |
| 128 | }) |
| 129 | |
| 130 | this.redis.on('end', () => { |
| 131 | logger.warn('Redis client connection closed') |
| 132 | this.isConnected = false |
| 133 | }) |
| 134 | } |
| 135 | |
| 136 | get io(): Server { |
| 137 | return this._io |
| 138 | } |
| 139 | |
| 140 | isReady(): boolean { |
| 141 | return this.isConnected |
| 142 | } |
| 143 | |
| 144 | async initialize(): Promise<void> { |
| 145 | if (this.isConnected) return |
| 146 | |
| 147 | try { |
| 148 | await this.redis.connect() |
| 149 | this.isConnected = true |
| 150 | |
| 151 | // Pre-load Lua scripts for better performance |
| 152 | this.removeUserScriptSha = await this.redis.scriptLoad(REMOVE_USER_SCRIPT) |
| 153 | this.updateActivityScriptSha = await this.redis.scriptLoad(UPDATE_ACTIVITY_SCRIPT) |
| 154 | |
| 155 | logger.info('RedisRoomManager connected to Redis and scripts loaded') |
| 156 | } catch (error) { |
| 157 | logger.error('Failed to connect to Redis:', error) |
| 158 | throw error |
| 159 | } |
| 160 | } |
nothing calls this directly
no outgoing calls
no test coverage detected