Skip to content

Commit e526fa4

Browse files
committed
fix: http timing module
1 parent f7573f0 commit e526fa4

File tree

1 file changed

+58
-43
lines changed

1 file changed

+58
-43
lines changed

common/http_timing.py

Lines changed: 58 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import asyncio
44
import time
5-
from typing import Any, Dict, Optional
5+
from typing import Any, Optional
66

77
import aiohttp
88

@@ -12,34 +12,26 @@
1212
class HttpTimingCollector:
1313
"""Utility class for measuring HTTP request timing with detailed breakdown."""
1414

15-
def __init__(self):
16-
self.timing: Dict[str, float] = {}
15+
def __init__(self) -> None:
16+
self.timing: dict[str, float] = {}
1717

1818
def create_trace_config(self) -> aiohttp.TraceConfig:
1919
"""Create aiohttp trace configuration for detailed timing measurement."""
2020
trace_config = aiohttp.TraceConfig()
2121

22-
async def on_request_start(session, context, params):
22+
async def on_request_start(_session: Any, _context: Any, _params: Any) -> None:
2323
self.timing["start"] = time.monotonic()
2424

25-
async def on_dns_resolvehost_start(session, context, params):
26-
self.timing["dns_start"] = time.monotonic()
27-
28-
async def on_dns_resolvehost_end(session, context, params):
29-
self.timing["dns_end"] = time.monotonic()
30-
31-
async def on_connection_create_start(session, context, params):
25+
async def on_connection_create_start(_session: Any, _context: Any, _params: Any) -> None:
3226
self.timing["conn_start"] = time.monotonic()
3327

34-
async def on_connection_create_end(session, context, params):
28+
async def on_connection_create_end(_session: Any, _context: Any, _params: Any) -> None:
3529
self.timing["conn_end"] = time.monotonic()
3630

37-
async def on_request_end(session, context, params):
31+
async def on_request_end(_session: Any, _context: Any, _params: Any) -> None:
3832
self.timing["end"] = time.monotonic()
3933

4034
trace_config.on_request_start.append(on_request_start)
41-
trace_config.on_dns_resolvehost_start.append(on_dns_resolvehost_start)
42-
trace_config.on_dns_resolvehost_end.append(on_dns_resolvehost_end)
4335
trace_config.on_connection_create_start.append(on_connection_create_start)
4436
trace_config.on_connection_create_end.append(on_connection_create_end)
4537
trace_config.on_request_end.append(on_request_end)
@@ -52,52 +44,75 @@ def get_connection_time(self) -> float:
5244
return self.timing["conn_end"] - self.timing["conn_start"]
5345
return 0.0
5446

55-
def get_dns_time(self) -> float:
56-
"""Get DNS resolution time in seconds."""
57-
if "dns_start" in self.timing and "dns_end" in self.timing:
58-
return self.timing["dns_end"] - self.timing["dns_start"]
47+
def get_total_time(self) -> float:
48+
"""Get total request time in seconds."""
49+
if "start" in self.timing and "end" in self.timing:
50+
return self.timing["end"] - self.timing["start"]
5951
return 0.0
6052

6153

6254
async def measure_http_request_timing(
6355
session: aiohttp.ClientSession,
6456
method: str,
6557
url: str,
66-
headers: Optional[Dict[str, str]] = None,
67-
json_data: Optional[Dict[str, Any]] = None,
58+
headers: Optional[dict[str, str]] = None,
59+
json_data: Optional[dict[str, Any]] = None,
6860
exclude_connection_time: bool = True,
6961
) -> tuple[float, aiohttp.ClientResponse]:
7062
"""Measure HTTP request timing with retry logic and detailed breakdown.
7163
64+
Args:
65+
exclude_connection_time: If True, exclude connection establishment time
66+
from the returned timing to measure pure API response time
67+
7268
Returns:
7369
tuple: (response_time_seconds, response)
7470
"""
75-
response_time = 0.0
76-
response = None
71+
timing_collector = HttpTimingCollector()
72+
trace_config: aiohttp.TraceConfig = timing_collector.create_trace_config()
7773

78-
for retry_count in range(MAX_RETRIES):
79-
start_time = time.monotonic()
74+
# Add timing trace to session temporarily
75+
session._trace_configs.append(trace_config)
8076

81-
# Send request
82-
if method.upper() == "POST":
83-
response = await session.post(
84-
url, headers=headers, json=json_data
85-
)
86-
else:
87-
response = await session.get(url, headers=headers)
77+
try:
78+
response = None
79+
80+
for retry_count in range(MAX_RETRIES):
81+
# Reset timing for each retry
82+
timing_collector.timing.clear()
8883

89-
response_time = time.monotonic() - start_time
84+
# Send request
85+
if method.upper() == "POST":
86+
response = await session.post(
87+
url, headers=headers, json=json_data
88+
)
89+
else:
90+
response = await session.get(url, headers=headers)
9091

91-
# Handle rate limiting
92-
if response.status == 429 and retry_count < MAX_RETRIES - 1:
93-
wait_time = int(response.headers.get("Retry-After", 3))
94-
await response.release()
95-
await asyncio.sleep(wait_time)
96-
continue
92+
# Handle rate limiting
93+
if response.status == 429 and retry_count < MAX_RETRIES - 1:
94+
wait_time = int(response.headers.get("Retry-After", 3))
95+
await response.release()
96+
await asyncio.sleep(wait_time)
97+
continue
9798

98-
break
99+
break
100+
101+
if not response:
102+
raise ValueError("No response received")
103+
104+
# Calculate response time based on exclusion setting
105+
total_time: float = timing_collector.get_total_time()
106+
107+
if exclude_connection_time:
108+
connection_time: float = timing_collector.get_connection_time()
109+
response_time: float = max(0.0, total_time - connection_time)
110+
else:
111+
response_time = total_time
99112

100-
if not response:
101-
raise ValueError("No response received")
113+
return response_time, response
102114

103-
return response_time, response
115+
finally:
116+
# Remove trace config to avoid affecting other requests
117+
if trace_config in session._trace_configs:
118+
session._trace_configs.remove(trace_config)

0 commit comments

Comments
 (0)