replit-river 0.1.16.dev3__tar.gz → 0.1.16.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/PKG-INFO +1 -1
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/pyproject.toml +1 -1
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/client_transport.py +42 -32
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/session.py +18 -7
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/transport.py +1 -2
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/transport_options.py +1 -1
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/LICENSE +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/README.md +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/__init__.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/client.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/client_session.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/__init__.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/__main__.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/client.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/run.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/schema.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/codegen/server.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/error_schema.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/message_buffer.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/messages.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/py.typed +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/rate_limiter.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/rpc.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/seq_manager.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/server.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/server_transport.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/task_manager.py +0 -0
- {replit_river-0.1.16.dev3 → replit_river-0.1.16.dev4}/replit_river/websocket_wrapper.py +0 -0
|
@@ -33,7 +33,6 @@ from replit_river.seq_manager import (
|
|
|
33
33
|
IgnoreMessageException,
|
|
34
34
|
InvalidMessageException,
|
|
35
35
|
)
|
|
36
|
-
from replit_river.session import Session
|
|
37
36
|
from replit_river.transport import Transport
|
|
38
37
|
from replit_river.transport_options import TransportOptions
|
|
39
38
|
|
|
@@ -57,29 +56,33 @@ class ClientTransport(Transport):
|
|
|
57
56
|
self._rate_limiter = LeakyBucketRateLimit(
|
|
58
57
|
transport_options.connection_retry_options
|
|
59
58
|
)
|
|
59
|
+
# We want to make sure there's only one session creation at a time
|
|
60
|
+
self._create_session_lock = asyncio.Lock()
|
|
60
61
|
|
|
61
62
|
async def close(self) -> None:
|
|
62
63
|
self._rate_limiter.close()
|
|
63
64
|
await self._close_all_sessions()
|
|
64
65
|
|
|
65
66
|
async def _get_existing_session(self) -> Optional[ClientSession]:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
async with self._session_lock:
|
|
68
|
+
if not self._sessions:
|
|
69
|
+
return None
|
|
70
|
+
if len(self._sessions) > 1:
|
|
71
|
+
raise RiverException(
|
|
72
|
+
"session_error",
|
|
73
|
+
"More than one session found in client, should only be one",
|
|
74
|
+
)
|
|
75
|
+
session = list(self._sessions.values())[0]
|
|
76
|
+
if isinstance(session, ClientSession):
|
|
77
|
+
return session
|
|
78
|
+
else:
|
|
79
|
+
raise RiverException(
|
|
80
|
+
"session_error", f"Client session type wrong, got {type(session)}"
|
|
81
|
+
)
|
|
80
82
|
|
|
81
83
|
async def _establish_new_connection(
|
|
82
84
|
self,
|
|
85
|
+
old_session: Optional[ClientSession] = None,
|
|
83
86
|
) -> Tuple[
|
|
84
87
|
WebSocketCommonProtocol,
|
|
85
88
|
ControlMessageHandshakeRequest,
|
|
@@ -92,19 +95,24 @@ class ClientTransport(Transport):
|
|
|
92
95
|
for i in range(max_retry):
|
|
93
96
|
if i > 0:
|
|
94
97
|
logging.info(f"Retrying build handshake number {i} times")
|
|
98
|
+
logging.info(
|
|
99
|
+
f"old_session: {old_session}, old_session.is_session_open(): {await old_session.is_session_open() if old_session else None}"
|
|
100
|
+
)
|
|
95
101
|
if not rate_limit.has_budget(client_id):
|
|
96
102
|
logging.debug("No retry budget for %s.", client_id)
|
|
97
103
|
break
|
|
98
104
|
try:
|
|
105
|
+
logging.error(
|
|
106
|
+
f"##### _establish_new_connection: old session : {old_session}"
|
|
107
|
+
)
|
|
99
108
|
ws = await websockets.connect(self._websocket_uri)
|
|
100
|
-
existing_session = await self._get_existing_session()
|
|
101
109
|
session_id = (
|
|
102
110
|
self.generate_session_id()
|
|
103
|
-
if not
|
|
104
|
-
else
|
|
111
|
+
if not old_session
|
|
112
|
+
else old_session.session_id
|
|
105
113
|
)
|
|
106
114
|
logging.error(
|
|
107
|
-
f"##### _establish_new_connection: existing session : {
|
|
115
|
+
f"##### _establish_new_connection: existing session : {old_session}"
|
|
108
116
|
)
|
|
109
117
|
rate_limit.consume_budget(client_id)
|
|
110
118
|
handshake_request, handshake_response = await self._establish_handshake(
|
|
@@ -120,7 +128,7 @@ class ClientTransport(Transport):
|
|
|
120
128
|
await asyncio.sleep(backoff_time / 1000)
|
|
121
129
|
raise RiverException(
|
|
122
130
|
ERROR_HANDSHAKE,
|
|
123
|
-
"Failed to create
|
|
131
|
+
"Failed to create ws after retrying max number of times",
|
|
124
132
|
)
|
|
125
133
|
|
|
126
134
|
async def _create_new_session(
|
|
@@ -151,36 +159,38 @@ class ClientTransport(Transport):
|
|
|
151
159
|
return new_session
|
|
152
160
|
|
|
153
161
|
async def _get_or_create_session(self) -> ClientSession:
|
|
154
|
-
|
|
162
|
+
logging.error(f"####### start get or create session")
|
|
163
|
+
async with self._create_session_lock:
|
|
155
164
|
existing_session = await self._get_existing_session()
|
|
156
165
|
if not existing_session:
|
|
157
|
-
logging.error(f"##### No existing session")
|
|
166
|
+
logging.error(f"##### _get_or_create_session No existing session")
|
|
158
167
|
return await self._create_new_session()
|
|
159
168
|
is_session_open = await existing_session.is_session_open()
|
|
160
169
|
if not is_session_open:
|
|
161
|
-
logging.error(
|
|
162
|
-
|
|
163
|
-
is_unexpected_close=False, acquire_transport_lock=False
|
|
170
|
+
logging.error(
|
|
171
|
+
f"##### _get_or_create_session session open, creating new session"
|
|
164
172
|
)
|
|
165
173
|
return await self._create_new_session()
|
|
166
|
-
is_ws_open = existing_session.is_websocket_open()
|
|
174
|
+
is_ws_open = await existing_session.is_websocket_open()
|
|
167
175
|
if is_ws_open:
|
|
168
|
-
logging.error(f"##### Reuse existing session")
|
|
176
|
+
logging.error(f"##### _get_or_create_session Reuse existing session")
|
|
169
177
|
return existing_session
|
|
170
178
|
else:
|
|
171
|
-
new_ws, _, hs_response = await self._establish_new_connection(
|
|
179
|
+
new_ws, _, hs_response = await self._establish_new_connection(
|
|
180
|
+
existing_session
|
|
181
|
+
)
|
|
172
182
|
if (
|
|
173
183
|
hs_response.status.sessionId
|
|
174
184
|
== existing_session.advertised_session_id
|
|
175
185
|
):
|
|
176
|
-
logging.error(
|
|
186
|
+
logging.error(
|
|
187
|
+
f"##### _get_or_create_session session open, replacing websocket"
|
|
188
|
+
)
|
|
177
189
|
await existing_session.replace_with_new_websocket(new_ws)
|
|
178
190
|
return existing_session
|
|
179
191
|
else:
|
|
180
192
|
logging.error(f"##### session open, not same session id, reuse")
|
|
181
|
-
await existing_session.close(
|
|
182
|
-
is_unexpected_close=False, acquire_transport_lock=False
|
|
183
|
-
)
|
|
193
|
+
await existing_session.close(is_unexpected_close=False)
|
|
184
194
|
return await self._create_new_session()
|
|
185
195
|
|
|
186
196
|
async def _send_handshake_request(
|
|
@@ -108,11 +108,9 @@ class Session(object):
|
|
|
108
108
|
"""Begin the countdown to close session, this should be called when
|
|
109
109
|
websocket is closed.
|
|
110
110
|
"""
|
|
111
|
-
logging.debug("begin_close_session_countdown")
|
|
112
111
|
if self._close_session_after_time_secs is not None:
|
|
113
112
|
# already in grace period, no need to set again
|
|
114
113
|
return
|
|
115
|
-
await self._ws_wrapper.close()
|
|
116
114
|
logging.debug(
|
|
117
115
|
"websocket closed from %s to %s begin grace period",
|
|
118
116
|
self._transport_id,
|
|
@@ -122,6 +120,7 @@ class Session(object):
|
|
|
122
120
|
self._close_session_after_time_secs = (
|
|
123
121
|
await self._get_current_time() + grace_period_ms / 1000
|
|
124
122
|
)
|
|
123
|
+
await self.close_websocket(self._ws_wrapper, should_retry=not self._is_server)
|
|
125
124
|
|
|
126
125
|
async def serve(self) -> None:
|
|
127
126
|
"""Serve messages from the websocket."""
|
|
@@ -164,8 +163,15 @@ class Session(object):
|
|
|
164
163
|
self._ws_wrapper.id,
|
|
165
164
|
)
|
|
166
165
|
try:
|
|
167
|
-
|
|
166
|
+
ws_wrapper = self._ws_wrapper
|
|
167
|
+
async for message in ws_wrapper.ws:
|
|
168
168
|
try:
|
|
169
|
+
logging.error(
|
|
170
|
+
f" await ws_wrapper.is_open(): : { await ws_wrapper.is_open()}"
|
|
171
|
+
)
|
|
172
|
+
if not await ws_wrapper.is_open():
|
|
173
|
+
# We should not process messages if the websocket is closed.
|
|
174
|
+
break
|
|
169
175
|
msg = parse_transport_msg(message, self._transport_options)
|
|
170
176
|
|
|
171
177
|
logging.debug(f"{self._transport_id} got a message %r", msg)
|
|
@@ -235,9 +241,13 @@ class Session(object):
|
|
|
235
241
|
await asyncio.sleep(
|
|
236
242
|
self._transport_options.close_session_check_interval_ms / 1000
|
|
237
243
|
)
|
|
244
|
+
logging.error("#### _check_to_close_session")
|
|
238
245
|
if not self._close_session_after_time_secs:
|
|
239
246
|
continue
|
|
240
247
|
current_time = await self._get_current_time()
|
|
248
|
+
logging.error(
|
|
249
|
+
f"#### _check_to_close_session : current_time: {current_time} self._close_session_after_time_secs: {self._close_session_after_time_secs}, {current_time > self._close_session_after_time_secs}"
|
|
250
|
+
)
|
|
241
251
|
if current_time > self._close_session_after_time_secs:
|
|
242
252
|
logging.debug(
|
|
243
253
|
"Grace period ended for %s, closing session", self._transport_id
|
|
@@ -282,14 +292,14 @@ class Session(object):
|
|
|
282
292
|
self._heartbeat_misses
|
|
283
293
|
>= self._transport_options.heartbeats_until_dead
|
|
284
294
|
):
|
|
295
|
+
if self._close_session_after_time_secs is not None:
|
|
296
|
+
# already in grace period, no need to set again
|
|
297
|
+
continue
|
|
285
298
|
logging.debug(
|
|
286
299
|
"%r closing websocket because of heartbeat misses",
|
|
287
300
|
self.session_id,
|
|
288
301
|
)
|
|
289
302
|
await self._begin_close_session_countdown()
|
|
290
|
-
await self.close_websocket(
|
|
291
|
-
self._ws_wrapper, should_retry=not self._is_server
|
|
292
|
-
)
|
|
293
303
|
continue
|
|
294
304
|
except FailedSendingMessageException:
|
|
295
305
|
# this is expected during websocket closed period
|
|
@@ -415,7 +425,8 @@ class Session(object):
|
|
|
415
425
|
return
|
|
416
426
|
await ws_wrapper.close()
|
|
417
427
|
if should_retry and self._retry_connection_callback:
|
|
418
|
-
|
|
428
|
+
logging.error("### running retry_connection_callback")
|
|
429
|
+
await self._task_manager.create_task(self._retry_connection_callback(self))
|
|
419
430
|
|
|
420
431
|
async def _open_stream_and_call_handler(
|
|
421
432
|
self,
|
|
@@ -61,8 +61,7 @@ class Transport:
|
|
|
61
61
|
async with self._session_lock:
|
|
62
62
|
self._sessions[session._to_id] = session
|
|
63
63
|
else:
|
|
64
|
-
|
|
65
|
-
del self._sessions[session._to_id]
|
|
64
|
+
self._sessions[session._to_id] = session
|
|
66
65
|
|
|
67
66
|
def generate_nanoid(self) -> str:
|
|
68
67
|
return str(nanoid.generate())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|