Skip to content

Commit 5cb47e9

Browse files
Increase the timeout of gRPC calls dynamically on none-successful attempts
Signed-off-by: camille-bouvy-frequenz <[email protected]>
1 parent b6cdeae commit 5cb47e9

File tree

1 file changed

+35
-9
lines changed

1 file changed

+35
-9
lines changed

src/frequenz/client/electricity_trading/_client.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,28 +89,54 @@ def validate_decimal_places(value: Decimal, decimal_places: int, name: str) -> N
8989

9090

9191
async def grpc_call_with_timeout(
92-
call: Callable[..., Awaitable[Any]], *args: Any, timeout: float = 300, **kwargs: Any
92+
call: Callable[..., Awaitable[Any]],
93+
*args: Any,
94+
initial_timeout: float = 60.0,
95+
max_timeout: float = 300.0,
96+
retries: int = 5,
97+
**kwargs: Any,
9398
) -> Any:
9499
"""
95-
Call a gRPC function with a timeout (in seconds).
100+
Call a gRPC function with a timeout.
101+
102+
This timeout is increased dynamically on every none-successful reconnection attempts.
96103
97104
Args:
98105
call: The gRPC method to be called.
99106
*args: Positional arguments for the gRPC call.
100-
timeout: Timeout duration in seconds. Defaults to 300.
107+
initial_timeout: Initial timeout duration in seconds.
108+
max_timeout: Maximum timeout duration in seconds.
109+
retries: Number of retry attempts for timeout errors.
101110
**kwargs: Keyword arguments for the gRPC call.
102111
103112
Returns:
104113
The result of the gRPC call.
105114
106115
Raises:
107-
asyncio.TimeoutError: If the call exceeds the timeout.
116+
asyncio.TimeoutError: If all retries are exhausted.
108117
"""
109-
try:
110-
return await asyncio.wait_for(call(*args, **kwargs), timeout=timeout)
111-
except asyncio.TimeoutError:
112-
_logger.error("Timeout while calling %s", call)
113-
raise
118+
timeout_growth_factor = (max_timeout / initial_timeout) ** (1 / max(1, retries - 1))
119+
timeout = initial_timeout
120+
121+
for attempt in range(1, retries + 1):
122+
try:
123+
return await asyncio.wait_for(call(*args, **kwargs), timeout=timeout)
124+
except asyncio.TimeoutError:
125+
if attempt == retries:
126+
_logger.error(
127+
"Timeout after %d retries (timeout=%ds): %s",
128+
retries,
129+
timeout,
130+
call,
131+
)
132+
raise
133+
_logger.warning(
134+
"Timeout on attempt %d/%d (timeout=%ds). Retrying with increased timeout.",
135+
attempt,
136+
retries,
137+
timeout,
138+
)
139+
timeout = min(max_timeout, timeout * timeout_growth_factor)
114140

115141

116142
class Client(BaseApiClient[ElectricityTradingServiceStub]):

0 commit comments

Comments
 (0)