Files
healthlog/docs/tasks/items/raw-archive-retention.md
T
avandClaude Opus 5 a53d0f0f2f задачи: мета переехала в блок, поле «Хук» стало «Зачем»
49 файлов, миграция сделана командой tasks.py check --fix — той самой, ради
которой в скрипте оставлена читаемость старой формы. Побочно тот же прогон
проставил тег decomposed целям, у которых есть задачи: это его штатная работа.

check после миграции зелёный, индексы согласованы.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 17:31:29 +03:00

6.7 KiB

Ретеншен сырого архива

  • Секция: инфра
  • Зачем: Архив не подчищается вовсе — 14-дневный срок объявлен, но не работает
  • Теги: goal:journal-and-rebuild

Срок жизни сырого архива объявлен (14 дней, storage.raw_retention), но удаления нет: архив растёт бесконечно. Пока это 16 МБ и проблемой не является.

Само правило изменилось. Экспорт Apple — снапшот всей истории, доставки после его даты — события поверх снапшота, и состояние всегда пересобираемо свёрткой. Значит доставки должны жить до следующего проверенного экспорта, а не фиксированные две недели: иначе между концом ретеншена и датой снапшота образуется дыра в журнале, и пересобрать этот отрезок будет нечем.

Цена измерена: ~23 МБ архива в сутки, то есть ~2 ГБ за квартал между экспортами. Дёшево за возможность пересобрать что угодно.

Отдельное исключение: stateOfMind в экспорт не попадает вовсе (проверено на свежем архиве). Для него доставки — не хвост журнала, а единственный источник, и под общее правило удаления он не подпадает.

Включать после того, как разбор устоится и пересборка докажет, что хранилище действительно восстанавливается: иначе страховка исчезнет раньше, чем перестанет быть нужна.

Готово, когда удаляются только доставки старше последнего проверенного экспорта, записи stateOfMind не трогаются вовсе, а /stats показывает глубину архива и дату снапшота, до которой он подрезан.

Предусловие снова открыто

Признак «доставка с непокрытой секцией» появился в change 2026-08-01-nerazobrannye-sekcii-dostavki и работал заодно защитой stateOfMind: такие доставки числились partial, и ретеншен их не тронул бы.

Change 2026-08-02-trenirovki-i-zapisi покрыл stateOfMind разбором, и защита исчезла: доставка из одного состояния разума теперь получает parsed с пустым списком непокрытых, то есть побайтово неотличима от доставки из метрик — а метрики восстановимы из экспорта Apple, состояние разума нет (находка 46). Ретеншен, написанный по правилу «удаляем всё, что не partial», сотрёт ровно те тела, которых в экспорте не существует, и первая же пересборка потеряет историю состояния разума навсегда.

Значит признак невосстановимости нужен не производный от «непокрытости». Варианты:

  • Перечень покрытых секций, которых нет в экспорте Apple рядом с доставкой (сегодня — ровно stateOfMind). Цена: колонка и строка в свёртке; читается так же, как uncovered_sections, и одним запросом.
  • Признак у доставки «тело — единственный источник», выставляемый разбором. Цена та же, но смысл шире и требует решения, что считать единственным источником для будущих секций.
  • Никогда не подрезать тела доставок, у которых есть строки в record. Цена нулевая по схеме, но неточная: провенанс записи указывает на доставку её текущей версии, а копий у записи бывает по 26.

Рекомендация — первый вариант: он прямо отвечает на вопрос «что останется потерянным», как это уже делает uncovered_sections, и не требует додумывать семантику.

Вместе с этим действует правило: задача, которая начинает разбирать секцию, тем же изменением переводит partial-строки с этим ключом в pending (так сделала миграция 00007). Ретеншену позволено смотреть на partial только пока правило соблюдается.

Что читать перед удалением тела

Две колонки учётной записи, и обе обязательны:

  • uncovered_sections — непустой список означает, что в теле есть секции, которых разбор не покрывает; удалять нельзя;
  • skipped_entities — число сущностей с собственным id, которые разбор не понял. NULL означает «не измерялось» и нулю не равен: так выглядят доставки, свёрнутые разбором, который пропусков не считал, и те, чей разбор не досчитал. NULL — «не удалять». Прочитать его как ноль значит удалить тело тренировки, маршрута которой нет больше нигде: в экспорте Apple его не существует.

Правило пришло из задачи «Дозакрыть находки ревью по слиянию сущностей» (миграция 00008), где колонка и заведена — без DEFAULT именно ради этого различия.