plan(SP22): Task 6 — apply Task 4/5 fixes (4xx terminal, RuntimeError, retry logging) preemptively

This commit is contained in:
Tyler
2026-06-21 13:51:42 -06:00
parent f6f821e082
commit 646d00adde
@@ -1177,7 +1177,8 @@ Add the methods to the `CycloneClient` class:
async def clearhouse_submit( async def clearhouse_submit(
self, payer_id: str, tx: str = "837P" self, payer_id: str, tx: str = "837P"
) -> SubmitResult: ) -> SubmitResult:
assert self._http is not None if self._http is None:
raise RuntimeError("use `async with CycloneClient(...)`")
# Do NOT auto-retry — would double-submit. # Do NOT auto-retry — would double-submit.
resp = await self._http.post( resp = await self._http.post(
"/api/clearhouse/submit", "/api/clearhouse/submit",
@@ -1190,7 +1191,8 @@ Add the methods to the `CycloneClient` class:
return SubmitResult.model_validate(resp.json()) return SubmitResult.model_validate(resp.json())
async def scheduler_tick(self) -> TickResult: async def scheduler_tick(self) -> TickResult:
assert self._http is not None if self._http is None:
raise RuntimeError("use `async with CycloneClient(...)`")
# Manual tick is idempotent; safe to retry. # Manual tick is idempotent; safe to retry.
last_exc: Exception | None = None last_exc: Exception | None = None
for attempt in range(MAX_RETRIES): for attempt in range(MAX_RETRIES):
@@ -1200,15 +1202,26 @@ Add the methods to the `CycloneClient` class:
) )
resp.raise_for_status() resp.raise_for_status()
return TickResult.model_validate(resp.json()) return TickResult.model_validate(resp.json())
except (httpx.HTTPStatusError, httpx.RequestError) as e: except httpx.HTTPStatusError as e:
if e.response is not None and e.response.status_code < 500:
raise
last_exc = e last_exc = e
if attempt < MAX_RETRIES - 1: except httpx.RequestError as e:
await asyncio.sleep(BACKOFF_S[attempt]) last_exc = e
assert last_exc is not None if attempt < MAX_RETRIES - 1:
log.warning(
"api_post_retry",
path="/api/admin/scheduler/tick",
attempt=attempt + 1, wait_s=BACKOFF_S[attempt],
error=str(last_exc),
)
await asyncio.sleep(BACKOFF_S[attempt])
assert last_exc is not None # loop body always sets this on the way out
raise last_exc raise last_exc
async def processed_files(self, since: str) -> list[ProcessedFile]: async def processed_files(self, since: str) -> list[ProcessedFile]:
assert self._http is not None if self._http is None:
raise RuntimeError("use `async with CycloneClient(...)`")
last_exc: Exception | None = None last_exc: Exception | None = None
for attempt in range(MAX_RETRIES): for attempt in range(MAX_RETRIES):
try: try:
@@ -1221,11 +1234,21 @@ Add the methods to the `CycloneClient` class:
ProcessedFile.model_validate(f) ProcessedFile.model_validate(f)
for f in _extract_items(resp.json()) for f in _extract_items(resp.json())
] ]
except (httpx.HTTPStatusError, httpx.RequestError) as e: except httpx.HTTPStatusError as e:
if e.response is not None and e.response.status_code < 500:
raise
last_exc = e last_exc = e
if attempt < MAX_RETRIES - 1: except httpx.RequestError as e:
await asyncio.sleep(BACKOFF_S[attempt]) last_exc = e
assert last_exc is not None if attempt < MAX_RETRIES - 1:
log.warning(
"api_get_retry",
path="/api/admin/scheduler/processed-files",
attempt=attempt + 1, wait_s=BACKOFF_S[attempt],
error=str(last_exc),
)
await asyncio.sleep(BACKOFF_S[attempt])
assert last_exc is not None # loop body always sets this on the way out
raise last_exc raise last_exc
``` ```