Skip to content

Commit 07aecfe

Browse files
raballewclaude
andcommitted
fix: raise AioRpcError on UNAVAILABLE timeout and assert router connection in test
Make UNAVAILABLE timeout in handle_async raise instead of returning silently, matching the FAILED_PRECONDITION timeout behavior. Add assertion that connect_router_stream is called after successful UNAVAILABLE retry. Generated-By: Forge/20260416_202053_681470_11575359_i242 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 990d7c4 commit 07aecfe

2 files changed

Lines changed: 22 additions & 16 deletions

File tree

python/packages/jumpstarter/jumpstarter/client/lease.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,7 @@ async def _acquire(self):
237237
# Old controllers (pre-918d6341) mark offline-but-matching
238238
# exporters as Unsatisfiable with reason "NoExporter".
239239
# This is transient — retry with a new lease.
240-
if condition_present_and_equal(
241-
result.conditions, "Unsatisfiable", "True", "NoExporter"
242-
):
240+
if condition_present_and_equal(result.conditions, "Unsatisfiable", "True", "NoExporter"):
243241
await self._handle_no_exporter_retry(spinner, message)
244242
continue
245243
logger.debug("Lease %s cannot be satisfied: %s", self.name, message)
@@ -325,38 +323,43 @@ async def handle_async(self, stream):
325323
if remaining <= 0:
326324
logger.debug(
327325
"Exporter not ready and dial timeout (%.1fs) exceeded after %d attempts",
328-
self.dial_timeout, attempt + 1
326+
self.dial_timeout,
327+
attempt + 1,
329328
)
330329
raise
331-
delay = min(base_delay * (2 ** attempt), max_delay, remaining)
330+
delay = min(base_delay * (2**attempt), max_delay, remaining)
332331
logger.debug(
333332
"Exporter not ready, retrying Dial in %.1fs (attempt %d, %.1fs remaining)",
334-
delay, attempt + 1, remaining
333+
delay,
334+
attempt + 1,
335+
remaining,
335336
)
336337
await sleep(delay)
337338
attempt += 1
338339
continue
339340
if e.code() == grpc.StatusCode.UNAVAILABLE:
340341
remaining = deadline - time.monotonic()
341342
if remaining <= 0:
342-
logger.warning(
343+
logger.debug(
343344
"Exporter unavailable and dial timeout (%.1fs) exceeded after %d attempts",
344-
self.dial_timeout, attempt + 1
345+
self.dial_timeout,
346+
attempt + 1,
345347
)
346-
return
347-
delay = min(base_delay * (2 ** attempt), max_delay, remaining)
348+
raise
349+
delay = min(base_delay * (2**attempt), max_delay, remaining)
348350
logger.debug(
349351
"Exporter unavailable, retrying Dial in %.1fs (attempt %d, %.1fs remaining)",
350-
delay, attempt + 1, remaining
352+
delay,
353+
attempt + 1,
354+
remaining,
351355
)
352356
await sleep(delay)
353357
attempt += 1
354358
continue
355359
if "permission denied" in str(e.details()).lower():
356360
self.lease_transferred = True
357361
logger.warning(
358-
"Lease %s has been transferred to another client. "
359-
"Your session is no longer valid.",
362+
"Lease %s has been transferred to another client. Your session is no longer valid.",
360363
self.name,
361364
)
362365
else:

python/packages/jumpstarter/jumpstarter/client/lease_test.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,11 +575,12 @@ async def mock_dial(request):
575575

576576
await lease.handle_async(stream)
577577

578-
assert dial_call_count == 2
578+
assert dial_call_count == 2
579+
mock_connect.assert_called_once_with("endpoint", "token", stream, lease.tls_config, lease.grpc_options)
579580

580581
@pytest.mark.anyio
581582
async def test_handle_async_unavailable_exceeds_dial_timeout(self):
582-
"""Dial returns UNAVAILABLE until dial_timeout is exceeded."""
583+
"""Dial returns UNAVAILABLE until dial_timeout is exceeded, then raises."""
583584
lease = self._make_lease_for_handle()
584585
lease.dial_timeout = 0.5
585586
dial_call_count = 0
@@ -592,6 +593,8 @@ async def mock_dial(request):
592593
lease.controller.Dial = mock_dial
593594
stream = Mock()
594595

595-
await lease.handle_async(stream)
596+
with pytest.raises(AioRpcError) as exc_info:
597+
await lease.handle_async(stream)
596598

599+
assert exc_info.value.code() == grpc.StatusCode.UNAVAILABLE
597600
assert dial_call_count >= 2

0 commit comments

Comments
 (0)