AURORA-1850: правка ячейки пересчитывает только затронутое поддерево #244

Open
aep wants to merge 20 commits from feat/235-aurora-1850-upd-form-cells-subtree into feat/215-aurora-1844-upd-form-cells-full-rows
Owner

Closes #235. Jira: AURORA-1850 (эпик AURORA-1834). Стек: база feat/215-aurora-1844-upd-form-cells-full-rows (PR #231), не test.

Что сделано

Миграция 0051_upd_form_cells_subtree: v3._form_affected_rows для FORM_1 и FORM_2 больше не собирает лист целиком.

  • Построители v_form{1,2}_sheet_sections -> *_sheet_jsonb -> v_form2_view / новая v_form1_view получили последним параметром p_focus_lines integer[] DEFAULT NULL (NULL — весь лист, '{}' — пустой результат).
  • sort_order считает построитель по лёгкому скелету листа (_sort_rank), одинаково для GET и правки; tie-break D6 (узел раньше строки; для OTCH9F id после (expense_item_id, month)).
  • _form_affected_rows (STABLE) и дельта АХР (get_form2_ahr_satellite_delta) вызывают построители с фокусом; дельта теперь включает предков SUB_SUB_ITEM.
  • SET jit = off на v_form2_sheet_sections, v_form1_sheet_sections, _form_affected_rows, дельте.
  • api/src, web/, perf/ не менялись. before-0051.sql — тела 8 функций ревизии 0050 (база ревью и отката).

Тесты

  • PG16 (perf-pg), чистая БД, pytest --init_db, весь набор: 3 failed (исходные чужие: test_backend_calls_form3_add_project_and_add_line_functions, два в test_export_service_query_params.py), 252 passed, 4 skipped (те же 4, что на базе; в новых файлах skip нет). Затем файлы feature после финальных правок: 54 passed, 0 skipped.
  • PG15.12 (dfip-pg15, ветка + исправление AURORA-1922, pytest --init_db, весь набор), HEAD b5a245e: 3 failed (те же 3), 256 passed, 4 skipped. Миграция проходит с нуля.
  • Снимок GET всех листов FORM_1/FORM_2 до/после 0051 (5840 строк, засеянная БД): различаются только 4 строки OTCH9F формы 26 (две пары с равным (expense_item_id, month), D6: tie-break по id); содержимое одинаково.
  • Эквивалентность: ответ на правку = эталон 0050 поверх полного v_form_view, побайтно data::text, по каждой строке листа, оба пути (_form_affected_rows и построитель с фокусом). 0 вложенных v_form_view на пути правки (track_functions). Защита 0037.

Проверить руками

  • Правка на FORM_2/AHR и FORM_1/AHR у двух соавторов: сумма предка обновилась у второго, соседние строки не затронуты.
  • Правка аренды обновляет АХР.

Замер до/после

Добавит контроллер после прогона на стенде (design.md, «Замер до/после», шаги 1–7). На тестовых данных GET AHR 105 -> ~114 мс (+8 %), первая реализация (+93 %) отвергнута.

Rulings I made

  • R-1/R-10: скелет сателлитов сохраняет те же JOIN (budget_line, vsp), что GET; vsp_id NOT NULL + FK доказано, JOIN оставлен.
  • R-2: роль perf — суперпользователь, тест «0 вложенных v_form_view» не пропускается (assert rolsuper). Цена: на не-суперпользовательской роли тест падает.
  • R-3: БД dfip_1850_base, dfip_1850_snap (и временные dfip_1850_*) одобрены.
  • R-4/R-6: before-0051.sql в каталоге change.
  • R-5, R-7, R-8, R-9, R-11, R-12, R-13: решения плана (шаблон БД, локальный снимок GET, track_functions, сравнение построителя с эталоном, v_form1_view только AHR/CAP/OPER, раскладка тестов, регулярка для защиты 0037).
  • R-14: подпись коммитов Claude Opus 5.5 по плану (в части коммитов исполнителей — Haiku 5.5).
  • Ruling по T3.1: ранг скелета присоединяется внешним JOIN поверх UNION (вариант JOIN в ветках дал +93 % к GET AHR). Цена: хрупкость плана на других объёмах, ловит замер.
  • Ruling по T4.4/O-4: D6-перестановка 4 строк OTCH9F принята. Цена: фронт мог опираться на случайный порядок равных ключей.
  • Ruling по финальному ревью: M-1 (мёртвые легаси-ветки _form_affected_rows) не чистится ради отсутствия повторного прогона PG15 — deferred.
    Все решения без обсуждения — в design.md -> ## Decisions made without discussion.

Находки

Маркер Суть Статус
T1.1 избыточное условие join в snap_seed (локальный файл) deferred
T1.2 решение посева в design.md fixed (8a843b3)
T1.3 form_limit id 13,14,15 захардкожены rejected (по плану)
T1.4 / T10.1 трейлер Opus vs Sonnet/Haiku rejected (R-14, косметика)
T2.1–T2.9 guard-проверки, ROOT-проверки, positive control, Row4, lt_id==13 и др. fixed b1fbac2
T2.10 test 5 не проверяет below/удалённую строку deferred
T2.11 неиспользуемые переменные, import json внутри функций deferred
T2.12 test 3 без положительного контроля deferred
T2.13 мёртвая is_tree fixed 655a69c
T2.14 import json внутри функции deferred
T3.1 ранг через внешний JOIN, +8 % GET AHR fixed (design.md) / замер на стенде
T3.2 внутренний JOIN sk может молча терять строки rejected (вывод ⊆ скелета по построению)
T3.3 дубли узлов tw rejected (PK expense_item_form_type, unique index mv)
T3.4 v_focus_eids включает desc_ids предков rejected (по дизайну D1)
T3.5 доп. БД dfip_1850_snap2 rejected (префикс разрешён; удалена)
T4.1 OTCH9F focus EXISTS без budget_form_id deferred
T4.2 _limit_template_path_set в WHERE по строке deferred
T4.3 OTCH9F скелет сканирует форму rejected (D6)
T4.4 JOIN vsp оставлен, D6-перестановки OTCH9F fixed (design.md)
T5.1 v_form1_view без SET search_path rejected (единообразие)
T5.2 GET FORM_1 не замерен локально deferred (замер стенда)
T6.1 неиспользуемые переменные в _form_affected_rows см. M-1
T8.1 тест jit не фиксирует одну перегрузку rejected (лишняя перегрузка попадёт в missing)
T8.2 будущая миграция без SET сбросит jit=off rejected (вне объёма)
T9.1 next() без сообщения deferred
T9.2 ожидание 6213 зависит от seed rejected (по плану)
T9.3 тест 2 не привязывает INPUT к lt_id rejected (усилено проверкой исходника)
I-1 test_sort_key_tie не создавал коллизию fixed 655a69c
M-1 мёртвые легаси-ветки и v_lc_path в _form_affected_rows deferred
M-2 устаревший заголовок 0051 fixed 655a69c
M-3 тест track_functions требует rolsuper, строка design.md про skip устарела deferred (по R-2; design.md дополнен)
M-4 sections покрыт только FORM_2/AHR fixed 655a69c
M-5 риск при rebase литеральных тел функций deferred (при rebase: pg_get_functiondef, diff с before-0051.sql, снимок GET)
O-1 подпись коммитов (см. R-14) rejected
O-2 тесты на 0050 идут ~10 мин (2 с на v_form_view) rejected (информация)
O-3 грязная БД замедляет прогон в 30 раз (JIT, оценки 2.7M); reset_db перед каждым прогоном rejected (подтверждает jit=off)
O-4 снимок GET: 4 строки OTCH9F (D6) rejected (допустимо по D6)

Deferred: T1.1, T2.10, T2.11, T2.12, T2.14, T4.1, T4.2, T5.2, T9.1, M-1, M-3, M-5.

🤖 Generated with Claude Code

Closes #235. Jira: AURORA-1850 (эпик AURORA-1834). Стек: база feat/215-aurora-1844-upd-form-cells-full-rows (PR #231), не `test`. ## Что сделано Миграция `0051_upd_form_cells_subtree`: `v3._form_affected_rows` для FORM_1 и FORM_2 больше не собирает лист целиком. - Построители `v_form{1,2}_sheet_sections` -> `*_sheet_jsonb` -> `v_form2_view` / новая `v_form1_view` получили последним параметром `p_focus_lines integer[] DEFAULT NULL` (NULL — весь лист, `'{}'` — пустой результат). - `sort_order` считает построитель по лёгкому скелету листа (`_sort_rank`), одинаково для GET и правки; tie-break D6 (узел раньше строки; для OTCH9F `id` после `(expense_item_id, month)`). - `_form_affected_rows` (STABLE) и дельта АХР (`get_form2_ahr_satellite_delta`) вызывают построители с фокусом; дельта теперь включает предков SUB_SUB_ITEM. - `SET jit = off` на `v_form2_sheet_sections`, `v_form1_sheet_sections`, `_form_affected_rows`, дельте. - `api/src`, `web/`, `perf/` не менялись. `before-0051.sql` — тела 8 функций ревизии 0050 (база ревью и отката). ## Тесты - PG16 (perf-pg), чистая БД, `pytest --init_db`, весь набор: 3 failed (исходные чужие: `test_backend_calls_form3_add_project_and_add_line_functions`, два в `test_export_service_query_params.py`), 252 passed, 4 skipped (те же 4, что на базе; в новых файлах skip нет). Затем файлы feature после финальных правок: 54 passed, 0 skipped. - PG15.12 (dfip-pg15, ветка + исправление AURORA-1922, `pytest --init_db`, весь набор), HEAD b5a245e: 3 failed (те же 3), 256 passed, 4 skipped. Миграция проходит с нуля. - Снимок GET всех листов FORM_1/FORM_2 до/после 0051 (5840 строк, засеянная БД): различаются только 4 строки OTCH9F формы 26 (две пары с равным `(expense_item_id, month)`, D6: tie-break по `id`); содержимое одинаково. - Эквивалентность: ответ на правку = эталон 0050 поверх полного `v_form_view`, побайтно `data::text`, по каждой строке листа, оба пути (`_form_affected_rows` и построитель с фокусом). 0 вложенных `v_form_view` на пути правки (`track_functions`). Защита 0037. ## Проверить руками - Правка на FORM_2/AHR и FORM_1/AHR у двух соавторов: сумма предка обновилась у второго, соседние строки не затронуты. - Правка аренды обновляет АХР. ## Замер до/после Добавит контроллер после прогона на стенде (design.md, «Замер до/после», шаги 1–7). На тестовых данных GET AHR 105 -> ~114 мс (+8 %), первая реализация (+93 %) отвергнута. ## Rulings I made - R-1/R-10: скелет сателлитов сохраняет те же JOIN (`budget_line`, `vsp`), что GET; `vsp_id` NOT NULL + FK доказано, JOIN оставлен. - R-2: роль perf — суперпользователь, тест «0 вложенных v_form_view» не пропускается (assert rolsuper). Цена: на не-суперпользовательской роли тест падает. - R-3: БД `dfip_1850_base`, `dfip_1850_snap` (и временные `dfip_1850_*`) одобрены. - R-4/R-6: `before-0051.sql` в каталоге change. - R-5, R-7, R-8, R-9, R-11, R-12, R-13: решения плана (шаблон БД, локальный снимок GET, track_functions, сравнение построителя с эталоном, `v_form1_view` только AHR/CAP/OPER, раскладка тестов, регулярка для защиты 0037). - R-14: подпись коммитов `Claude Opus 5.5` по плану (в части коммитов исполнителей — Haiku 5.5). - Ruling по T3.1: ранг скелета присоединяется внешним JOIN поверх UNION (вариант JOIN в ветках дал +93 % к GET AHR). Цена: хрупкость плана на других объёмах, ловит замер. - Ruling по T4.4/O-4: D6-перестановка 4 строк OTCH9F принята. Цена: фронт мог опираться на случайный порядок равных ключей. - Ruling по финальному ревью: M-1 (мёртвые легаси-ветки `_form_affected_rows`) не чистится ради отсутствия повторного прогона PG15 — deferred. Все решения без обсуждения — в design.md -> `## Decisions made without discussion`. ## Находки | Маркер | Суть | Статус | |---|---|---| | T1.1 | избыточное условие join в snap_seed (локальный файл) | deferred | | T1.2 | решение посева в design.md | fixed (8a843b3) | | T1.3 | form_limit id 13,14,15 захардкожены | rejected (по плану) | | T1.4 / T10.1 | трейлер Opus vs Sonnet/Haiku | rejected (R-14, косметика) | | T2.1–T2.9 | guard-проверки, ROOT-проверки, positive control, Row4, lt_id==13 и др. | fixed b1fbac2 | | T2.10 | test 5 не проверяет below/удалённую строку | deferred | | T2.11 | неиспользуемые переменные, import json внутри функций | deferred | | T2.12 | test 3 без положительного контроля | deferred | | T2.13 | мёртвая is_tree | fixed 655a69c | | T2.14 | import json внутри функции | deferred | | T3.1 | ранг через внешний JOIN, +8 % GET AHR | fixed (design.md) / замер на стенде | | T3.2 | внутренний JOIN sk может молча терять строки | rejected (вывод ⊆ скелета по построению) | | T3.3 | дубли узлов tw | rejected (PK expense_item_form_type, unique index mv) | | T3.4 | v_focus_eids включает desc_ids предков | rejected (по дизайну D1) | | T3.5 | доп. БД dfip_1850_snap2 | rejected (префикс разрешён; удалена) | | T4.1 | OTCH9F focus EXISTS без budget_form_id | deferred | | T4.2 | _limit_template_path_set в WHERE по строке | deferred | | T4.3 | OTCH9F скелет сканирует форму | rejected (D6) | | T4.4 | JOIN vsp оставлен, D6-перестановки OTCH9F | fixed (design.md) | | T5.1 | v_form1_view без SET search_path | rejected (единообразие) | | T5.2 | GET FORM_1 не замерен локально | deferred (замер стенда) | | T6.1 | неиспользуемые переменные в _form_affected_rows | см. M-1 | | T8.1 | тест jit не фиксирует одну перегрузку | rejected (лишняя перегрузка попадёт в missing) | | T8.2 | будущая миграция без SET сбросит jit=off | rejected (вне объёма) | | T9.1 | next() без сообщения | deferred | | T9.2 | ожидание 6213 зависит от seed | rejected (по плану) | | T9.3 | тест 2 не привязывает INPUT к lt_id | rejected (усилено проверкой исходника) | | I-1 | test_sort_key_tie не создавал коллизию | fixed 655a69c | | M-1 | мёртвые легаси-ветки и v_lc_path в _form_affected_rows | deferred | | M-2 | устаревший заголовок 0051 | fixed 655a69c | | M-3 | тест track_functions требует rolsuper, строка design.md про skip устарела | deferred (по R-2; design.md дополнен) | | M-4 | sections покрыт только FORM_2/AHR | fixed 655a69c | | M-5 | риск при rebase литеральных тел функций | deferred (при rebase: pg_get_functiondef, diff с before-0051.sql, снимок GET) | | O-1 | подпись коммитов (см. R-14) | rejected | | O-2 | тесты на 0050 идут ~10 мин (2 с на v_form_view) | rejected (информация) | | O-3 | грязная БД замедляет прогон в 30 раз (JIT, оценки 2.7M); reset_db перед каждым прогоном | rejected (подтверждает jit=off) | | O-4 | снимок GET: 4 строки OTCH9F (D6) | rejected (допустимо по D6) | Deferred: T1.1, T2.10, T2.11, T2.12, T2.14, T4.1, T4.2, T5.2, T9.1, M-1, M-3, M-5. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 20 commits 2026-10-28 23:36:22 +03:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
План в 10 задач по design.md: тестовая БД dfip_1850 на 0050 и тела функций
до 0051 (D8), красный тест эквивалентности, построители с фокусом и
_sort_rank (FORM_2, затем FORM_1 и v_form1_view), _form_affected_rows,
дельта АХР, jit = off, защита 0037, документация. Решения плана дописаны
в design.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Ночной замер на стенде perf/ (29.10, масштаб 1, CPU 1x, arm-ka-* остановлены) — часть 1 (1154 строки)

Сборки: «до» — 1844 f0cac30 (D0 + миграция 0050), «после» — этот PR b5a245e (та же БД + 0051). Перед замерами после восстановления дампа — ANALYZE.

Корректность (условие мержа) — выполнено

  • Шаг 3, снимок GET до/после 0051 (compare_responses.py dump на 1844 и на 1850, без правок между снимками): все 25 ответов листов FORM_1/FORM_2 (все owner, с direction, FORM_2/AHR c sections) совпали байт в байт (exact=25, перестановок 0); весь набор из 56 запросов (листы FORM_1/2/4, своды 1/2, сводные, отчёты проектов) тоже exact=56. OTCH9F с совпадающим ключом на стенде перестановок не дал. Примечание: первый проход я снял с coauthor-прогоном между снимками — данные менялись, расхождение было артефактом методики, переснято отдельно (snap2-*).
  • Шаг 4, SQL-эквивалентность _form_affected_rows против эталона из sheet_contract.py (фильтр 0050 по v_form_view, побайтно data::text), только чтение: FORM_2/AHR 50 случайных строк + 2 пакета (5 строк, sections NULL и plan,q1), FORM_2 CAP/OPER/AHR_RENT и FORM_1/AHR по 20 + 2 пакета — 140 из 140 совпали (EQ_OK).

Скорость, шаг 6 (coauthor FORM_2/AHR, rate 1 и 5, 3 повтора; sql/coauthor/sql_top.json)

метрика 1844 (до) 1850 (после) критерий
mean upd_form_cell 310.9 мс (695 вызовов) 31.0 мс (757) ≤ 50 — выполнен
вложенные v_form_view (toplevel=false) 695 (по одному на правку) 0 0 — выполнен
v_form2_view(p_form_id, p_sheet, p_sections) calls 707 12 = числу GET листа (12) = GET — выполнен
rate 5: (echoed+errors)/sent (200+49)/262 = 0.95 (224+39)/263 = 1.00 ≥ 0.95 — выполнен
main_thread_ms_per_step автора, rate 1 / rate 5 92.9 / 107.2 90.8 / 107.5 справочно

Шаг 5, GET без регресса — НЕ выполнен (цель, не условие мержа)

psql/asyncpg, одно соединение, 5 прогревов, 20 чередований count(*) v_form_view(7,'AHR',NULL,NULL) и upd_form_cell в транзакции с ROLLBACK (tools/get_vs_upd.py), два прогона на сторону после ANALYZE:

сборка GET mean / median, мс upd_form_cell mean, мс
test 0049 223.7 / 221.4 464.5
1844 (0050) 226.4 / 222.3; 227.7 / 226.3 226.7; 228.6
1850 (0051) 264.6 / 262.8; 261.0 / 258.5 18.2; 17.9

GET FORM_2/AHR на 1850 медленнее 1844 на +16 % (порог +10 %). Через API это тоже видно: 12 GET листа в coauthor-прогоне 349 мс (1850) — но там шла нагрузка правками, сравнивать по psql-цифрам.
Диагностика без изменения кода (в откатываемой транзакции, tools/get_jit_probe.py): как есть (jit=off на v_form2_sheet_sections) — медиана 278; ALTER FUNCTION ... RESET jit (сервер jit=on) — медиана 262, но среднее 775 (выбросы компиляции JIT); сессия jit=off — 263. JIT не причина регресса GET; риски из design (force_custom_plan, снятие jit=off с построителей) я не применял — код PR не правлю. Гипотеза (не проверена): лишняя цена — ранжирование скелета _sort_rank в полном GET. Решение — за человеком.

Шаг 7 (2092 строки после grow) — допишу частью 2.

Файлы: /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1850/ (snap2-1844, snap2-1850, snap2-compare*.txt, eq.txt, getupd-.txt, jit-probe.txt, api--co.log); прогоны perf/results/n-p1844-co, n-p1850-co в worktree perf-bench.

🤖 Generated with Claude Code

## Ночной замер на стенде perf/ (29.10, масштаб 1, CPU 1x, arm-ka-* остановлены) — часть 1 (1154 строки) **Сборки:** «до» — 1844 f0cac30 (D0 + миграция 0050), «после» — этот PR b5a245e (та же БД + 0051). Перед замерами после восстановления дампа — `ANALYZE`. ### Корректность (условие мержа) — выполнено - **Шаг 3, снимок GET до/после 0051** (`compare_responses.py dump` на 1844 и на 1850, без правок между снимками): все 25 ответов листов FORM_1/FORM_2 (все owner, с direction, FORM_2/AHR c sections) **совпали байт в байт** (exact=25, перестановок 0); весь набор из 56 запросов (листы FORM_1/2/4, своды 1/2, сводные, отчёты проектов) тоже exact=56. OTCH9F с совпадающим ключом на стенде перестановок не дал. Примечание: первый проход я снял с coauthor-прогоном между снимками — данные менялись, расхождение было артефактом методики, переснято отдельно (snap2-*). - **Шаг 4, SQL-эквивалентность** `_form_affected_rows` против эталона из `sheet_contract.py` (фильтр 0050 по `v_form_view`, побайтно `data::text`), только чтение: FORM_2/AHR 50 случайных строк + 2 пакета (5 строк, sections NULL и `plan,q1`), FORM_2 CAP/OPER/AHR_RENT и FORM_1/AHR по 20 + 2 пакета — **140 из 140 совпали** (EQ_OK). ### Скорость, шаг 6 (coauthor FORM_2/AHR, rate 1 и 5, 3 повтора; `sql/coauthor/sql_top.json`) | метрика | 1844 (до) | 1850 (после) | критерий | |---|---|---|---| | mean `upd_form_cell` | 310.9 мс (695 вызовов) | **31.0 мс** (757) | ≤ 50 — выполнен | | вложенные `v_form_view` (toplevel=false) | 695 (по одному на правку) | **0** | 0 — выполнен | | `v_form2_view(p_form_id, p_sheet, p_sections)` calls | 707 | **12** = числу GET листа (12) | = GET — выполнен | | rate 5: (echoed+errors)/sent | (200+49)/262 = 0.95 | (224+39)/263 = **1.00** | ≥ 0.95 — выполнен | | main_thread_ms_per_step автора, rate 1 / rate 5 | 92.9 / 107.2 | 90.8 / 107.5 | справочно | ### Шаг 5, GET без регресса — **НЕ выполнен** (цель, не условие мержа) psql/asyncpg, одно соединение, 5 прогревов, 20 чередований `count(*) v_form_view(7,'AHR',NULL,NULL)` и `upd_form_cell` в транзакции с ROLLBACK (`tools/get_vs_upd.py`), два прогона на сторону после ANALYZE: | сборка | GET mean / median, мс | upd_form_cell mean, мс | |---|---|---| | test 0049 | 223.7 / 221.4 | 464.5 | | 1844 (0050) | 226.4 / 222.3; 227.7 / 226.3 | 226.7; 228.6 | | 1850 (0051) | 264.6 / 262.8; 261.0 / 258.5 | 18.2; 17.9 | GET FORM_2/AHR на 1850 медленнее 1844 на **+16 %** (порог +10 %). Через API это тоже видно: 12 GET листа в coauthor-прогоне 349 мс (1850) — но там шла нагрузка правками, сравнивать по psql-цифрам. Диагностика без изменения кода (в откатываемой транзакции, `tools/get_jit_probe.py`): как есть (jit=off на `v_form2_sheet_sections`) — медиана 278; `ALTER FUNCTION ... RESET jit` (сервер jit=on) — медиана 262, но среднее 775 (выбросы компиляции JIT); сессия `jit=off` — 263. **JIT не причина** регресса GET; риски из design (force_custom_plan, снятие jit=off с построителей) я не применял — код PR не правлю. Гипотеза (не проверена): лишняя цена — ранжирование скелета `_sort_rank` в полном GET. Решение — за человеком. Шаг 7 (2092 строки после grow) — допишу частью 2. Файлы: `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1850/` (snap2-1844, snap2-1850, snap2-compare*.txt, eq.txt, getupd-*.txt, jit-probe.txt, api-*-co.log); прогоны `perf/results/n-p1844-co`, `n-p1850-co` в worktree perf-bench. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Ночной замер — часть 2 (итог)

Сборки как в части 1 («до» — 1844 f0cac30, «после» — b5a245e). Добавлено: уточнение шага 5 (GET) и шаг 7 (после gen.grow).

Шаг 5: GET листа, +16 %

Лист FORM_2/AHR (form 7, роль rf, 1154 строки), вызов count(*) FROM v3.v_form_view(7,'AHR',NULL,NULL), одно соединение, 5 прогревов + 20 чередований GET/upd_form_cell (в транзакции с ROLLBACK), после ANALYZE; 2 прогона на сторону (tools/get_vs_upd.py):

сборка прогон 1 mean / median, мс прогон 2 mean / median, мс среднее mean
test 0049 223.7 / 221.4 — 223.7
1844 (0050) 226.4 / 222.3 227.7 / 226.3 227.1
1850 (0051) 264.6 / 262.8 261.0 / 258.5 262.8
Δ 1850 к 1844: +15.8 % по среднему, +16.2 % по медиане; разброс внутри стороны ≤ 0,6 %, т.е. регресс устойчив (первичные прогоны 1850 дали 265.9 и 273.3 — тот же уровень). К test (0049) +17,5 %. JIT не причина (jit=off в сессии 263, RESET jit — медиана 262, среднее 775 из-за выбросов компиляции). Через API 12 GET листа под нагрузкой от правок: test 283.2, 1844 569.3, 1850 349.4 мс — не сопоставимы (правки 1844 по 311 мс занимают БД).
Порог ≤ +10 % — не выполнен. Гипотеза (не проверена): ранжирование скелета _sort_rank в полном GET.

Шаг 7: после grow (D1, FORM_2/AHR 2092 строки), coauthor rate 1 и 5, 3 повтора

Прогон завершился rc=1 (04:08): 2 из 6 повторов (#1 при rate 1 и rate 5) не стартовали — бот-соавтор вышел с кодом 2, сервер отказал в первых правках с RaiseError: computed_field: q3.actual_m2 / plan.q1 / seq_dfip.q4 / q3.actual_m1 / seq_ssp.q3 / seq_ssp.q1 (значение синхронизируется из сателлитного листа), HTTP 400: бот выбирает вычисляемые поля. Это дефект выбора ячеек ботом на выращенных данных, не регресс PR. Итог по 4 повторам (по 2 на rate):

(после grow) значение порог
mean upd_form_cell, 313 вызовов 38.1 мс (D0: 31.0) ≤ 50 — выполнен
вложенные v_form_view 0 выполнен
очередь rate 5: (echoed+errors)/sent (130.5 + 294.5) / 425 = 1.00 ≥ 0.95 — выполнен
rate 5: echoed / errors / sent 130.5 / 294.5 / 425 (errors 69 % — отказы computed_field; на D0 было 15–25 %) справочно
v_form2_view на GET листа, 12 вызовов под нагрузкой 613.9 мс (D0: 325.6) растёт ~пропорционально строкам (+81 %)
Не снято: GET 1844 против 1850 после grow (psql-сценарий на D1 не запускался); coauthor для 1844 на D1 (нет «до» для 38.1 мс на том же объёме); итог упавших повторов.

Вердикт по критериям Jira: upd ≤ 50 мс, nested 0, очередь ≥ 0.95 — выполнены (D0 и после grow); GET ≤ +10 % — НЕ выполнен (+15.8 %). Решение — за человеком.
Файлы: /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1850/ (getupd-*.txt, jit-probe.txt, api-1850-grow-co.log), logs/n-p1850-grow-co.log, прогон perf/results/n-p1850-grow-co/sql/coauthor/sql_top.json.

## Ночной замер — часть 2 (итог) Сборки как в части 1 («до» — 1844 f0cac30, «после» — b5a245e). Добавлено: уточнение шага 5 (GET) и шаг 7 (после `gen.grow`). ### Шаг 5: GET листа, +16 % Лист FORM_2/AHR (form 7, роль rf, 1154 строки), вызов `count(*) FROM v3.v_form_view(7,'AHR',NULL,NULL)`, одно соединение, 5 прогревов + 20 чередований GET/`upd_form_cell` (в транзакции с ROLLBACK), после ANALYZE; 2 прогона на сторону (`tools/get_vs_upd.py`): | сборка | прогон 1 mean / median, мс | прогон 2 mean / median, мс | среднее mean | |---|---|---|---| | test 0049 | 223.7 / 221.4 | — | 223.7 | | 1844 (0050) | 226.4 / 222.3 | 227.7 / 226.3 | 227.1 | | 1850 (0051) | 264.6 / 262.8 | 261.0 / 258.5 | 262.8 | **Δ 1850 к 1844: +15.8 % по среднему, +16.2 % по медиане**; разброс внутри стороны ≤ 0,6 %, т.е. регресс устойчив (первичные прогоны 1850 дали 265.9 и 273.3 — тот же уровень). К test (0049) +17,5 %. JIT не причина (jit=off в сессии 263, RESET jit — медиана 262, среднее 775 из-за выбросов компиляции). Через API 12 GET листа под нагрузкой от правок: test 283.2, 1844 569.3, 1850 349.4 мс — не сопоставимы (правки 1844 по 311 мс занимают БД). **Порог ≤ +10 % — не выполнен.** Гипотеза (не проверена): ранжирование скелета `_sort_rank` в полном GET. ### Шаг 7: после grow (D1, FORM_2/AHR 2092 строки), coauthor rate 1 и 5, 3 повтора Прогон завершился rc=1 (04:08): **2 из 6 повторов (#1 при rate 1 и rate 5) не стартовали** — бот-соавтор вышел с кодом 2, сервер отказал в первых правках с `RaiseError: computed_field: q3.actual_m2 / plan.q1 / seq_dfip.q4 / q3.actual_m1 / seq_ssp.q3 / seq_ssp.q1 (значение синхронизируется из сателлитного листа)`, HTTP 400: бот выбирает вычисляемые поля. Это дефект выбора ячеек ботом на выращенных данных, не регресс PR. Итог по 4 повторам (по 2 на rate): | (после grow) | значение | порог | |---|---|---| | mean `upd_form_cell`, 313 вызовов | **38.1 мс** (D0: 31.0) | ≤ 50 — **выполнен** | | вложенные `v_form_view` | 0 | выполнен | | очередь rate 5: (echoed+errors)/sent | (130.5 + 294.5) / 425 = **1.00** | ≥ 0.95 — **выполнен** | | rate 5: echoed / errors / sent | 130.5 / 294.5 / 425 (errors 69 % — отказы `computed_field`; на D0 было 15–25 %) | справочно | | `v_form2_view` на GET листа, 12 вызовов под нагрузкой | 613.9 мс (D0: 325.6) | растёт ~пропорционально строкам (+81 %) | **Не снято:** GET 1844 против 1850 после grow (psql-сценарий на D1 не запускался); coauthor для 1844 на D1 (нет «до» для 38.1 мс на том же объёме); итог упавших повторов. **Вердикт по критериям Jira:** upd ≤ 50 мс, nested 0, очередь ≥ 0.95 — **выполнены** (D0 и после grow); **GET ≤ +10 % — НЕ выполнен (+15.8 %)**. Решение — за человеком. Файлы: `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1850/` (getupd-*.txt, jit-probe.txt, api-1850-grow-co.log), `logs/n-p1850-grow-co.log`, прогон `perf/results/n-p1850-grow-co/sql/coauthor/sql_top.json`.
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/235-aurora-1850-upd-form-cells-subtree:feat/235-aurora-1850-upd-form-cells-subtree
git checkout feat/235-aurora-1850-upd-form-cells-subtree
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#244
No description provided.