Files
healthlog/docs/tasks/items/entity-size-limits.md
T
av 3d24248075 docs: документация приведена к канону av-dev-pm 4
- каждая запись каталога задач получила тип вместо тега kind: и префикса
  заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок
  секций канонический
- поправлены протухшие факты: нереализованные маршруты Read API, MCP и
  `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию
  диффа, периметр перестал дублировать security.md
- замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек
  storage и parsing
2026-08-05 19:09:35 +03:00

6.2 KiB
Raw Blame History

Ограничить размер сущности и считать форму потоково

  • Тип: feature
  • Категория: Ядро
  • Зачем: Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
  • Теги: goal:limits-and-load

Остаток задачи «Дозакрыть находки ревью по слиянию сущностей» (архивный change dozakryt-nahodki-sushchnostej). Та задача убрала канонизацию приехавшей сущности из транзакции и перестала считать каноническую форму дважды. Осталось структурное: предела на размер одной сущности нет вовсе, а форма и хеш считаются материализацией значения целиком.

Двигает строку «Завершения» цели: «У тела, сущности и секции доставки есть названный предел».

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

Оракулы жили в tmp/adv/mem_test.go и tmp/adv/lock_test.go; числа снимались на теле в пределах приёма (64 МиБ):

тело 40 МиБ → пик HeapAlloc 768.3 МиБ
тело 63 МиБ → повторная доставка держит блокировку 5.019 с при busy_timeout 5000

При _txlock=immediate конкурентный CreateDelivery получает SQLITE_BUSY, inTx повторяет до пяти раз и на исчерпании отдаёт store.ErrBusy — приём отвечает 500 по доставке, тело которой уже в архиве. Осиротевшее тело подберёт reindex, но узнать о нём можно только из лога.

Что делать

  1. Предел на размер одной сущности и на суммарный размер секции, отдельно от предела тела (64 МиБ). Сегодня одна тренировка законно может занять всё тело целиком. Вход, превышающий предел, обязан отклоняться до канонизации, а не после.
  2. Потоковый расчёт канонической формы и хеша: canon.Form разворачивает значение в дерево any, из-за чего пик кучи кратен размеру входа (замер даёт множитель около 19×). Хеш считается по потоку; форма нужна целиком только для сравнения, и только когда хеш разошёлся.
  3. Разбор сохранённой версии всё ещё идёт внутри транзакции: её содержимое читается оттуда же. Убрать это можно оптимистичным чтением до транзакции — но только с перепроверкой хеша и провенанса внутри транзакции, иначе две конкурентные свёртки одного id дадут потерянное обновление и исход снова станет функцией порядка коммитов, а не журнала.

Условия, пришедшие из закрывающей задачи

  1. Мягкое чтение заголовка сущности увеличило долю тел, доходящих до канонизации: сущность, которая раньше отсекалась на json.Unmarshal заголовка почти бесплатно, теперь разбирается и канонизируется целиком. То есть худший случай по памяти стал достижим на входах, которые до него не доходили, — предел из пункта 1 после этого обязателен, а не желателен.

  2. Каноническая форма и множества ключей всех версий доставки теперь удерживаются до конца транзакции слияния (раньше считались лениво и на одной доставке из сорока четырёх). Расход стал пропорционален размеру ДОСТАВКИ, а не самой большой её сущности; предел обязан считать суммарный размер секции, а не только одной сущности.

  3. Потолок на число версий одного ключа в одной доставке. Выбор победителя квадратичен по числу кандидатов; версии с совпавшей канонической формой схлопываются, но различных тело вмещает сколько угодно. Отмена цикл прерывает (дедлайн свёртки снова работает), но доставка при этом уходит в failed — то есть отравленное тело стоит полного дедлайна воркера. Тот же вопрос открыт для точек на одной координате: merge-cost-wide-delivery.md, пункт 4.

Связано

  • Цена слияния на широкой доставке — та же плата со стороны точек (hashPoints пересчитывает форму всех точек часа). Задачи делать вместе: половина решения общая — canon.
  • Из того же ревью: «хеш без полного прохода по содержимому не посчитать» — отброшено как предел по конструкции, но условием ложится сюда.