Skip to content

Commit 9a0eccd

Browse files
smypmsaclaude
andcommitted
fix(hyperliquid): enforce 55s timeout for entire metric collection
- Wrap HTTP and WebSocket metric collection in asyncio.wait_for - Remove redundant inner timeout configurations in aiohttp - Reduce MAX_RETRIES from 3 to 2 and Retry-After from 15s to 3s - Ensure total execution cannot exceed 55s including retries/sleeps This prevents Hyperliquid metrics from exceeding the 59s Vercel limit by enforcing the timeout at the outer scope rather than per-request. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e473562 commit 9a0eccd

File tree

1 file changed

+26
-11
lines changed

1 file changed

+26
-11
lines changed

common/metric_types.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from common.metric_config import MetricConfig, MetricLabelKey, MetricLabels
1414
from common.metrics_handler import MetricsHandler
1515

16-
MAX_RETRIES = 3
16+
MAX_RETRIES = 2
1717

1818

1919
class WebSocketMetric(BaseMetric):
@@ -59,18 +59,29 @@ async def collect_metric(self) -> None:
5959
"""Collects single WebSocket message."""
6060
websocket = None
6161

62-
try:
62+
async def _collect_ws_data():
63+
nonlocal websocket
6364
websocket = await self.connect()
6465
await self.subscribe(websocket)
6566
data = await self.listen_for_data(websocket)
66-
67+
6768
if data is not None:
68-
latency: int | float = self.process_data(data)
69-
self.update_metric_value(latency)
70-
self.mark_success()
71-
return
69+
return data
7270
raise ValueError("No data in response")
7371

72+
try:
73+
data = await asyncio.wait_for(
74+
_collect_ws_data(),
75+
timeout=self.config.timeout
76+
)
77+
latency: int | float = self.process_data(data)
78+
self.update_metric_value(latency)
79+
self.mark_success()
80+
81+
except asyncio.TimeoutError:
82+
self.mark_failure()
83+
self.handle_error(TimeoutError(f"WebSocket metric collection exceeded {self.config.timeout}s timeout"))
84+
7485
except Exception as e:
7586
self.mark_failure()
7687
self.handle_error(e)
@@ -97,13 +108,19 @@ def get_endpoint(self) -> str:
97108

98109
async def collect_metric(self) -> None:
99110
try:
100-
data = await self.fetch_data()
111+
data = await asyncio.wait_for(
112+
self.fetch_data(),
113+
timeout=self.config.timeout
114+
)
101115
if data is not None:
102116
latency: int | float = self.process_data(data)
103117
self.update_metric_value(latency)
104118
self.mark_success()
105119
return
106120
raise ValueError("No data in response")
121+
except asyncio.TimeoutError:
122+
self.mark_failure()
123+
self.handle_error(TimeoutError(f"Metric collection exceeded {self.config.timeout}s timeout"))
107124
except Exception as e:
108125
self.mark_failure()
109126
self.handle_error(e)
@@ -205,7 +222,6 @@ async def on_request_end(session, context, params):
205222
trace_config.on_request_end.append(on_request_end)
206223

207224
async with aiohttp.ClientSession(
208-
timeout=aiohttp.ClientTimeout(total=self.config.timeout),
209225
trace_configs=[trace_config],
210226
) as session:
211227
response_time = 0.0
@@ -219,7 +235,7 @@ async def on_request_end(session, context, params):
219235
response_time: float = time.monotonic() - start_time
220236

221237
if response.status == 429 and retry_count < MAX_RETRIES - 1:
222-
wait_time = int(response.headers.get("Retry-After", 15))
238+
wait_time = int(response.headers.get("Retry-After", 3))
223239
await response.release()
224240
await asyncio.sleep(wait_time)
225241
continue
@@ -279,7 +295,6 @@ async def _send_request(
279295
"Content-Type": "application/json",
280296
},
281297
json=self._base_request,
282-
timeout=self.config.timeout, # type: ignore
283298
)
284299

285300
def process_data(self, value: float) -> float:

0 commit comments

Comments
 (0)