fix(msk): сухой прогон пред-геокода падал на отсутствующей кэш-таблице
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Failing after 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 58s
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Failing after 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 58s
Кэш `msk_raw.avito_geocode` создаётся только боевым прогоном (`geocode and not dry_run`), а читается безусловно. На проде это роняло `--dry-run` первым же запросом — UndefinedTable msk_raw.avito_geocode, то есть ломалась ровно та репетиция, ради которой сухой прогон и существует. Наличие отношения проверяется через `to_regclass`, а не ловится исключением: в Postgres упавший оператор кладёт транзакцию целиком, и except потребовал бы rollback посреди чужого батча. Замер после правки (500 карточек, прод): отобрано 458, область 7, не разрешено 35 (7%), геокод-вызовов 338 на 500 карточек — дедупликация адреса внутри страницы работает. Счётчики сходятся. Заодно выяснилось, что дневная квота DaData на подсказки — 200 000, а не 10 000: `stat/daily` на проде показывает suggestions remaining 200000 при нулевом расходе. Весь корпус (21 565 различных адресов) проходит за один заход, дробить на трое суток не нужно. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ8jqr4SFirX5tFLwdSrXh
This commit is contained in:
parent
c5186883a9
commit
de5f4a32cd
2 changed files with 51 additions and 1 deletions
|
|
@ -224,6 +224,7 @@ CREATE TABLE IF NOT EXISTS msk_raw.avito_geocode (
|
|||
resolved_at timestamptz NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""
|
||||
_GEO_CACHE_EXISTS = "SELECT to_regclass('msk_raw.avito_geocode')"
|
||||
_GEO_CACHE_SELECT = """
|
||||
SELECT cache_key, lat, lon, region_code, full_address
|
||||
FROM msk_raw.avito_geocode
|
||||
|
|
@ -350,7 +351,17 @@ def _resolve_points(
|
|||
return {}
|
||||
|
||||
points: dict[str, GeoPoint | None] = {}
|
||||
cached = db.execute(text(_GEO_CACHE_SELECT), {"keys": list(wanted)}).mappings().all()
|
||||
# Кэш создаётся только боевым прогоном (`geocode and not dry_run`), поэтому на
|
||||
# первом `--dry-run` таблицы ещё нет и SELECT по ней роняет весь замер — то
|
||||
# есть ломается ровно та репетиция, ради которой сухой прогон и существует.
|
||||
# Проверяем наличие отношения, а не ловим исключение: в Postgres упавший
|
||||
# оператор кладёт транзакцию целиком, и except потребовал бы rollback.
|
||||
cache_exists = db.execute(text(_GEO_CACHE_EXISTS)).scalar() is not None
|
||||
cached = (
|
||||
db.execute(text(_GEO_CACHE_SELECT), {"keys": list(wanted)}).mappings().all()
|
||||
if cache_exists
|
||||
else ()
|
||||
)
|
||||
for row in cached:
|
||||
points[row["cache_key"]] = (
|
||||
None
|
||||
|
|
|
|||
|
|
@ -105,6 +105,10 @@ class _FakeResult:
|
|||
def all(self) -> list[dict[str, Any]]:
|
||||
return self._rows
|
||||
|
||||
def scalar(self) -> Any:
|
||||
"""Проба существования кэш-таблицы: None = таблицы ещё нет."""
|
||||
return self._rows[0] if self._rows else None
|
||||
|
||||
|
||||
class _FakeSession:
|
||||
"""Отдаёт строки постранично по тому же keyset-условию, что и настоящая вью."""
|
||||
|
|
@ -361,3 +365,38 @@ def test_geocode_is_refused_for_cian(captured: list[dict[str, Any]]) -> None:
|
|||
"""У Циана город виден по префиксу округа даром — квоту на него не тратим."""
|
||||
with pytest.raises(SystemExit):
|
||||
mod.import_msk_raw(_FakeSession(_rows()), source="cian", batch_size=10, geocode=True)
|
||||
|
||||
|
||||
def test_dry_run_survives_missing_geocode_cache(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Первый `--dry-run` идёт по проду, где кэш-таблицы ещё нет.
|
||||
|
||||
Таблицу создаёт только боевой прогон, поэтому SELECT по ней в сухом ронял
|
||||
ровно ту репетицию, ради которой сухой прогон и существует (замер на проде
|
||||
11.09: UndefinedTable msk_raw.avito_geocode). Резолвер обязан молча считать
|
||||
кэш пустым и идти к DaData за остатком.
|
||||
"""
|
||||
|
||||
class _NoCacheSession:
|
||||
def __init__(self) -> None:
|
||||
self.cache_reads = 0
|
||||
|
||||
def execute(self, sql: Any, params: dict[str, Any] | None = None) -> _FakeResult:
|
||||
if "to_regclass" in str(sql):
|
||||
return _FakeResult([]) # таблицы нет
|
||||
self.cache_reads += 1
|
||||
raise AssertionError("к отсутствующей кэш-таблице ходить нельзя")
|
||||
|
||||
point = mod.GeoPoint(55.6412, 37.6089, 77, "г Москва, ш Варшавское, д 62 к 1")
|
||||
|
||||
async def _fake_geocode(misses: list[Any]) -> dict[str, Any]:
|
||||
return {misses[0][0]: point}
|
||||
|
||||
monkeypatch.setattr(mod, "_geocode_many", _fake_geocode)
|
||||
|
||||
db = _NoCacheSession()
|
||||
points = mod._resolve_points(
|
||||
db, [{"id": 1, "payload": _avito()}], mod.GeocodeBudget(10), dry_run=True
|
||||
)
|
||||
|
||||
assert db.cache_reads == 0
|
||||
assert list(points.values()) == [point]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue