Skip to content

Commit 0fb984c

Browse files
committed
Use similar timeout logic for 1-to-1 requests
- One-to-many response logic doesn't use the request_timeout for the ``recv()`` since it indefinitely listens to the open websocket and with each turn of the while loop it needs to check the cache again. Use a similar logic for one-to-one requests except add the request timeout around the entirety of the while loop. This makes it so that we look for a response throughout the defined request_timeout time but we make sure to constantly check both the cache and call ``recv()`` on the websocket, in case the response has snuck into the cache from another request made somewhere else or from a persistent listener set up to receive messages.
1 parent 4cc490e commit 0fb984c

File tree

1 file changed

+19
-15
lines changed

1 file changed

+19
-15
lines changed

web3/providers/websocket/websocket_v2.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def __init__(
8787
)
8888
if found_restricted_keys:
8989
raise Web3ValidationError(
90-
f"Found restricted keys for websocket_kwargs: "
90+
"Found restricted keys for websocket_kwargs: "
9191
f"{found_restricted_keys}."
9292
)
9393

@@ -186,7 +186,15 @@ async def _match_response_id_to_request_id() -> RPCResponse:
186186
f"Response for id {request_id} is not cached, calling "
187187
"`recv()` on websocket."
188188
)
189-
response = await self._ws_recv(timeout=self.request_timeout)
189+
try:
190+
# keep timeout low but reasonable to check both the
191+
# cache and the websocket connection for new responses
192+
response = await self._ws_recv(timeout=2)
193+
except asyncio.TimeoutError:
194+
# keep the request timeout around the whole of this
195+
# while loop in case the response sneaks into the cache
196+
# from another call.
197+
continue
190198

191199
response_id = response.get("id")
192200

@@ -209,23 +217,19 @@ async def _match_response_id_to_request_id() -> RPCResponse:
209217
await asyncio.sleep(0.05)
210218

211219
try:
212-
# Enters a while loop, looking for a response id match to the request id.
213-
# If the provider does not give responses with matching ids, this will
214-
# hang forever. The JSON-RPC spec requires that providers respond with
215-
# the same id that was sent in the request, but we need to handle these
216-
# "bad" cases somewhat gracefully.
217-
timeout = (
218-
self.request_timeout
219-
if self.request_timeout and self.request_timeout <= 20
220-
else 20
220+
# Add the request timeout around the while loop that checks the request
221+
# cache and tried to recv(). If the request is neither in the cache, nor
222+
# received within the request_timeout, raise ``TimeExhausted``.
223+
return await asyncio.wait_for(
224+
_match_response_id_to_request_id(), self.request_timeout
221225
)
222-
return await asyncio.wait_for(_match_response_id_to_request_id(), timeout)
223226
except asyncio.TimeoutError:
224227
raise TimeExhausted(
225228
f"Timed out waiting for response with request id `{request_id}` after "
226-
f"{self.request_timeout} second(s). This is likely due to the provider "
227-
f"not returning a response with the same id that was sent in the "
228-
f"request, which is required by the JSON-RPC spec."
229+
f"{self.request_timeout} second(s). This may be due to the provider "
230+
"not returning a response with the same id that was sent in the "
231+
"request or an exception raised during the request was caught and "
232+
"allowed to continue."
229233
)
230234

231235
async def _ws_recv(self, timeout: float = None) -> RPCResponse:

0 commit comments

Comments
 (0)