fix(health): preserve transient failure codes through stale snapshot fallback
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
4a2e7dd893
commit
9c5a01ed67
@@ -209,3 +209,15 @@ def test_slots_rpc_normalizes_snapshot_store_read_failure():
|
||||
with pytest.raises(AccountSnapshotUnavailable) as raised:
|
||||
service.holdings_for_slots_rpc()
|
||||
assert raised.value.code == "read_failed"
|
||||
|
||||
|
||||
def test_sell_stale_snapshot_preserves_transient_live_failure_detail_code():
|
||||
broker, _, service, now = _wire()
|
||||
assert service.refresh()
|
||||
broker.balance_error = "timeout"
|
||||
now[0] += timedelta(seconds=3601)
|
||||
|
||||
with pytest.raises(AccountSnapshotUnavailable) as raised:
|
||||
service.holdings_for_sell()
|
||||
assert raised.value.code == "expired"
|
||||
assert raised.value.detail_code == "timeout"
|
||||
|
||||
@@ -214,6 +214,7 @@ class AccountSnapshotService:
|
||||
|
||||
def holdings_for_sell(self) -> list[BalanceItem]:
|
||||
"""Prefer a live balance; fallback only on transient transport failure."""
|
||||
transient_code: str | None = None
|
||||
try:
|
||||
return self._broker.get_balance()
|
||||
except BrokerBalanceError as exc:
|
||||
@@ -221,8 +222,14 @@ class AccountSnapshotService:
|
||||
raise AccountSnapshotUnavailable(
|
||||
"live_read_failed", detail_code=exc.code
|
||||
) from None
|
||||
transient_code = exc.code
|
||||
snapshot = self._load_current()
|
||||
age = self._age(snapshot.holdings_captured_at, self._sell_max_age)
|
||||
try:
|
||||
age = self._age(snapshot.holdings_captured_at, self._sell_max_age)
|
||||
except AccountSnapshotUnavailable as exc:
|
||||
raise AccountSnapshotUnavailable(
|
||||
exc.code, detail_code=transient_code
|
||||
) from None
|
||||
_log.warning(
|
||||
"sell balance using account snapshot: age_sec=%.0f",
|
||||
age,
|
||||
|
||||
@@ -443,9 +443,11 @@ class ExecutionService:
|
||||
try:
|
||||
holdings = self._sell_dispatch_balance_reader()
|
||||
except AccountSnapshotUnavailable as exc:
|
||||
code = "stale_snapshot" if exc.code == "expired" else (
|
||||
getattr(exc, "detail_code", None) or exc.code
|
||||
)
|
||||
detail_code = getattr(exc, "detail_code", None)
|
||||
if exc.code == "expired" and detail_code is None:
|
||||
code = "stale_snapshot"
|
||||
else:
|
||||
code = detail_code or exc.code
|
||||
lifecycle, backoff = self._balance_health.record_failure(
|
||||
self._balance_worker, code=code
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user