@@ -42,31 +42,37 @@ async def register_connection(
4242 user_id : str | None ,
4343 ) -> str :
4444 connection_id = uuid .uuid4 ().hex
45- redis = await get_redis ( )
45+ setattr ( websocket . state , "realtime_connection_id" , connection_id )
4646 payload = {
4747 "agent_id" : agent_id ,
4848 "session_id" : session_id or "" ,
4949 "user_id" : user_id or "" ,
5050 "instance_id" : self .instance_id ,
5151 }
52- async with redis .pipeline (transaction = True ) as pipe :
53- pipe .sadd (self ._agent_index_key (agent_id ), connection_id )
54- pipe .hset (self ._connection_key (connection_id ), mapping = payload )
55- pipe .expire (self ._connection_key (connection_id ), PRESENCE_TTL_SECONDS )
56- pipe .expire (self ._agent_index_key (agent_id ), PRESENCE_TTL_SECONDS )
57- await pipe .execute ()
58- setattr (websocket .state , "realtime_connection_id" , connection_id )
52+ try :
53+ redis = await get_redis ()
54+ async with redis .pipeline (transaction = True ) as pipe :
55+ pipe .sadd (self ._agent_index_key (agent_id ), connection_id )
56+ pipe .hset (self ._connection_key (connection_id ), mapping = payload )
57+ pipe .expire (self ._connection_key (connection_id ), PRESENCE_TTL_SECONDS )
58+ pipe .expire (self ._agent_index_key (agent_id ), PRESENCE_TTL_SECONDS )
59+ await pipe .execute ()
60+ except Exception as exc :
61+ logger .warning (f"[Realtime] Redis presence unavailable; using local websocket only: { exc } " )
5962 return connection_id
6063
6164 async def unregister_connection (self , * , agent_id : str , websocket : WebSocket ) -> None :
6265 connection_id = getattr (websocket .state , "realtime_connection_id" , None )
6366 if not connection_id :
6467 return
65- redis = await get_redis ()
66- async with redis .pipeline (transaction = True ) as pipe :
67- pipe .srem (self ._agent_index_key (agent_id ), connection_id )
68- pipe .delete (self ._connection_key (connection_id ))
69- await pipe .execute ()
68+ try :
69+ redis = await get_redis ()
70+ async with redis .pipeline (transaction = True ) as pipe :
71+ pipe .srem (self ._agent_index_key (agent_id ), connection_id )
72+ pipe .delete (self ._connection_key (connection_id ))
73+ await pipe .execute ()
74+ except Exception as exc :
75+ logger .warning (f"[Realtime] Redis presence cleanup failed: { exc } " )
7076
7177 async def is_user_viewing_session (self , * , agent_id : str , session_id : str , user_id : str ) -> bool :
7278 for record in await self ._list_presence (agent_id ):
@@ -118,21 +124,25 @@ async def route_message(
118124 if not remote_targets :
119125 return
120126
121- redis = await get_redis ()
122- envelope = json .dumps (
123- {
124- "message" : message ,
125- "agent_id" : agent_id ,
126- "session_id" : session_id ,
127- "user_id" : user_id ,
128- "origin_instance_id" : self .instance_id ,
129- }
130- )
131- publish_tasks = [
132- redis .publish (f"{ PUBSUB_PREFIX } :instance:{ instance_id } " , envelope )
133- for instance_id in remote_targets
134- ]
135- await asyncio .gather (* publish_tasks , return_exceptions = True )
127+ try :
128+ redis = await get_redis ()
129+ envelope = json .dumps (
130+ {
131+ "message" : message ,
132+ "agent_id" : agent_id ,
133+ "session_id" : session_id ,
134+ "user_id" : user_id ,
135+ "origin_instance_id" : self .instance_id ,
136+ }
137+ )
138+ publish_tasks = [
139+ redis .publish (f"{ PUBSUB_PREFIX } :instance:{ instance_id } " , envelope )
140+ for instance_id in remote_targets
141+ ]
142+ await asyncio .gather (* publish_tasks , return_exceptions = True )
143+ except Exception as exc :
144+ logger .warning (f"[Realtime] Redis pubsub unavailable; skipped remote websocket routing: { exc } " )
145+ return
136146 logger .debug (
137147 f"[Realtime] Routed agent={ agent_id } local={ local_sent } remote_instances={ list (remote_targets .keys ())} "
138148 )
@@ -154,9 +164,13 @@ async def stop(self) -> None:
154164 self ._started = False
155165
156166 async def _subscriber_loop (self , deliver_local ) -> None :
157- redis = await get_redis ()
158- pubsub = redis .pubsub ()
159- await pubsub .subscribe (self ._instance_channel ())
167+ try :
168+ redis = await get_redis ()
169+ pubsub = redis .pubsub ()
170+ await pubsub .subscribe (self ._instance_channel ())
171+ except Exception as exc :
172+ logger .warning (f"[Realtime] Redis pubsub subscriber unavailable; remote websocket routing disabled: { exc } " )
173+ return
160174 try :
161175 while True :
162176 message = await pubsub .get_message (ignore_subscribe_messages = True , timeout = 1.0 )
@@ -180,21 +194,25 @@ async def _subscriber_loop(self, deliver_local) -> None:
180194 await pubsub .aclose ()
181195
182196 async def _list_presence (self , agent_id : str ) -> list [dict [str , str ]]:
183- redis = await get_redis ()
184- connection_ids = await redis .smembers (self ._agent_index_key (agent_id ))
185- if not connection_ids :
197+ try :
198+ redis = await get_redis ()
199+ connection_ids = await redis .smembers (self ._agent_index_key (agent_id ))
200+ if not connection_ids :
201+ return []
202+ records : list [dict [str , str ]] = []
203+ stale_ids : list [str ] = []
204+ for connection_id in connection_ids :
205+ data = await redis .hgetall (self ._connection_key (connection_id ))
206+ if not data :
207+ stale_ids .append (connection_id )
208+ continue
209+ records .append (data )
210+ if stale_ids :
211+ await redis .srem (self ._agent_index_key (agent_id ), * stale_ids )
212+ return records
213+ except Exception as exc :
214+ logger .warning (f"[Realtime] Redis presence lookup failed: { exc } " )
186215 return []
187- records : list [dict [str , str ]] = []
188- stale_ids : list [str ] = []
189- for connection_id in connection_ids :
190- data = await redis .hgetall (self ._connection_key (connection_id ))
191- if not data :
192- stale_ids .append (connection_id )
193- continue
194- records .append (data )
195- if stale_ids :
196- await redis .srem (self ._agent_index_key (agent_id ), * stale_ids )
197- return records
198216
199217
200218realtime_router = RealtimeRouter ()
0 commit comments