[tradein][backend] N+1 INSERT в _save_yandex_history_items → executemany #826

Closed
opened 2026-05-30 19:52:08 +00:00 by lekss361 · 2 comments
Owner

Worker: auto-backend, ветка от forgejo/main. Только этот hot-path, без рефактора соседних функций.

Задача

_save_yandex_history_items пишет N history-items в house_placement_history циклом for row in rows: db.execute(sql, row) — N round-trip вместо одного executemany. Yandex отдаёт десятки точек на дом → N запросов на каждый estimate-save.

Файлы

  • estimator.py:553 — def _save_yandex_history_items(...)
  • estimator.py:661-664 — цикл for row in rows: db.execute(sql,row); SQL с ON CONFLICT (source,ext_item_id) DO NOTHING (636-659), executemany-совместим.

Definition of Done

  • Цикл заменён на if rows: db.execute(sql, rows) (list-of-dict → executemany).
  • ON CONFLICT DO NOTHING сохранён; возврат len(rows) как был; except (rollback+warning+return 0) сохранён.
  • pytest test_yandex_valuation_save.py pass; ruff clean.

Не делать

  • НЕ менять SQL/колонки. НЕ трогать соседние функции. НЕ менять формат count.
> Worker: auto-backend, ветка от forgejo/main. Только этот hot-path, без рефактора соседних функций. ## Задача `_save_yandex_history_items` пишет N history-items в house_placement_history циклом `for row in rows: db.execute(sql, row)` — N round-trip вместо одного executemany. Yandex отдаёт десятки точек на дом → N запросов на каждый estimate-save. ## Файлы - estimator.py:553 — def _save_yandex_history_items(...) - estimator.py:661-664 — цикл for row in rows: db.execute(sql,row); SQL с ON CONFLICT (source,ext_item_id) DO NOTHING (636-659), executemany-совместим. ## Definition of Done - [ ] Цикл заменён на `if rows: db.execute(sql, rows)` (list-of-dict → executemany). - [ ] ON CONFLICT DO NOTHING сохранён; возврат len(rows) как был; except (rollback+warning+return 0) сохранён. - [ ] pytest test_yandex_valuation_save.py pass; ruff clean. ## Не делать - НЕ менять SQL/колонки. НЕ трогать соседние функции. НЕ менять формат count.
lekss361 added this to the Night batch — 2026-05-31 milestone 2026-05-30 19:52:08 +00:00
lekss361 added the
scope/backend
performance
priority/p3
tradein
status/ready
labels 2026-05-30 19:52:08 +00:00
bot-backend added
status/wip
and removed
status/ready
labels 2026-05-30 19:56:24 +00:00
bot-backend self-assigned this 2026-05-30 19:56:25 +00:00
bot-backend added
status/review
and removed
status/wip
labels 2026-05-30 20:01:32 +00:00
Collaborator

PR #832 (feat/826-yandex-executemany) → review. Цикл for row in rows: db.execute(sql, row)if rows: db.execute(sql, rows) (executemany). SQL/ON CONFLICT/return/except не тронуты, только эта функция. 6/6 passed, ruff clean.

PR #832 (feat/826-yandex-executemany) → review. Цикл `for row in rows: db.execute(sql, row)` → `if rows: db.execute(sql, rows)` (executemany). SQL/ON CONFLICT/return/except не тронуты, только эта функция. 6/6 passed, ruff clean.
bot-reviewer added
status/qa
and removed
status/review
labels 2026-05-30 20:07:12 +00:00
Collaborator

QA PASS — N+1 → executemany (PR #832, merged). Verified по merged diff.

estimator._save_yandex_history_items:

  • for row in rows: db.execute(sql, row)if rows: db.execute(sql, rows) — list-of-dict в Session.execute = один executemany round-trip вместо N.
  • db.commit(), return len(rows), except (rollback+warning+return 0) — сохранены (изменён только цикл).
  • SQL / ON CONFLICT (source,ext_item_id) DO NOTHING не тронут.
  • if rows: guard корректно скипает пустой список (executemany на [] упал бы). INSERT без RETURNING (count из Python len) → нет executemany-RETURNING pitfall.

Тесты (test_yandex_valuation_save.py): обновлены на call_count == 1 (один executemany), args[1] = list-of-dicts, проверки полей через rows[0]. 6/6 passed, ruff clean.

DoD: цикл→executemany , ON CONFLICT/return/except сохранены , pytest 6/6 + ruff , только эта функция .

status/done, closing.

✅ **QA PASS** — N+1 → executemany (PR #832, merged). Verified по merged diff. **`estimator._save_yandex_history_items`:** - `for row in rows: db.execute(sql, row)` → `if rows: db.execute(sql, rows)` — list-of-dict в `Session.execute` = один executemany round-trip вместо N. ✅ - `db.commit()`, `return len(rows)`, `except (rollback+warning+return 0)` — сохранены (изменён только цикл). ✅ - SQL / `ON CONFLICT (source,ext_item_id) DO NOTHING` не тронут. ✅ - `if rows:` guard корректно скипает пустой список (executemany на `[]` упал бы). INSERT без RETURNING (count из Python `len`) → нет executemany-RETURNING pitfall. ✅ **Тесты (`test_yandex_valuation_save.py`):** обновлены на `call_count == 1` (один executemany), `args[1]` = list-of-dicts, проверки полей через `rows[0]`. 6/6 passed, ruff clean. ✅ DoD: цикл→executemany ✅, ON CONFLICT/return/except сохранены ✅, pytest 6/6 + ruff ✅, только эта функция ✅. → `status/done`, closing.
bot-qa added
status/done
and removed
status/qa
labels 2026-05-30 20:09:16 +00:00
Sign in to join this conversation.
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#826
No description provided.