fix(sf): per-quarter commit в ird/opportunity harvest — не терять прогресс при краше (#1107)
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
ird_harvest + opportunity_harvest: commit после КАЖДОГО квартала (внутри per-quarter try) + db.rollback() в except перед continue. Прод-баг: single end-commit терял весь прогресс многочасового grid-walk при краше/таймауте/WorkerLost. Теперь durable поквартально; SAVEPOINT per-row не тронут. +2 теста (commit-count durability, flaky-quarter rollback+continue). Refs #1067. Co-authored-by: lekss361 <lekss361@gendsgn.local> Co-committed-by: lekss361 <lekss361@gendsgn.local>
This commit is contained in:
parent
dee7d64ac8
commit
fe7727e338
3 changed files with 48 additions and 2 deletions
|
|
@ -211,10 +211,14 @@ def harvest_ird_overlays(quarters: list[str] | None = None) -> dict[str, int]:
|
||||||
fetched_at=fetched_at,
|
fetched_at=fetched_at,
|
||||||
):
|
):
|
||||||
n_features += 1
|
n_features += 1
|
||||||
|
# Durable per-quarter commit: длинный grid-walk (часы) не теряет прогресс при
|
||||||
|
# краше/таймауте/WorkerLost середины прогона (commit раз-в-конце терял ВСЁ).
|
||||||
|
db.commit()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("ird_harvest: квартал %s failed: %s", quarter, exc)
|
logger.warning("ird_harvest: квартал %s failed: %s", quarter, exc)
|
||||||
|
db.rollback() # сбросить незакоммиченный tx квартала перед следующим
|
||||||
continue
|
continue
|
||||||
db.commit()
|
db.commit() # финальный — на случай пустого списка / подстраховка
|
||||||
logger.info("ird_harvest: готово, кварталов=%d фич=%d", n_quarters, n_features)
|
logger.info("ird_harvest: готово, кварталов=%d фич=%d", n_quarters, n_features)
|
||||||
return {"quarters": n_quarters, "features": n_features}
|
return {"quarters": n_quarters, "features": n_features}
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,14 @@ def harvest_opportunity_overlays(quarters: list[str] | None = None) -> dict[str,
|
||||||
fetched_at=fetched_at,
|
fetched_at=fetched_at,
|
||||||
):
|
):
|
||||||
n_features += 1
|
n_features += 1
|
||||||
|
# Durable per-quarter commit: длинный grid-walk не теряет прогресс при
|
||||||
|
# краше/таймауте середины прогона (commit раз-в-конце терял ВСЁ).
|
||||||
|
db.commit()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("opportunity_harvest: квартал %s failed: %s", quarter, exc)
|
logger.warning("opportunity_harvest: квартал %s failed: %s", quarter, exc)
|
||||||
|
db.rollback() # сбросить незакоммиченный tx квартала перед следующим
|
||||||
continue
|
continue
|
||||||
db.commit()
|
db.commit() # финальный — на случай пустого списка / подстраховка
|
||||||
logger.info("opportunity_harvest: готово, кварталов=%d фич=%d", n_quarters, n_features)
|
logger.info("opportunity_harvest: готово, кварталов=%d фич=%d", n_quarters, n_features)
|
||||||
return {"quarters": n_quarters, "features": n_features}
|
return {"quarters": n_quarters, "features": n_features}
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ class _FakeDB:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.executed: list[tuple[Any, dict[str, Any] | None]] = []
|
self.executed: list[tuple[Any, dict[str, Any] | None]] = []
|
||||||
self.committed = False
|
self.committed = False
|
||||||
|
self.commit_count = 0
|
||||||
|
self.rollback_count = 0
|
||||||
self.closed = False
|
self.closed = False
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|
@ -67,6 +69,10 @@ class _FakeDB:
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
||||||
self.committed = True
|
self.committed = True
|
||||||
|
self.commit_count += 1
|
||||||
|
|
||||||
|
def rollback(self) -> None:
|
||||||
|
self.rollback_count += 1
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
@ -131,3 +137,35 @@ def test_upsert_feature_skips_missing_geom_data_id(monkeypatch: Any) -> None:
|
||||||
is False
|
is False
|
||||||
)
|
)
|
||||||
assert db.executed == []
|
assert db.executed == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_quarter_commit_durability(monkeypatch: Any) -> None:
|
||||||
|
"""Каждый квартал коммитится отдельно → краш середины не теряет прошлые (фикс #1067)."""
|
||||||
|
db = _FakeDB()
|
||||||
|
_patch(monkeypatch, db)
|
||||||
|
|
||||||
|
result = ird_harvest.harvest_ird_overlays(quarters=["66:41:0000001", "66:41:0000002"])
|
||||||
|
|
||||||
|
assert result["quarters"] == 2
|
||||||
|
# commit per-quarter (2) + финальный = >=2; точно больше одного (не «раз в конце»)
|
||||||
|
assert db.commit_count >= 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_quarter_failure_rolls_back_and_continues(monkeypatch: Any) -> None:
|
||||||
|
"""Сбой одного квартала → rollback + продолжение; остальные кварталы коммитятся."""
|
||||||
|
db = _FakeDB()
|
||||||
|
client = _patch(monkeypatch, db)
|
||||||
|
|
||||||
|
def _flaky(cad: str, thematic_id: int = 1) -> Any:
|
||||||
|
if cad == "66:41:0000001":
|
||||||
|
raise RuntimeError("NSPD timeout")
|
||||||
|
ring = [[0, 0], [100, 0], [100, 100], [0, 100], [0, 0]]
|
||||||
|
return _SearchRes(_Feat({"type": "Polygon", "coordinates": [ring]}, {"cad": cad}))
|
||||||
|
|
||||||
|
monkeypatch.setattr(client, "search_by_cad", _flaky)
|
||||||
|
|
||||||
|
result = ird_harvest.harvest_ird_overlays(quarters=["66:41:0000001", "66:41:0000002"])
|
||||||
|
|
||||||
|
assert db.rollback_count >= 1 # битый квартал откатился
|
||||||
|
assert result["quarters"] == 1 # второй квартал обработан
|
||||||
|
assert db.committed is True
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue