- половина потока (50 доставок из 104) не несёт metrics вовсе и до сих пор числилась parsed: ретеншен, поверив статусу, срезал бы тела stateOfMind, которых в экспорте Apple нет - разбор перечисляет верхнеуровневые ключи data, непокрытые проглатываются декодированием: тело 40 МиБ из непокрытой секции удерживает 0 МиБ - статус partial и колонка delivery.uncovered_sections; миграция переводит прежние parsed в pending — им верить нельзя - витрина не изменилась: отпечаток совпал с прогоном до изменения
7.3 KiB
ADDED Requirements
Requirement: Перечисление непокрытых секций доставки
Разбор SHALL перечислять верхнеуровневые ключи объекта data и возвращать
вызывающему те из них, которые он не покрывает. Содержимое непокрытой секции
MUST NOT удерживаться после того, как разбор прошёл мимо неё: тела доходят до
42 МиБ, и удержание кучи здесь — часть контракта, а не деталь реализации.
Покрытым сегодня является ровно один ключ — metrics. Разбор и перечисление
MUST ходить по одному объявленному множеству покрытых имён: состояние «секция
разбирается, но числится непокрытой» невыразимо по построению.
Непокрытым ключ считается независимо от того, что лежит внутри: содержимое не интерпретируется, поэтому и о пустоте секции разбор честно ничего не знает. Измерено на живом архиве — пустых секций HAE не присылает ни разу (99 доставок).
Список SHALL быть каноничен: имена отсортированы, повторов нет. Порядок ключей в JSON от HAE нестабилен, а значение уезжает в базу и сравнивается между доставками.
Отсутствие непокрытых ключей и отсутствие секции metrics — разные события, и
оба нормальны: половина потока состоит из доставок без метрик вовсе (48 из 99).
Scenario: Незнакомая секция попадает в список непокрытых
- WHEN тело содержит
data.workoutsнаряду сdata.metrics - THEN разбор возвращает
workoutsв списке непокрытых ключей - AND точки секции
metricsразбираются как обычно
Scenario: Доставка без метрик разбирается и не теряется
- WHEN тело содержит только
data.stateOfMind - THEN разбор завершается без ошибки, точек нет
- AND
stateOfMindвозвращается в списке непокрытых ключей
Scenario: Доставка из одних метрик непокрытых ключей не даёт
- WHEN единственный ключ
data—metrics - THEN список непокрытых ключей пуст
Scenario: Один и тот же набор секций даёт один и тот же список
- WHEN два тела несут те же секции в разном порядке, а одно из них повторяет непокрытый ключ дважды
- THEN списки непокрытых ключей у них совпадают
Scenario: Содержимое непокрытой секции не удерживается в памяти
- WHEN тело в десятки мегабайт состоит преимущественно из непокрытой секции
- THEN после разбора удержано не больше четырёх размеров тела — та же граница, что и для тела из метрик
- AND содержимое непокрытой секции в результат разбора не попадает
Requirement: Границы списка непокрытых секций
Список непокрытых ключей MUST быть ограничен — не больше 32 имён и не больше 64 байт на имя: имена приходят из тела, которым отправитель управляет целиком. Срабатывание любой из границ MUST быть видно вызывающему — молчаливое усечение превратило бы список в уверенный, но неполный ответ на вопрос «что останется потерянным, если тело удалить».
Число имён сверх предела отдаётся счётчиком. Имя длиннее предела обрезается по
границе рун, к обрезанному приписывается маркер … — сверх предела, а не внутри
него. Обрезка не инъективна, поэтому обрезанное имя сравнению со словарём
известных секций не подлежит.
Предел длины считается по байтам декодированного имени: escape- последовательности JSON к этому моменту уже разобраны.
Scenario: Ключей больше предела
- WHEN объект
dataсодержит 40 непокрытых ключей - THEN список содержит 32 имени
- AND число отброшенных имён отдано отдельным счётчиком
Scenario: Имя ключа длиннее предела
- WHEN непокрытый ключ длиннее 64 байт
- THEN в списке лежит имя, обрезанное по границе рун, с маркером
…
Requirement: Отказ разбора остаётся всё или ничего
Разбор SHALL оставаться операцией «всё или ничего»: ошибка, встреченная
после того, как секция metrics уже разобрана (обрезанное тело, мусор в
следующем члене), MUST NOT оставлять точки в результате — доставка считается
неразобранной целиком.
Иначе часть точек оказалась бы в витрине под статусом, по которому доставку никто не подберёт, и свёртка перестала бы быть детерминированной по журналу.
Повтор ключа metrics в одном объекте data SHALL давать объединение секций, а
не победу последней: молча терять точки нельзя.
Scenario: Тело оборвано после секции метрик
- WHEN тело содержит целую секцию
metrics, а следующий членdataоборван - THEN разбор завершается ошибкой и точек не отдаёт
Scenario: Секция метрик встречается дважды
- WHEN объект
dataсодержит два ключаmetrics - THEN точки обеих секций попадают в результат