2026-11-07 04:01:45 +04:00

5.3 KiB
Raw Blame History

Spec Delta

Purpose

Дельта change quality-metrics (метрики качества первички).

MODIFIED Requirements

Requirement: Inbox принимает результат идемпотентно

Для конверта со status ∈ {done, needs_check, error} ядро SHALL валидировать его по recognition-result-message.schema.json, найти задание по job_id и: если result_msg_id задания уже равен Nats-Msg-Id сообщения — ничего не менять; иначе зарегистрировать файл результата в s3_object (bucket_role = results, created_by = <service.name>; только при наличии result_s3), обновить задание (status, service_name, service_version, finished_at, metrics, error_code, error_message, result_schema_version, result_s3_object_id, result_msg_id) и зафиксировать транзакцию. При status = error файл задания (если current_recognition_job_id равен заданию) SHALL получить stored_file.status = ERROR; при done/needs_check файл остаётся PROCESSING и SHALL быть обработан циклом маппинга (спека osv-result-mapping); inbox MUST NOT читать файл результата. Неизвестный job_id SHALL быть залогирован. Результат для задания, уже находящегося в финальном статусе (done, needs_check, error, timeout, dlq), SHALL логироваться и не применяться. Если в конверте есть raw_s3_key, ядро SHALL независимо от status зарегистрировать сырой ответ ИС в s3_object (bucket_role = raw, ключ raw/{job_id}.json) и проставить recognition_job.raw_s3_object_id. При status = error в той же транзакции SHALL записываться quality_failure_fact (failure_kind = SERVICE_ERROR, error_code из конверта).

Scenario: Успешный результат

  • WHEN сервис публикует конверт status = done с result_s3 = {arm-ka, results/<file_id>/<job_id>/result.json} и Nats-Msg-Id = result:<job_id>:1
  • THEN задание получает status = done, result_msg_id = result:<job_id>:1, result_s3_object_id указывает на новую строку s3_object с этим ключом, finished_at заполнен; файл остаётся PROCESSING до маппинга

Scenario: Повторная доставка

  • WHEN то же сообщение получено второй раз
  • THEN строка recognition_job и s3_object не меняются

Scenario: Ошибка сервиса

  • WHEN конверт содержит status = error, error = {UNSUPPORTED_FORMAT, …} без result_s3
  • THEN задание получает status = error, error_code = UNSUPPORTED_FORMAT, error_message из конверта, result_s3_object_id пуст; файл — ERROR

Scenario: Результат пришёл после таймаута

  • WHEN задание уже timeout, и приходит конверт done
  • THEN задание не меняется, событие залогировано

Scenario: Сырой ответ зарегистрирован

  • WHEN конверт done содержит raw_s3_key = raw/<job_id>.json
  • THEN создан s3_object с bucket_role = raw, recognition_job.raw_s3_object_id указывает на него

Scenario: Факт отказа сервиса

  • WHEN конверт содержит status = error
  • THEN кроме ERROR файла записан quality_failure_fact с failure_kind = SERVICE_ERROR

Requirement: Таймаут ожидания результата

Фоновый вахтёр SHALL с периодом 30 с переводить задания processing и published с reply_deadline < now() в status = timeout, error_code = TIMEOUT, finished_at = now(), а их файлы (по current_recognition_job_id) — в stored_file.status = ERROR. Публикатор при переводе задания в dlq SHALL так же переводить файл в ERROR. Ручной повтор после TIMEOUT/NO_ACK SHALL создавать новое задание по требованию «Новая попытка обработки файла». Каждый перевод в timeout и в dlq SHALL писать quality_failure_fact (TIMEOUT / NO_ACK) в той же транзакции.

Scenario: Сервис взял, но не ответил

  • WHEN задание в processing и reply_deadline прошёл без результата
  • THEN не позднее чем через 30 с задание имеет status = timeout, файл — ERROR с error_code = TIMEOUT

Scenario: Факт таймаута

  • WHEN вахтёр перевёл задание в timeout
  • THEN записан quality_failure_fact с failure_kind = TIMEOUT