Files
healthlog/docs/backlog/cena-sliyaniya-na-shirokoj-dostavke.md
T
av 63bffe2865 Приём отвечает 200 до свёртки, свёртку ведёт фоновый воркер
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`,
  канал несёт только бит «есть работа». Переполнять нечего, падение процесса
  очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а
  не отдельный код. Классификация исхода общая с пересборкой журнала.
- Исход разбора начал отражать доставку, а не обстоятельства: отмена и
  занятость базы статус не меняют (иначе конкуренция за базу выводила бы
  доставку из очереди навсегда), паника свёртки больше не валит процесс, а
  учёт доставки идёт через транзакцию с повторами.
- Длинный бюджет ответа выдан маршруту приёма, а не всему серверу:
  `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с
  остальных маршрутов.
2026-08-02 11:01:42 +03:00

4.1 KiB

Цена слияния на широкой доставке

Приоритет: средний

Вынуто ревью кода задачи pravilo-sliyaniya-tochek (профиль deep, враждебный проход и независимая реализация — независимо друг от друга).

Оракул: измерено

Тело 63 МБ (в запросе ~200 КБ gzip — предел приёма 64 МиБ), 119 точек на ОДНОЙ координате:

транзакция держалась 5.149 с, аллоцировано 6108 МБ, пик кучи 189 МБ

При busy_timeout 5000 мс и _txlock=immediate параллельная доставка получает SQLITE_BUSY, inTx повторяет её до пяти раз (каждый повтор — полный пересчёт слияния), и на исчерпании попыток доставка уходит в parse_status=failed. Тело при этом в архиве остаётся, но пересборки, которая его подберёт, ещё нет.

Отдельный вклад, измеренный бенчмарком на часовом объекте нижнего слоя (3 600 точек):

HashAll на объект   9.46 мс   6.7 МБ аллокаций
Form одной точки    2.2 мкс

hashPoints пересчитывает каноническую форму ВСЕХ точек часа на каждое слияние, включая неизменившиеся. Утверждение docs/architecture.md о дешевизне глубокого прохода («4400 сравнений хеша, почти все сойдутся») стоимости самих сравнений не учитывает: чтобы сравнить хеш, его надо посчитать.

Часть цены задачей pravilo-sliyaniya-tochek уже снята: isEmpty больше не материализует значение (было 410 мс на точку 16.5 МБ), а разбор точки переиспользуется через canon.Fields. Осталось структурное.

Что делать

  1. Держать хеш каждой точки в payload рядом с координатами (storedPoint уже есть) и пересчитывать форму только для новых и выигравших столкновение — стоимость станет пропорциональна дельте, а не объёму часа.
  2. Считать canon.Form точки один раз на столкновение и переиспользовать в Equal, Relate и порядке вместо трёх независимых разборов.
  3. Проверять ctx.Err() в цикле по точкам: сейчас дедлайн свёртки неисполним, прервать слияние нечем.
  4. Оценить потолок числа кандидатов на одной координате: выбор победителя квадратичен по ним, и 119 точек на одну метку — вход, который приём принимает.

Связано

  • Разнесение ответа приёма и свёртки сделано (архив change 2026-08-02-otvet-i-svyortka): воркер убрал влияние на время ответа, но не на блокировку записи — длинная транзакция слияния держит её по-прежнему. Заодно оттуда взято главное смягчение: занятость базы больше не выводит доставку из очереди, она остаётся pending и пересворачивается. Оракул окна — task verify:busy.
  • Пересборка (healthlog reindex) подбирает доставки, ушедшие в failed по другим причинам.