docs(openspec): recognition-contract — SCHEMA_MISMATCH без ack, нечитаемый запрос, повторы публикации, примеры как иллюстрации
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a977ab7792
commit
2423854f27
@ -21,8 +21,28 @@
|
||||
заголовком `Nats-Msg-Id` = `<job_id>:<attempt>`
|
||||
|
||||
#### Scenario: Сообщение не по схеме
|
||||
- **WHEN** сервис получает запрос, не проходящий схему (нет `s3.key`, неизвестный `file_type`)
|
||||
- **THEN** сервис публикует результат со `status = error`, `error.code = SCHEMA_MISMATCH` и не падает
|
||||
- **WHEN** сервис получает запрос, не проходящий схему (нет `s3.key`, неизвестный `file_type`), но из тела извлекаются
|
||||
корректные `job_id` и `file_id` (и `attempt` для `Nats-Msg-Id`)
|
||||
- **THEN** сервис публикует результат со `status = error`, `error.code = SCHEMA_MISMATCH` **без предшествующего ack
|
||||
`processing`** и не падает
|
||||
|
||||
#### Scenario: Нечитаемый запрос
|
||||
- **WHEN** сервис получает сообщение, из которого нельзя извлечь `job_id` (не JSON, не объект, `job_id` отсутствует или
|
||||
не uuid), а значит нельзя собрать валидный конверт
|
||||
- **THEN** сервис ответа не публикует: запрос записывается в лог и отбрасывается; ядро не получит ack, повторит
|
||||
публикацию `OUTBOX_MAX_REPUBLISH` раз и переведёт задание в `dlq` (`error_code = NO_ACK`)
|
||||
|
||||
#### Scenario: Невалидный повтор запроса, уже находящегося в работе
|
||||
- **WHEN** сервис получает запрос, не проходящий схему, но с `job_id`, который уже в работе или обработан этим процессом
|
||||
- **THEN** конверт `SCHEMA_MISMATCH` не публикуется (его `Nats-Msg-Id` `result:<job_id>:<attempt>` совпал бы с настоящим
|
||||
результатом, и ядро отбросило бы настоящий как дубликат); запрос записывается в лог и отбрасывается
|
||||
|
||||
#### Scenario: Примеры контракта — иллюстрации
|
||||
- **WHEN** запрос берётся из `docs/contracts/examples/request-*.json` (в `request-image.json` `reply_deadline` лежит в
|
||||
прошлом)
|
||||
- **THEN** он проходит схему: схема проверяет для `reply_deadline` лишь формат `date-time`, но не сравнивает срок с
|
||||
текущим временем; пример — иллюстрация формы сообщения, а не готовое к исполнению задание; проверка срока — дело
|
||||
сервиса (`scan-gateway` отвечает `error TIMEOUT`, если `reply_deadline` истёк до отправки файла в ИС Сканирование)
|
||||
|
||||
### Requirement: Подтверждение взятия в работу (ack без JetStream)
|
||||
Получив запрос, сервис SHALL немедленно, до чтения файла из S3, опубликовать в `recognition.result` конверт со
|
||||
@ -82,8 +102,10 @@ v1.0: обязательные поля `schema_version` (`"1.0"`), `job_id`, `f
|
||||
|
||||
#### Scenario: Несовместимая версия запроса
|
||||
- **WHEN** сервис, поддерживающий `1.x`, получает запрос `schema_version = "2.0"`
|
||||
- **THEN** публикуется результат `status = error`, `error.code = SCHEMA_MISMATCH`, запрос подтверждается (ack), чтобы не
|
||||
зациклить доставку
|
||||
- **THEN** публикуется результат `status = error`, `error.code = SCHEMA_MISMATCH` **без ack `processing`**: ответом на
|
||||
запрос служит сам финальный конверт, ядро закрывает им задание (`published → error`), и повторной доставки запроса не
|
||||
будет. Конверт публикуется только если из тела извлекаются корректные `job_id` и `file_id`; иначе — как для нечитаемого
|
||||
запроса (ответа нет, ядро закроет задание по `NO_ACK` / `dlq`)
|
||||
|
||||
### Requirement: Идемпотентность результатов без брокера
|
||||
Публикующий SHALL ставить заголовок `Nats-Msg-Id` на каждое сообщение (запрос `<job_id>:<attempt>`, ack
|
||||
@ -95,6 +117,23 @@ v1.0: обязательные поля `schema_version` (`"1.0"`), `job_id`, `f
|
||||
- **WHEN** сервис опубликовал один и тот же результат два раза (повтор публикации после сетевой ошибки)
|
||||
- **THEN** задание изменено один раз, второе сообщение проигнорировано
|
||||
|
||||
### Requirement: Повторы публикации ответов сервисом
|
||||
Сервис SHALL публиковать ack и результат с заголовком `Nats-Msg-Id` и при временной ошибке публикации (потеря соединения,
|
||||
таймаут подтверждения сервера) повторять публикацию с тем же `Nats-Msg-Id`: **одна первая попытка и до
|
||||
`PUBLISH_RETRIES` повторов** (значение по умолчанию 3, то есть не более 4 попыток), с паузой между попытками; пауза —
|
||||
деталь реализации. Повторы исчерпаны — сервис пишет error-лог и не публикует это сообщение снова; задание закрывается
|
||||
на стороне ядра (не пришёл результат — `reply_deadline` → `timeout`; не пришёл ack — повтор запроса и `dlq`, но результат,
|
||||
дошедший без ack, ядро принимает и в статусе `published`). Дубликаты, появившиеся из-за повтора,
|
||||
безопасны: ядро дедуплицирует их по `Nats-Msg-Id`.
|
||||
|
||||
#### Scenario: Временный сбой публикации
|
||||
- **WHEN** публикация результата упала с временной ошибкой, а следующая попытка успешна
|
||||
- **THEN** сообщение опубликовано с прежним `Nats-Msg-Id`; ядро изменит задание один раз, даже если первая попытка тоже дошла
|
||||
|
||||
#### Scenario: Повторы исчерпаны
|
||||
- **WHEN** все 1 + `PUBLISH_RETRIES` попыток публикации результата неуспешны
|
||||
- **THEN** сервис пишет error-лог и продолжает работу (процесс не падает), а ядро закрывает задание по `reply_deadline`
|
||||
|
||||
### Requirement: Копия контракта в сервисе идентична источнику
|
||||
Каждый сервис, читающий или пишущий контракт, SHALL хранить копию файлов схем в `api/contracts/` и проверять в `make check`
|
||||
их байтовую идентичность файлам `docs/contracts/` (`../docs` или путь `ARM_KA_DOCS`; при недоступности — пропуск с предупреждением).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user