@@ -342,48 +342,68 @@ async def session_id(self) -> str:
342342 return self .build_session_id (self .current_session )
343343
344344 async def connect (self ):
345- old_session : Optional [ClientWebSocketResponse ] = None if self .current_session is None else self .current_session
346- if self .wss_uri is None :
347- # If the underlying WSS URL does not exist,
348- # acquiring a new active WSS URL from the server-side first
349- self .wss_uri = await self .issue_new_wss_url ()
350-
351- self .current_session = await self .aiohttp_client_session .ws_connect (
352- self .wss_uri ,
353- autoping = False ,
354- heartbeat = self .ping_interval ,
355- proxy = self .proxy ,
356- ssl = self .web_client .ssl ,
357- )
358- session_id : str = await self .session_id ()
359- self .auto_reconnect_enabled = self .default_auto_reconnect_enabled
360- self .stale = False
361- self .logger .info (f"A new session ({ session_id } ) has been established" )
362-
363- # The first ping from the new connection
364- if self .logger .level <= logging .DEBUG :
365- self .logger .debug (f"Sending a ping message with the newly established connection ({ session_id } )..." )
366- t = time .time ()
367- await self .current_session .ping (f"sdk-ping-pong:{ t } " )
368-
369- if self .current_session_monitor is not None :
370- self .current_session_monitor .cancel ()
371-
372- self .current_session_monitor = asyncio .ensure_future (self .monitor_current_session ())
373- if self .logger .level <= logging .DEBUG :
374- self .logger .debug (f"A new monitor_current_session() executor has been recreated for { session_id } " )
375-
376- if self .message_receiver is not None :
377- self .message_receiver .cancel ()
378-
379- self .message_receiver = asyncio .ensure_future (self .receive_messages ())
380- if self .logger .level <= logging .DEBUG :
381- self .logger .debug (f"A new receive_messages() executor has been recreated for { session_id } " )
345+ # This loop is used to ensure when a new session is created,
346+ # a new monitor and a new message receiver are also created.
347+ # If a new session is created but we failed to create the new
348+ # monitor or the new message, we should try it.
349+ while True :
350+ try :
351+ old_session : Optional [ClientWebSocketResponse ] = (
352+ None if self .current_session is None else self .current_session
353+ )
382354
383- if old_session is not None :
384- await old_session .close ()
385- old_session_id = self .build_session_id (old_session )
386- self .logger .info (f"The old session ({ old_session_id } ) has been abandoned" )
355+ # If the old session is broken (e.g. reset by peer), it might fail to close it.
356+ # We don't want to retry when this kind of cases happen.
357+ try :
358+ # We should close old session before create a new one. Because when disconnect
359+ # reason is `too_many_websockets`, we need to close the old one first to
360+ # to decrease the number of connections.
361+ self .auto_reconnect_enabled = False
362+ if old_session is not None :
363+ await old_session .close ()
364+ old_session_id = self .build_session_id (old_session )
365+ self .logger .info (f"The old session ({ old_session_id } ) has been abandoned" )
366+ except Exception as e :
367+ self .logger .exception (f"Failed to close the old session : { e } " )
368+
369+ if self .wss_uri is None :
370+ # If the underlying WSS URL does not exist,
371+ # acquiring a new active WSS URL from the server-side first
372+ self .wss_uri = await self .issue_new_wss_url ()
373+
374+ self .current_session = await self .aiohttp_client_session .ws_connect (
375+ self .wss_uri ,
376+ autoping = False ,
377+ heartbeat = self .ping_interval ,
378+ proxy = self .proxy ,
379+ ssl = self .web_client .ssl ,
380+ )
381+ session_id : str = await self .session_id ()
382+ self .auto_reconnect_enabled = self .default_auto_reconnect_enabled
383+ self .stale = False
384+ self .logger .info (f"A new session ({ session_id } ) has been established" )
385+
386+ # The first ping from the new connection
387+ if self .logger .level <= logging .DEBUG :
388+ self .logger .debug (f"Sending a ping message with the newly established connection ({ session_id } )..." )
389+ t = time .time ()
390+ await self .current_session .ping (f"sdk-ping-pong:{ t } " )
391+
392+ if self .current_session_monitor is not None :
393+ self .current_session_monitor .cancel ()
394+ self .current_session_monitor = asyncio .ensure_future (self .monitor_current_session ())
395+ if self .logger .level <= logging .DEBUG :
396+ self .logger .debug (f"A new monitor_current_session() executor has been recreated for { session_id } " )
397+
398+ if self .message_receiver is not None :
399+ self .message_receiver .cancel ()
400+ self .message_receiver = asyncio .ensure_future (self .receive_messages ())
401+ if self .logger .level <= logging .DEBUG :
402+ self .logger .debug (f"A new receive_messages() executor has been recreated for { session_id } " )
403+ break
404+ except Exception as e :
405+ self .logger .exception (f"Failed to connect (error: { e } ); Retrying..." )
406+ await asyncio .sleep (self .ping_interval )
387407
388408 async def disconnect (self ):
389409 if self .current_session is not None :
0 commit comments