fix(tradein/proxy): успешный fetch затирал exit_ip и latency_ms в NULL (#3283) (#3305)
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Has been cancelled
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Has been cancelled
Co-authored-by: lekss361 <lekss361@gendsgn.local> Co-committed-by: lekss361 <lekss361@gendsgn.local>
This commit is contained in:
parent
72cb410b05
commit
4c29b59041
2 changed files with 47 additions and 4 deletions
|
|
@ -573,8 +573,20 @@ def mark_health(
|
||||||
SET consecutive_fails = 0,
|
SET consecutive_fails = 0,
|
||||||
last_ok_at = now(),
|
last_ok_at = now(),
|
||||||
last_check_at = now(),
|
last_check_at = now(),
|
||||||
exit_ip = CAST(:exit_ip AS text),
|
-- COALESCE, а не присваивание (#3283): подавляющее
|
||||||
latency_ms = CAST(:latency_ms AS integer),
|
-- большинство вызовов приходит НЕ из healthcheck'а, а из
|
||||||
|
-- _report_fetch_result на КАЖДЫЙ успешный /fetch и из
|
||||||
|
-- finally у curl_proxy_url — они зовут mark_health(lease, ok)
|
||||||
|
-- БЕЗ exit_ip/latency_ms, и адаптер подставляет None. Голое
|
||||||
|
-- присваивание затирало этим None адрес, записанный
|
||||||
|
-- proxy_rotation._update_exit_ip минутой раньше, так что
|
||||||
|
-- exit_ip в базе жил до первого же успешного запроса
|
||||||
|
-- (прод 31.08: у обоих живых узлов NULL при new_ip=
|
||||||
|
-- 31.173.86.74 в логе ротации). Явное значение по-прежнему
|
||||||
|
-- пишется; очистить поле через mark_health больше нельзя —
|
||||||
|
-- для этого есть прямой UPDATE.
|
||||||
|
exit_ip = COALESCE(CAST(:exit_ip AS text), exit_ip),
|
||||||
|
latency_ms = COALESCE(CAST(:latency_ms AS integer), latency_ms),
|
||||||
enabled = CASE
|
enabled = CASE
|
||||||
WHEN disabled_reason IS NULL THEN true ELSE enabled
|
WHEN disabled_reason IS NULL THEN true ELSE enabled
|
||||||
END,
|
END,
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,13 @@ class FakeSession:
|
||||||
row = self._by_id(p["id"])
|
row = self._by_id(p["id"])
|
||||||
if row is not None:
|
if row is not None:
|
||||||
row["consecutive_fails"] = 0
|
row["consecutive_fails"] = 0
|
||||||
|
# Зеркалит COALESCE(:param, <колонка>) реального SQL (#3283):
|
||||||
|
# mark_health(lease, ok) без exit_ip/latency_ms приходит из
|
||||||
|
# _report_fetch_result на каждый успешный /fetch, и None не должен
|
||||||
|
# затирать адрес, записанный ротацией.
|
||||||
|
if p["exit_ip"] is not None:
|
||||||
row["exit_ip"] = p["exit_ip"]
|
row["exit_ip"] = p["exit_ip"]
|
||||||
|
if p["latency_ms"] is not None:
|
||||||
row["latency_ms"] = p["latency_ms"]
|
row["latency_ms"] = p["latency_ms"]
|
||||||
row["last_ok_at"] = datetime.now(UTC)
|
row["last_ok_at"] = datetime.now(UTC)
|
||||||
row["last_check_at"] = datetime.now(UTC)
|
row["last_check_at"] = datetime.now(UTC)
|
||||||
|
|
@ -756,6 +762,31 @@ def test_mark_health_ok_resets_and_records() -> None:
|
||||||
assert row["last_ok_at"] is not None
|
assert row["last_ok_at"] is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_mark_health_ok_without_exit_ip_keeps_stored_value() -> None:
|
||||||
|
"""#3283: успешный /fetch зовёт mark_health(lease, ok) БЕЗ exit_ip/latency_ms.
|
||||||
|
|
||||||
|
До фикса None затирал адрес, записанный proxy_rotation._update_exit_ip, и
|
||||||
|
scrape_proxies.exit_ip жил до первого же успешного запроса (прод 31.08: у обоих
|
||||||
|
живых узлов NULL, при том что в логе ротации new_ip=31.173.86.74).
|
||||||
|
"""
|
||||||
|
db = FakeSession([_proxy(1)])
|
||||||
|
mark_health(db, 1, ok=True, exit_ip="31.173.86.74", latency_ms=120) # type: ignore[arg-type]
|
||||||
|
mark_health(db, 1, ok=True) # type: ignore[arg-type] # как из _report_fetch_result
|
||||||
|
row = db._by_id(1)
|
||||||
|
assert row["exit_ip"] == "31.173.86.74"
|
||||||
|
assert row["latency_ms"] == 120
|
||||||
|
|
||||||
|
|
||||||
|
def test_mark_health_ok_with_exit_ip_overwrites_stored_value() -> None:
|
||||||
|
"""Явное значение по-прежнему пишется — COALESCE не превращает поле в append-only."""
|
||||||
|
db = FakeSession([_proxy(1)])
|
||||||
|
mark_health(db, 1, ok=True, exit_ip="1.2.3.4", latency_ms=88) # type: ignore[arg-type]
|
||||||
|
mark_health(db, 1, ok=True, exit_ip="5.6.7.8", latency_ms=99) # type: ignore[arg-type]
|
||||||
|
row = db._by_id(1)
|
||||||
|
assert row["exit_ip"] == "5.6.7.8"
|
||||||
|
assert row["latency_ms"] == 99
|
||||||
|
|
||||||
|
|
||||||
def test_mark_health_ok_revives_disabled_proxy() -> None:
|
def test_mark_health_ok_revives_disabled_proxy() -> None:
|
||||||
"""Успешная проба реанимирует выключенный узел (#2600 п.1) — enabled=true, fails=0."""
|
"""Успешная проба реанимирует выключенный узел (#2600 п.1) — enabled=true, fails=0."""
|
||||||
db = FakeSession([_proxy(1, enabled=False, fails=DISABLE_THRESHOLD)])
|
db = FakeSession([_proxy(1, enabled=False, fails=DISABLE_THRESHOLD)])
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue