1
1
import asyncio
2
2
import inspect
3
+ import time
3
4
from collections .abc import AsyncGenerator , Awaitable , Callable , Coroutine
4
5
from contextlib import AsyncExitStack , asynccontextmanager
5
6
from dataclasses import dataclass , field
@@ -52,7 +53,8 @@ class Client:
52
53
_active_subscriptions : ActiveSubscriptions = field (default_factory = dict , init = False )
53
54
_active_transactions : set [Transaction ] = field (default_factory = set , init = False )
54
55
_exit_stack : AsyncExitStack = field (default_factory = AsyncExitStack , init = False )
55
- _heartbeat_task : asyncio .Task [None ] = field (init = False )
56
+ _send_heartbeat_task : asyncio .Task [None ] = field (init = False )
57
+ _check_server_heartbeat_task : asyncio .Task [None ] = field (init = False )
56
58
_listen_task : asyncio .Task [None ] = field (init = False )
57
59
_task_group : asyncio .TaskGroup = field (init = False )
58
60
_on_heartbeat_is_async : bool = field (init = False )
@@ -68,7 +70,7 @@ def __post_init__(self) -> None:
68
70
disconnect_confirmation_timeout = self .disconnect_confirmation_timeout ,
69
71
active_subscriptions = self ._active_subscriptions ,
70
72
active_transactions = self ._active_transactions ,
71
- set_heartbeat_interval = self ._restart_heartbeat_task ,
73
+ set_heartbeat_interval = self ._restart_heartbeat_tasks ,
72
74
),
73
75
connection_class = self .connection_class ,
74
76
connect_retry_attempts = self .connect_retry_attempts ,
@@ -83,7 +85,8 @@ def __post_init__(self) -> None:
83
85
84
86
async def __aenter__ (self ) -> Self :
85
87
self ._task_group = await self ._exit_stack .enter_async_context (asyncio .TaskGroup ())
86
- self ._heartbeat_task = self ._task_group .create_task (asyncio .sleep (0 ))
88
+ self ._send_heartbeat_task = self ._task_group .create_task (asyncio .sleep (0 ))
89
+ self ._check_server_heartbeat_task = self ._task_group .create_task (asyncio .sleep (0 ))
87
90
await self ._exit_stack .enter_async_context (self ._connection_manager )
88
91
self ._listen_task = self ._task_group .create_task (self ._listen_to_frames ())
89
92
return self
@@ -96,18 +99,42 @@ async def __aexit__(
96
99
await asyncio .Future ()
97
100
finally :
98
101
self ._listen_task .cancel ()
99
- self ._heartbeat_task .cancel ()
100
- await asyncio .wait ([self ._listen_task , self ._heartbeat_task ])
102
+ self ._send_heartbeat_task .cancel ()
103
+ self ._check_server_heartbeat_task .cancel ()
104
+ await asyncio .wait ([self ._listen_task , self ._send_heartbeat_task , self ._check_server_heartbeat_task ])
101
105
await self ._exit_stack .aclose ()
102
106
103
- def _restart_heartbeat_task (self , interval : float ) -> None :
104
- self ._heartbeat_task .cancel ()
105
- self ._heartbeat_task = self ._task_group .create_task (self ._send_heartbeats_forever (interval ))
107
+ def _restart_heartbeat_tasks (self , server_heartbeat : Heartbeat ) -> None :
108
+ self ._send_heartbeat_task .cancel ()
109
+ self ._check_server_heartbeat_task .cancel ()
110
+ self ._send_heartbeat_task = self ._task_group .create_task (
111
+ self ._send_heartbeats_forever (server_heartbeat .want_to_receive_interval_ms )
112
+ )
113
+ self ._check_server_heartbeat_task = self ._task_group .create_task (
114
+ self ._monitor_server_aliveness (server_heartbeat .will_send_interval_ms )
115
+ )
106
116
107
- async def _send_heartbeats_forever (self , interval : float ) -> None :
117
+ async def _send_heartbeats_forever (self , client_heartbeat_interval_ms : int ) -> None :
108
118
while True :
109
119
await self ._connection_manager .write_heartbeat_reconnecting ()
110
- await asyncio .sleep (interval )
120
+ await asyncio .sleep (client_heartbeat_interval_ms / 1000 )
121
+
122
+ async def _monitor_server_aliveness (self , server_heartbeat_interval_ms : int ) -> None :
123
+ server_heartbeat_interval_seconds = server_heartbeat_interval_ms / 1000
124
+ while True :
125
+ await asyncio .sleep (server_heartbeat_interval_seconds * self .check_server_alive_interval_factor )
126
+ last_read_time_ms = (
127
+ self ._connection_manager ._active_connection_state
128
+ and self ._connection_manager ._active_connection_state .connection .last_read_time_ms
129
+ )
130
+ if not last_read_time_ms :
131
+ print ("No last_Read_time_ms" )
132
+ continue
133
+ if (t := time .time () - last_read_time_ms ) > server_heartbeat_interval_seconds :
134
+ print ("reset time" , t )
135
+ self ._connection_manager ._active_connection_state = None
136
+ else :
137
+ print ("no reset" )
111
138
112
139
async def _listen_to_frames (self ) -> None :
113
140
async with asyncio .TaskGroup () as task_group :
0 commit comments