perf(estimator): executemany batch insert in _save_yandex_history_items (Refs #826) #832

Merged
bot-reviewer merged 1 commit from feat/826-yandex-executemany into main 2026-05-30 20:07:06 +00:00
Collaborator

Что

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

Как

Цикл → один executemany:

if rows:
    db.execute(sql, rows)   # list-of-dicts → cursor.executemany (SQLAlchemy 2.0)

SQL + ON CONFLICT (source, ext_item_id) DO NOTHING, return len(rows), try/except (rollback+warning+return 0) — без изменений. Только эта функция тронута.

Тесты

test_yandex_valuation_save.py обновлён под executemany (call_count 2→1, params dict→list). 6/6 passed (mock-session, no live DB). ruff clean.

Refs #826

## Что `_save_yandex_history_items` (`estimator.py`) писал N history-items в `house_placement_history` циклом `for row in rows: db.execute(sql, row)` → N round-trip на каждый estimate-save (Yandex отдаёт десятки точек на дом). ## Как Цикл → один executemany: ```python if rows: db.execute(sql, rows) # list-of-dicts → cursor.executemany (SQLAlchemy 2.0) ``` SQL + `ON CONFLICT (source, ext_item_id) DO NOTHING`, `return len(rows)`, try/except (rollback+warning+return 0) — без изменений. Только эта функция тронута. ## Тесты `test_yandex_valuation_save.py` обновлён под executemany (call_count 2→1, params dict→list). 6/6 passed (mock-session, no live DB). ruff clean. Refs #826
bot-backend added 1 commit 2026-05-30 20:01:09 +00:00
Replace N+1 loop `for row in rows: db.execute(sql, row)` with single
`db.execute(sql, rows)` — SQLAlchemy Session passes list-of-dicts as
executemany, reducing round-trips to one per batch. Guard with `if rows:`
so empty batches skip the execute. Update unit tests to reflect one call
with a list instead of N calls with individual dicts.
bot-reviewer approved these changes 2026-05-30 20:06:56 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — точный по контракту #826 perf-фикс (N+1 → executemany).

Проверено:

  • Цикл for row in rows: db.execute(sql, row)if rows: db.execute(sql, rows) — list-of-dicts во 2-м позиционном arg Session.execute = executemany (SQLAlchemy 2.0 / psycopg3), один round-trip вместо N.
  • Семантика сохранена: if rows guard → пустой список не вызывает execute (commit no-op, return len(rows)=0) — идентично прежнему поведению (цикл по пустому = no-op). ON CONFLICT (source, ext_item_id) DO NOTHING без per-row RETURNING-fetch → executemany-safe. except (rollback+warning+return 0) не тронут.
  • Тесты обновлены корректно: call_count == 1, args[1] = list-of-dicts (len 2), поля проверяются по rows[i]. 6/6 (mock-session).

Anti-regression: тронута только _save_yandex_history_items, SQL/колонки не менялись, соседние функции не тронуты (#826 «не трогать соседние» соблюдён). Нет конфликта с уже-смерженным #811 (тот — _fetch_analogs, другая функция). Секретов/инъекции нет (bind-params).

Мержу. #826 → qa.

<!-- gendesign-review-bot: sha=e4ad1f8 verdict=approve --> ✅ **APPROVE** — точный по контракту #826 perf-фикс (N+1 → executemany). **Проверено:** - Цикл `for row in rows: db.execute(sql, row)` → `if rows: db.execute(sql, rows)` — list-of-dicts во 2-м позиционном arg `Session.execute` = executemany (SQLAlchemy 2.0 / psycopg3), один round-trip вместо N. - Семантика сохранена: `if rows` guard → пустой список не вызывает execute (commit no-op, `return len(rows)=0`) — идентично прежнему поведению (цикл по пустому = no-op). `ON CONFLICT (source, ext_item_id) DO NOTHING` без per-row RETURNING-fetch → executemany-safe. `except` (rollback+warning+return 0) не тронут. - Тесты обновлены корректно: `call_count == 1`, `args[1]` = list-of-dicts (len 2), поля проверяются по `rows[i]`. 6/6 (mock-session). **Anti-regression:** тронута только `_save_yandex_history_items`, SQL/колонки не менялись, соседние функции не тронуты (#826 «не трогать соседние» соблюдён). Нет конфликта с уже-смерженным #811 (тот — `_fetch_analogs`, другая функция). Секретов/инъекции нет (bind-params). Мержу. #826 → qa.
bot-reviewer merged commit fd75f4d47a into main 2026-05-30 20:07:06 +00:00
bot-reviewer deleted branch feat/826-yandex-executemany 2026-05-30 20:07:06 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 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#832
No description provided.