fix(pzz_loader): SAVEPOINT pattern + outer error handler (transaction correctness) #124
1 changed files with 35 additions and 27 deletions
|
|
@ -80,6 +80,10 @@ def sync_pzz_zones_to_db() -> dict[str, int]:
|
|||
"""Загрузить зоны из PKK6 и UPSERT в pzz_zones_ekb.
|
||||
|
||||
Возвращает словарь {fetched, inserted, updated, skipped}.
|
||||
|
||||
Каждый UPSERT обёрнут в SAVEPOINT (db.begin_nested), чтобы ошибка
|
||||
одной записи не откатывала всю транзакцию и счётчики оставались
|
||||
согласованными с тем, что реально записано в DB.
|
||||
"""
|
||||
features = asyncio.run(fetch_pkk6_zones())
|
||||
inserted = 0
|
||||
|
|
@ -105,41 +109,45 @@ def sync_pzz_zones_to_db() -> dict[str, int]:
|
|||
)
|
||||
description = props.get("description")
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
INSERT INTO pzz_zones_ekb
|
||||
(rosreestr_id, zone_code, zone_name, description, raw_props, geom)
|
||||
VALUES (
|
||||
:rid, :code, :nm, :desc, CAST(:props AS jsonb),
|
||||
ST_Multi(ST_SetSRID(ST_GeomFromGeoJSON(:g), 4326))
|
||||
)
|
||||
ON CONFLICT (rosreestr_id) DO UPDATE
|
||||
SET zone_code = EXCLUDED.zone_code,
|
||||
zone_name = EXCLUDED.zone_name,
|
||||
description = EXCLUDED.description,
|
||||
raw_props = EXCLUDED.raw_props,
|
||||
geom = EXCLUDED.geom,
|
||||
fetched_at = NOW()
|
||||
RETURNING (xmax = 0) AS is_insert
|
||||
"""),
|
||||
{
|
||||
"rid": rosreestr_id,
|
||||
"code": zone_code,
|
||||
"nm": zone_name,
|
||||
"desc": description,
|
||||
"props": json.dumps(props, ensure_ascii=False),
|
||||
"g": json.dumps(geom),
|
||||
},
|
||||
).scalar()
|
||||
with db.begin_nested(): # SAVEPOINT — откат только этой записи
|
||||
result = db.execute(
|
||||
text("""
|
||||
INSERT INTO pzz_zones_ekb
|
||||
(rosreestr_id, zone_code, zone_name, description, raw_props, geom)
|
||||
VALUES (
|
||||
:rid, :code, :nm, :desc, CAST(:props AS jsonb),
|
||||
ST_Multi(ST_SetSRID(ST_GeomFromGeoJSON(:g), 4326))
|
||||
)
|
||||
ON CONFLICT (rosreestr_id) DO UPDATE
|
||||
SET zone_code = EXCLUDED.zone_code,
|
||||
zone_name = EXCLUDED.zone_name,
|
||||
description = EXCLUDED.description,
|
||||
raw_props = EXCLUDED.raw_props,
|
||||
geom = EXCLUDED.geom,
|
||||
fetched_at = NOW()
|
||||
RETURNING (xmax = 0) AS is_insert
|
||||
"""),
|
||||
{
|
||||
"rid": rosreestr_id,
|
||||
"code": zone_code,
|
||||
"nm": zone_name,
|
||||
"desc": description,
|
||||
"props": json.dumps(props, ensure_ascii=False),
|
||||
"g": json.dumps(geom),
|
||||
},
|
||||
).scalar()
|
||||
if result:
|
||||
inserted += 1
|
||||
else:
|
||||
updated += 1
|
||||
except Exception as e:
|
||||
logger.warning("PZZ insert failed for rosreestr_id=%s: %s", rosreestr_id, e)
|
||||
db.rollback()
|
||||
skipped += 1
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.exception("pzz_loader: unexpected error, outer tx rolled back: %s", e)
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue