Приём отвечает 200 до свёртки, свёртку ведёт фоновый воркер
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`, канал несёт только бит «есть работа». Переполнять нечего, падение процесса очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а не отдельный код. Классификация исхода общая с пересборкой журнала. - Исход разбора начал отражать доставку, а не обстоятельства: отмена и занятость базы статус не меняют (иначе конкуренция за базу выводила бы доставку из очереди навсегда), паника свёртки больше не валит процесс, а учёт доставки идёт через транзакцию с повторами. - Длинный бюджет ответа выдан маршруту приёма, а не всему серверу: `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с остальных маршрутов.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Учёт частично разобранной доставки
|
||||
|
||||
Система SHALL отличать доставку, разобранную целиком, от доставки, в теле
|
||||
которой остались непокрытые разбором секции. Доставка с непустым списком
|
||||
непокрытых ключей MUST получать статус `partial`, а не `parsed`.
|
||||
|
||||
Статусы разбора:
|
||||
|
||||
```
|
||||
pending этим разбором ещё не смотрели — или смотрели, но работа не сделана
|
||||
по обстоятельствам (см. ниже)
|
||||
parsed разобрано всё, что в теле было
|
||||
partial разобрано покрытое; в теле остались непокрытые секции
|
||||
failed разобрать не удалось, точек нет
|
||||
```
|
||||
|
||||
Источник истины — список непокрытых ключей; статус производен от него и от
|
||||
факта отказа, в порядке `failed` → `partial` → `parsed`. Приоритет назван явно,
|
||||
чтобы читатели (ретеншен, статистика) спрашивали статус, а не сравнивали список
|
||||
со строкой.
|
||||
|
||||
**Отказ обстоятельств статуса не меняет вовсе.** Отмена работы снаружи и
|
||||
занятость базы дольше повторов транзакции означают «не сделано», а не «не
|
||||
выходит»: доставка остаётся `pending` и будет свёрнута снова. Правило появилось
|
||||
не из аккуратности — фоновая свёртка `failed` не подбирает никогда, и без этого
|
||||
различения занятость базы (а с фоновой свёрткой конкуренция за неё штатная)
|
||||
выводила бы доставку из очереди навсегда. Различение живёт **в одном месте**:
|
||||
тот, кто пишет исход, и тот, кто классифицирует его в счётчики, спрашивают один
|
||||
предикат.
|
||||
|
||||
Дедлайн самой свёртки к обстоятельствам MUST NOT относиться: доставка, не
|
||||
уложившаяся в бюджет, не уложится в него и в следующий раз, а бесконечный повтор
|
||||
заведомо безнадёжного — это очередь, которая не движется.
|
||||
|
||||
Отказы, случившиеся **до** чтения тела (учётной записи нет, соседний запрос не
|
||||
прошёл), и отказ самой записи исхода статуса не меняют по другой причине —
|
||||
записать его нечем. Доставка остаётся `pending`, что честно: этим разбором её не
|
||||
досмотрели.
|
||||
|
||||
Список непокрытых ключей SHALL сохраняться рядом с доставкой — именами ключей,
|
||||
без содержимого секций. Он же ответ на вопрос «что останется потерянным, если
|
||||
тело удалить»: для `stateOfMind` доставки HAE единственный источник, в экспорте
|
||||
Apple его нет (находка 46). Поэтому список MUST сохраняться и при отказе
|
||||
разбора, если разбор успел его собрать: `failed` с непустым списком — законное
|
||||
состояние.
|
||||
|
||||
Запись списка MUST замещать прежнее значение целиком, включая замещение пустым:
|
||||
иначе доставка, все секции которой стали покрытыми, осталась бы `partial`
|
||||
навсегда.
|
||||
|
||||
Список — снимок покрытия **на момент свёртки**. Задача, которая начинает
|
||||
разбирать секцию, тем же изменением SHALL переводить `partial`-строки с этим
|
||||
ключом в `pending`; ретеншену позволено смотреть на `partial` только при
|
||||
соблюдении этого правила.
|
||||
|
||||
Статусы, поставленные разбором, который частичного исхода не различал, доверия
|
||||
не заслуживают: под `parsed` у них лежат и полностью разобранные доставки, и
|
||||
доставки без метрик вовсе. Такие строки MUST переводиться в `pending` — «этим
|
||||
разбором ещё не смотрели». Число точек у них до пересвёртки остаётся прежним: оно
|
||||
производно от объектов витрины, которые никуда не делись.
|
||||
|
||||
#### Scenario: Доставка с непокрытой секцией отмечается частичной
|
||||
|
||||
- **WHEN** разбор доставки вернул непустой список непокрытых ключей
|
||||
- **THEN** `parse_status` доставки равен `partial`
|
||||
- **AND** список непокрытых ключей сохранён вместе с доставкой
|
||||
- **AND** точки покрытой секции сохранены как обычно
|
||||
|
||||
#### Scenario: Доставка без непокрытых секций остаётся `parsed`
|
||||
|
||||
- **WHEN** разбор доставки не дал непокрытых ключей
|
||||
- **THEN** `parse_status` равен `parsed`
|
||||
- **AND** сохранённый список непокрытых ключей пуст
|
||||
|
||||
#### Scenario: Отказ разбора сильнее частичности
|
||||
|
||||
- **WHEN** разбор доставки завершился ошибкой в самом разборе или в записи
|
||||
точек
|
||||
- **THEN** `parse_status` равен `failed`
|
||||
- **AND** список непокрытых ключей сохранён, если разбор успел его собрать
|
||||
|
||||
#### Scenario: Занятая база доставку из очереди не выводит
|
||||
|
||||
- **WHEN** разбор не состоялся из-за занятости базы или отмены работы снаружи
|
||||
- **THEN** `parse_status` остаётся `pending`
|
||||
|
||||
#### Scenario: Пересвёртка после того, как секция стала покрытой
|
||||
|
||||
- **WHEN** доставка со статусом `partial` сворачивается повторно разбором,
|
||||
который эту секцию покрывает
|
||||
- **THEN** `parse_status` становится `parsed`
|
||||
- **AND** сохранённый список непокрытых ключей пуст
|
||||
Reference in New Issue
Block a user