fix: insert demoted source after credible alternatives, not at tail
Round 4 showed tail placement routes the third attempt to junk sources whenever the credible alternative is gate-skipped (tight-RPM source admitted as credible, then skipped by the limiter, falling through to the flapping watchdog source). Demoted sources now sit between credible and non-credible candidates.
This commit is contained in:
@@ -87,12 +87,43 @@ def _demote_call_failures(
|
|||||||
demoted = [s for s in ordered if attempt_fails.get(s.name, 0) >= 2]
|
demoted = [s for s in ordered if attempt_fails.get(s.name, 0) >= 2]
|
||||||
if not demoted or len(demoted) == len(ordered):
|
if not demoted or len(demoted) == len(ordered):
|
||||||
return ordered
|
return ordered
|
||||||
if health is not None:
|
if health is None:
|
||||||
|
return _move_to_tail(ordered, demoted)
|
||||||
|
return _health_gated_reorder(ordered, demoted, attempt_fails, health)
|
||||||
|
|
||||||
|
|
||||||
|
def _move_to_tail(ordered: list[SourceConfig], demoted: list[SourceConfig]) -> list[SourceConfig]:
|
||||||
|
"""无健康视图: 无条件移尾(冷启动保护原语义)。"""
|
||||||
|
names = {d.name for d in demoted}
|
||||||
|
return [s for s in ordered if s.name not in names] + demoted
|
||||||
|
|
||||||
|
|
||||||
|
def _health_gated_reorder(
|
||||||
|
ordered: list[SourceConfig],
|
||||||
|
demoted: list[SourceConfig],
|
||||||
|
attempt_fails: dict[str, int],
|
||||||
|
health: Callable[[str], float],
|
||||||
|
) -> list[SourceConfig]:
|
||||||
|
"""健康门槛降权: 无可信替代则原地重试;有则插到可信替代之后。"""
|
||||||
demoted = _credible_demotions(ordered, demoted, attempt_fails, health)
|
demoted = _credible_demotions(ordered, demoted, attempt_fails, health)
|
||||||
if not demoted:
|
if not demoted:
|
||||||
return ordered
|
return ordered
|
||||||
names = {d.name for d in demoted}
|
names = {d.name for d in demoted}
|
||||||
return [s for s in ordered if s.name not in names] + demoted
|
rest = [s for s in ordered if s.name not in names]
|
||||||
|
return _insert_after_credible(rest, demoted, health)
|
||||||
|
|
||||||
|
|
||||||
|
def _insert_after_credible(
|
||||||
|
rest: list[SourceConfig],
|
||||||
|
demoted: list[SourceConfig],
|
||||||
|
health: Callable[[str], float],
|
||||||
|
) -> list[SourceConfig]:
|
||||||
|
"""插入位置(第四轮教训): 被降权源排在可信替代之后、不可信源之前——
|
||||||
|
可信替代被限流闸/熔断跳过时,下一候选是失败源本身而非垃圾源。"""
|
||||||
|
bar = 0.5 * max(health(d.name) for d in demoted)
|
||||||
|
credible = [s for s in rest if health(s.name) >= bar]
|
||||||
|
junk = [s for s in rest if health(s.name) < bar]
|
||||||
|
return credible + demoted + junk
|
||||||
|
|
||||||
|
|
||||||
def _credible_demotions(
|
def _credible_demotions(
|
||||||
|
|||||||
@@ -537,3 +537,23 @@ class TestHealthGatedDemotion:
|
|||||||
)
|
)
|
||||||
await mw(_REQ)
|
await mw(_REQ)
|
||||||
assert [n for n, _ in transport.calls] == ["a", "a", "b"]
|
assert [n for n, _ in transport.calls] == ["a", "a", "b"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestDemotionInsertPosition:
|
||||||
|
"""迭代 3(设计 §3.36 补): 被降权源插在可信替代之后、不可信源之前。"""
|
||||||
|
|
||||||
|
async def test_demoted_lands_before_junk_sources(self):
|
||||||
|
# a 失败 2 次;b 可信(0.9)但会被跳过时,第三候选应是 a 而非垃圾源 c
|
||||||
|
from polygateway.middleware.retry import _demote_call_failures
|
||||||
|
|
||||||
|
srcs = [_src("a"), _src("b"), _src("c")]
|
||||||
|
health = {"a": 0.9, "b": 0.9, "c": 0.05}.__getitem__
|
||||||
|
out = _demote_call_failures(srcs, {"a": 2}, health)
|
||||||
|
assert [s.name for s in out] == ["b", "a", "c"]
|
||||||
|
|
||||||
|
async def test_health_blind_demotion_still_tail(self):
|
||||||
|
from polygateway.middleware.retry import _demote_call_failures
|
||||||
|
|
||||||
|
srcs = [_src("a"), _src("b"), _src("c")]
|
||||||
|
out = _demote_call_failures(srcs, {"a": 2}, None)
|
||||||
|
assert [s.name for s in out] == ["b", "c", "a"]
|
||||||
|
|||||||
Reference in New Issue
Block a user