From 3e1f39cd9e2314ddbc4301fe5c748060db7991d2 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 4 Oct 2026 06:41:29 +0400 Subject: [PATCH] =?UTF-8?q?docs(context):=2003-architecture=20=E2=80=94=20?= =?UTF-8?q?SCHEMA=5FMISMATCH=20=D0=B1=D0=B5=D0=B7=20ack,=20=D0=BD=D0=B5?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D0=BC=D1=8B=D0=B9=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=20=D0=B1=D0=B5=D0=B7=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=B0,=20PUBLISH=5FRETRIES,=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D1=80=D1=8B=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- context/03-architecture.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/context/03-architecture.md b/context/03-architecture.md index 9bc4e22..a11ac65 100644 --- a/context/03-architecture.md +++ b/context/03-architecture.md @@ -42,7 +42,11 @@ и [`recognition-result-message.schema.json`](../contracts/recognition-result-message.schema.json) — тела сообщений, [`recognition-result.schema.json`](../contracts/recognition-result.schema.json) — файл результата в S3; примеры — [`docs/contracts/examples/`](../contracts/examples/). Примеры проверяются схемами: `python scripts/validate_contracts.py` -(из корня репозитория docs). +(из корня репозитория docs). Примеры — иллюстрации формы сообщений, а не готовые к исполнению задания: +схема проверяет у `reply_deadline` только формат `date-time` и не сравнивает срок с текущим временем, поэтому +`request-image.json` проходит схему при `reply_deadline` в прошлом. Срок проверяет сервис: `scan-gateway` отвечает +`error TIMEOUT`, если `reply_deadline` истёк до отправки файла в ИС Сканирование; `tabular-parser` срок не проверяет. +Файлы `examples/` копируются в сервисы побайтно, поэтому пример не правится ради «свежей» даты. | Поток | Subject | Кто пишет | Полезная нагрузка | `Nats-Msg-Id` | Схема | |---|---|---|---|---|---| @@ -57,7 +61,9 @@ ядро — queue group `arm-core` на `recognition.result`; сервис сразу по получении запроса публикует ack `processing`; ядро, не получив ack за `ACK_TIMEOUT_SECONDS` (30), публикует запрос повторно с тем же `Nats-Msg-Id`, не более `OUTBOX_MAX_REPUBLISH` (3) раз, затем `dlq`. Сервис держит process-local множество `job_id` в работе и на повторный -запрос отвечает ack'ом без второй обработки. Появится JetStream — меняются только publisher/subscriber. +запрос отвечает ack'ом без второй обработки. Публикация ack и результата сервисом при временной ошибке повторяется +с тем же `Nats-Msg-Id`: первая попытка плюс `PUBLISH_RETRIES` повторов (по умолчанию 3, всего до 4 попыток); повторы +исчерпаны — error-лог, задание закроет ядро (по `reply_deadline`). Появится JetStream — меняются только publisher/subscriber. Правила: - `recognition_job` создаётся в статусе `queued` **в одной транзакции** со `stored_file`; фоновый публикатор переводит в @@ -70,8 +76,12 @@ Ack (`processing`) сервис публикует **до чтения файла из S3**; повторная публикация запроса ядром идёт с тем же `Nats-Msg-Id` и тем же `attempt`. Дубликат результата ядро определяет по `recognition_job.result_msg_id`. - `schema_version` — `MAJOR.MINOR`; ядро и сервисы принимают тот же MAJOR и любой MINOR. Сервис отвергает запрос с - неизвестным MAJOR результатом `error` с кодом `SCHEMA_MISMATCH` и подтверждает его ack'ом, чтобы не зациклить - доставку. Новый MINOR раскатывается сначала на потребителей + неизвестным MAJOR (как и запрос не по схеме) результатом `error` с кодом `SCHEMA_MISMATCH` **без предшествующего ack + `processing`**: финальный конверт сам закрывает задание в ядре (`published → error`), повторной доставки не будет. + Конверт публикуется, только если из тела извлекаются корректные `job_id` и `file_id`; для `job_id`, уже находящегося в + работе или обработанного, он не публикуется (его `Nats-Msg-Id` совпал бы с настоящим результатом). Нечитаемый запрос + (не JSON, нет `job_id`) остаётся без ответа: сервис пишет лог и отбрасывает его, ядро не получает ack, повторяет + публикацию `OUTBOX_MAX_REPUBLISH` раз и закрывает задание в `dlq` с `error_code = NO_ACK`. Новый MINOR раскатывается сначала на потребителей (ядро и сервисы обновляют копии схем раньше, чем производитель начнёт слать 1.1), потому что закрытые enum отвергают незнакомые значения. - Сервисы не знают про БД и про бизнес-смысл: они возвращают структуру файла; нормализация счетов, резолв