Files
av 34e5109b6d непокрытые секции доставки видны в статусе разбора
- половина потока (50 доставок из 104) не несёт metrics вовсе и до сих пор
  числилась parsed: ретеншен, поверив статусу, срезал бы тела stateOfMind,
  которых в экспорте Apple нет
- разбор перечисляет верхнеуровневые ключи data, непокрытые проглатываются
  декодированием: тело 40 МиБ из непокрытой секции удерживает 0 МиБ
- статус partial и колонка delivery.uncovered_sections; миграция переводит
  прежние parsed в pending — им верить нельзя
- витрина не изменилась: отпечаток совпал с прогоном до изменения
2026-08-01 21:25:59 +03:00

7.0 KiB
Raw Permalink Blame History

1. Разбор: перечисление непокрытых ключей

  • 1.1 decodeMetricsdecodeEnvelope: один json.Decoder идёт по верхнему уровню, имя члена читается Token(), значение покрытого ключа декодируется на месте, значение непокрытого проглатывается декодированием в выбрасываемый json.RawMessage (ограничитель вложенности stdlib при этом работает, в отличие от ручного счёта глубины)
  • 1.2 Покрытость — функция рядом с разбором, а не изменяемая пакетная карта; Result.Uncovered отдаёт непокрытые отсортированными и без повторов
  • 1.3 Границы: не больше 32 имён (Result.UncoveredDropped считает отброшенные), имя длиннее 64 байт декодированного имени обрезано по границе рун, маркер приписывается сверх предела
  • 1.4 Повтор ключа metrics даёт объединение секций; повтор непокрытого ключа даёт одно имя. Тело без data, data не объект, data пустой, metrics неверного типа — прежнее поведение (ошибка ровно там, где была)
  • 1.5 Ошибка после разобранной секции metrics точек не отдаёт

2. Хранилище: статус и список

  • 2.1 Константа store.ParsePartial; док-комментарий ParsePending переписан на «этим разбором ещё не смотрели»
  • 2.2 Миграция: колонка uncovered_sections TEXT NOT NULL DEFAULT '[]' и перевод существующих parsed в pending; в комментарии миграции сказано, что по данным она односторонняя — Down снимает колонку, прежние статусы не восстанавливает
  • 2.3 Исход разбора пишется структурой (store.ParseOutcome), а не растущим списком позиционных параметров; список замещает прежнее значение целиком, включая замещение пустым; nil и пустой срез записываются как []

3. Свёртка: исход и лог

  • 3.1 Stats.Uncovered и Stats.UncoveredDropped; статус partial при непустом списке, failed сильнее; при отказе список сохраняется, если разбор успел его собрать
  • 3.2 Атрибуты uncovered (структурным []string) и uncovered_dropped в единственном логирующем чекпоинте; уровень из-за одной лишь частичности не растёт, срабатывание границ даёт WARN

4. Тесты (приёмочные критерии)

  • 4.1 Фикстура uncovered_sections.json: точки метрик сохранены, ключи workouts и stateOfMind в списке, статус partial
  • 4.2 Доставка из одной непокрытой секции: разбор без ошибки, ноль точек, ключ в списке, статус partial
  • 4.3 Доставка из одних метрик: список пуст, статус parsed
  • 4.4 Детерминизм: тот же набор секций в разном порядке и с повтором ключа даёт тот же список
  • 4.5 Границы: 40 ключей → 32 имени и счётчик отброшенных; длинное имя → обрезка с маркером
  • 4.6 Удержание кучи: тело в десятки мегабайт, состоящее преимущественно из непокрытой секции, удерживает не больше четырёх тел; тело из вложенных скобок отвергается, а не съедает память
  • 4.7 Отказ всё или ничего: тело оборвано после секции метрик — ошибка, ноль точек, failed
  • 4.8 Лог свёртки: имена ключей есть, содержимого секций нет; уровень при обычной частичности не повышен
  • 4.9 Миграция: строка со статусом parsed становится pending, колонка получает []
  • 4.10 Пересвёртка: доставка partial, у которой список опустел, становится parsed с пустым списком
  • 4.11 Прогон живого архива (task verify:archive): доставки без метрик получают partial с непустым списком, повторный прогон состояния не меняет
  • 4.12 Сверка с состоянием ДО изменения: витрина, собранная из живого архива новым кодом, совпадает по объектам, точкам и метрикам со снимком, снятым до изменения

5. Документация

  • 5.1 docs/database.md: колонка, полный набор статусов, новый смысл pending
  • 5.2 docs/architecture.md: частичный разбор в разделе приёма
  • 5.3 docs/local-research.md: находка о наборах секций в живом потоке (99 доставок: 51 metrics, 24 workouts, 24 stateOfMind, секции не смешиваются, пустых нет)

6. Замеры после реализации

  • 6.1 Живой архив (104 доставки): частично разобрано 50 — 25 stateOfMind и 25 workouts. Отпечаток витрины a59b38ea… совпал с прогоном ДО изменения на том же архиве: переписанный разбор конверта — строгий no-op для витрины
  • 6.2 Удержание кучи: тело 40 МиБ, из которых почти всё — непокрытая секция, удерживает 0 МиБ; тело из 100 000 уровней вложенности отвергается
  • 6.3 Живой сервис разобрал пришедшие с телефона доставки (5 штук) — заодно закрыт пункт 6.3a задачи razbor-metrik-v-obekty