feat: redefine generation_ms as net generation time with stall attribution
CallStats gains stall_ms/stall_count (None = no stall observation); winner sample triple (wall, stall_ms, stall_count) converted once in _attempt success branch; net = max(0, wall - stall) converged into the shared _record_winner_sample used by both the single-path guard and hedge winner registration. Two-fail hedge rounds record nothing (no winner to attribute). Design section 4.2; issues #27.
This commit is contained in:
@@ -569,6 +569,27 @@ class TestHedgeFailureCombination:
|
||||
# 两败轮次同样登记对冲路数(设计 §4.5: 实际并发发出即计)
|
||||
assert ctx.snapshot().hedges == 2
|
||||
|
||||
async def test_stall_attribution_none_when_no_winner(self):
|
||||
"""两败轮次无赢家(1.3.8 排除条款): generation 不记、stall 归因恒 None。
|
||||
|
||||
对冲已实际发出(hedges 照登),但无赢家路 → 裸生成时间与沉默归因都
|
||||
无归属;两路各自的沉默归属各自 attempt 遥测行(那是 T3 的事)。
|
||||
"""
|
||||
transport = HedgeTransport(
|
||||
{
|
||||
"s1": [("fail_after", 0.1, lambda: TransientError("p", source_name="s1"))],
|
||||
"s2": [("fail_after", 0.12, lambda: TransientError("h", source_name="s2"))],
|
||||
}
|
||||
)
|
||||
mw, _, _ = _harness([_src("s1"), _src("s2")], transport, max_attempts=1)
|
||||
ctx = _ctx()
|
||||
with pytest.raises(AllSourcesExhausted):
|
||||
await asyncio.wait_for(mw(_req(ctx=ctx)), timeout=5)
|
||||
stats = ctx.snapshot()
|
||||
assert stats.hedges == 1 and stats.hedge_won is False
|
||||
assert stats.generation_ms == 0
|
||||
assert stats.stall_ms is None and stats.stall_count is None
|
||||
|
||||
async def test_both_429_refund_no_budget(self):
|
||||
"""两路皆 429: 免预算且耗时退 stall 账——小 stall 窗下终局 stalled 而非耗尽。"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user