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:
|
with pytest.raises(AccountSnapshotUnavailable) as raised:
|
||||||
service.holdings_for_slots_rpc()
|
service.holdings_for_slots_rpc()
|
||||||
assert raised.value.code == "read_failed"
|
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]:
|
def holdings_for_sell(self) -> list[BalanceItem]:
|
||||||
"""Prefer a live balance; fallback only on transient transport failure."""
|
"""Prefer a live balance; fallback only on transient transport failure."""
|
||||||
|
transient_code: str | None = None
|
||||||
try:
|
try:
|
||||||
return self._broker.get_balance()
|
return self._broker.get_balance()
|
||||||
except BrokerBalanceError as exc:
|
except BrokerBalanceError as exc:
|
||||||
@@ -221,8 +222,14 @@ class AccountSnapshotService:
|
|||||||
raise AccountSnapshotUnavailable(
|
raise AccountSnapshotUnavailable(
|
||||||
"live_read_failed", detail_code=exc.code
|
"live_read_failed", detail_code=exc.code
|
||||||
) from None
|
) from None
|
||||||
|
transient_code = exc.code
|
||||||
snapshot = self._load_current()
|
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(
|
_log.warning(
|
||||||
"sell balance using account snapshot: age_sec=%.0f",
|
"sell balance using account snapshot: age_sec=%.0f",
|
||||||
age,
|
age,
|
||||||
|
|||||||
@@ -443,9 +443,11 @@ class ExecutionService:
|
|||||||
try:
|
try:
|
||||||
holdings = self._sell_dispatch_balance_reader()
|
holdings = self._sell_dispatch_balance_reader()
|
||||||
except AccountSnapshotUnavailable as exc:
|
except AccountSnapshotUnavailable as exc:
|
||||||
code = "stale_snapshot" if exc.code == "expired" else (
|
detail_code = getattr(exc, "detail_code", None)
|
||||||
getattr(exc, "detail_code", None) or exc.code
|
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(
|
lifecycle, backoff = self._balance_health.record_failure(
|
||||||
self._balance_worker, code=code
|
self._balance_worker, code=code
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user