docs(dev): README — раздел «Проверка окружения» со сквозным сценарием
Помощники dpsql/dmc/new_job/poll и фактический вывод трёх сценариев на
профиле services (2026-10-04): tabular → error/NOT_IMPLEMENTED; image →
эмулятор ИС → error/INVALID_DOCUMENT (ответ-ошибка из фикстуры aep_5) и
raw/{job_id}.json в MinIO; tabular-parser остановлен → 3 повтора через 30 с →
dlq/NO_ACK (~120 с). Что сквозная проверка Среза 0 не покрывает.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
e98a9239bf
commit
2e2e50accb
106
dev/README.md
106
dev/README.md
@ -145,6 +145,112 @@ curl -s localhost:8000/api/v1/me -H "Authorization: Bearer <JWT>" # email,
|
||||
**Остановка и сброс профиля** — только с `--profile services`: без него compose не видит контейнеры профиля
|
||||
(см. «Сброс окружения»).
|
||||
|
||||
## Проверка окружения (сквозной сценарий)
|
||||
|
||||
Ручная проверка обмена `recognition_job` через NATS на поднятом профиле `services`: ядро публикует задание, сервис
|
||||
отвечает ack и результатом, ядро меняет статус. UI и API загрузки файлов в Срезе 0 нет, поэтому файл кладётся в MinIO
|
||||
через `mc`, а строки `stored_file` / `s3_object` / `recognition_job` вставляются через `psql`. Проверено 2026-10-04
|
||||
(все три сценария ниже — фактический вывод, время UTC).
|
||||
|
||||
Помощники (bash/zsh, из `docs/dev/`; `uuidgen` есть в macOS и util-linux):
|
||||
|
||||
```bash
|
||||
dpsql() { docker compose exec -T postgres sh -c 'psql -X -q -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" "$@"' sh "$@"; }
|
||||
dmc() { docker compose run --rm -v "$PWD/../../fixtures:/fixtures:ro" --entrypoint sh minio-init \
|
||||
-c 'mc alias set local http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" >/dev/null && mc "$@"' sh "$@"; }
|
||||
new_job() { # new_job <tabular|image> <file_type> <файл в fixtures> — кладёт файл в uploads/ и ставит задание queued
|
||||
F=$(uuidgen | tr A-Z a-z); JOB=$(uuidgen | tr A-Z a-z); O=$(uuidgen | tr A-Z a-z); N=$(basename "$3")
|
||||
dmc cp -q "/fixtures/$3" "local/arm-ka/uploads/$F/$JOB/$N" >/dev/null || return 1
|
||||
dpsql <<SQL
|
||||
insert into dkb.legal_entity (id, inn, name, valid_from) values (gen_random_uuid(), '7700000001', 'Проверка окружения', now())
|
||||
on conflict (inn) do nothing;
|
||||
insert into dkb.s3_object (id, bucket_role, bucket, key, created_at) values ('$O', 'uploads', 'arm-ka', 'uploads/$F/$JOB/$N', now());
|
||||
insert into dkb.stored_file (id, legal_entity_id, file_type, original_name, s3_object_id, status, uploaded_at)
|
||||
values ('$F', (select id from dkb.legal_entity where inn = '7700000001'), '$2', '$N', '$O', 'UPLOADED', now());
|
||||
insert into dkb.recognition_job (id, file_id, pipeline, request_subject, schema_version, status, requested_at)
|
||||
values ('$JOB', '$F', '$1', 'recognition.$1.request', '1.0', 'queued', now());
|
||||
SQL
|
||||
echo "JOB=$JOB"
|
||||
}
|
||||
poll() { # poll <секунд> — статус задания $JOB раз в секунду, до финального статуса
|
||||
for _ in $(seq "$1"); do
|
||||
s=$(dpsql -At -c "select status || ' ' || coalesce(error_code, '') || ' ' || coalesce(metrics->>'republish_count', '') from dkb.recognition_job where id = '$JOB'")
|
||||
[ "$s" != "$p" ] && echo "$(date -u +%T) $s" && p=$s
|
||||
case $s in error*|done*|needs_check*|timeout*|dlq*) p=; return;; esac
|
||||
sleep 1
|
||||
done; p=
|
||||
}
|
||||
```
|
||||
|
||||
Ключ исходника — `uploads/{file_id}/{job_id}/{имя}` (`../context/03-architecture.md`, «Схема ключей S3»). Сообщения на
|
||||
шине можно смотреть параллельно: `docker run --rm --network arm-ka_default natsio/nats-box:0.17.0 nats sub -s
|
||||
nats://nats:4222 'recognition.>'` (печатает `Nats-Msg-Id` и тело каждого запроса, ack и результата).
|
||||
|
||||
**1. `tabular` → `NOT_IMPLEMENTED`.** Заглушка парсера Среза 0 отвечает ошибкой на любой файл:
|
||||
|
||||
```bash
|
||||
new_job tabular XLSX "osv/schastl/2025-Q3/Оборотносальдовая ведомость по счету 60 за 3 квартал 2025 г..xlsx" && poll 30
|
||||
# JOB=e585f47b-1b38-44a6-84c5-9432c7e6d194
|
||||
# 02:12:15 queued
|
||||
# 02:12:20 error NOT_IMPLEMENTED
|
||||
dpsql -c "select status, error_code, error_message, published_at::time(3), started_at::time(3), finished_at::time(3) from dkb.recognition_job where id = '$JOB'"
|
||||
# error | NOT_IMPLEMENTED | разбор XLS/XLSX/XLSB реализуется в Срезе 2 | 02:12:18.98 | 02:12:19.021 | 02:12:19.024
|
||||
```
|
||||
|
||||
`published → processing → error` проходит за десятки миллисекунд (в прогонах 14–51 мс), опросом его не поймать; последовательность видна по
|
||||
`published_at < started_at < finished_at` (`started_at` пишет ack), в логе `arm-core` («Запрос распознавания
|
||||
опубликован» → «Сервис подтвердил получение запроса (ack): задание в processing» → «Результат распознавания принят …
|
||||
error_code=NOT_IMPLEMENTED») и на шине: `recognition.tabular.request` (`Nats-Msg-Id: <job_id>:1`) →
|
||||
`recognition.result` `ack:<job_id>:1` (`status: processing`) → `result:<job_id>:1` (`status: error`). Публикатор
|
||||
опрашивает outbox раз в 5 с, поэтому от вставки до публикации проходит до 5 с.
|
||||
|
||||
**2. `image` → эмулятор ИС → `INVALID_DOCUMENT`, сырой ответ в `raw/`:**
|
||||
|
||||
```bash
|
||||
new_job image PDF ocr/aep_5.PDF && poll 30
|
||||
# JOB=46b0c90b-5a1b-4ac0-9e8a-0cdad80d857e
|
||||
# 02:12:27 queued
|
||||
# 02:12:29 processing
|
||||
# 02:12:34 error INVALID_DOCUMENT
|
||||
dmc ls local/arm-ka/raw/
|
||||
# [2026-10-04 02:12:34 UTC] 588B STANDARD 46b0c90b-5a1b-4ac0-9e8a-0cdad80d857e.json
|
||||
dmc cat "local/arm-ka/raw/$JOB.json" # {"file_uid": "<job_id>", "error": {"code": "INVALID_DOCUMENT", "message": "Не удалось определить тип документа."}, "data_ocr": {…}}
|
||||
```
|
||||
|
||||
Код `INVALID_DOCUMENT` здесь — **настоящий ответ ИС из фикстуры**: обе пары в `fixtures/ocr` (`aep_5.json`,
|
||||
`aep_8_form4.json`) — ответы-ошибки ИС Сканирование («Не удалось определить тип документа.», пустой `table_structure`).
|
||||
Эмулятор нашёл пару (в логе `scan-emulator`: `<job_id>_aep_5.PDF -> …/Output/<job_id>/<job_id>.json`; при
|
||||
отсутствии пары сообщение было бы «…: нет эталонного ответа в фикстурах»). Поэтому успешный путь `image`
|
||||
(`done`/`needs_check`, файл `results/…`) сквозной проверкой на текущих фикстурах не проходит — нужен успешный ответ ИС
|
||||
в `fixtures/ocr`. Имя файла — `aep_5.PDF` (расширение в верхнем регистре; на macOS `aep_5.pdf` тоже находится, на Linux — нет).
|
||||
`processing` виден ~5 с: задержка эмулятора 2 с плюс опрос горячей папки шлюзом.
|
||||
|
||||
**3. Сервис недоступен → три повторные публикации → `dlq` / `NO_ACK`:**
|
||||
|
||||
```bash
|
||||
docker compose stop tabular-parser # имя сервиса указано явно — профиль не нужен
|
||||
new_job tabular XLSX "osv/schastl/2025-Q3/Оборотносальдовая ведомость по счету 60 за 3 квартал 2025 г..xlsx" && poll 180
|
||||
# 02:12:43 queued
|
||||
# 02:12:44 published
|
||||
# 02:13:14 published 1 ← republish_count: повтор через ACK_TIMEOUT_SECONDS = 30 с, тот же Nats-Msg-Id <job_id>:1
|
||||
# 02:13:45 published 2
|
||||
# 02:14:14 published 3
|
||||
# 02:14:44 dlq NO_ACK 3 ← ещё через 30 с после третьего повтора (OUTBOX_MAX_REPUBLISH = 3)
|
||||
dpsql -c "select status, error_code, error_message, metrics, request_msg_id from dkb.recognition_job where id = '$JOB'"
|
||||
# dlq | NO_ACK | Сервис распознавания не подтвердил получение запроса (ack) после 3 повторных публикаций | {"republish_count": 3} | <job_id>:1
|
||||
docker compose --profile services up -d --wait tabular-parser # вернуть сервис; rc=0, Healthy
|
||||
```
|
||||
|
||||
От первой публикации до `dlq` — ~120 с (4 × 30 с). `published_at` при каждом повторе перезаписывается (время последней
|
||||
публикации), `attempt` остаётся 1, `started_at` и `service_name` пусты. Задание в `dlq` после возврата сервиса само не
|
||||
перезапускается (replay — вне Среза 0).
|
||||
|
||||
Что сквозная проверка **не** покрывает (Срез 0): успешные результаты (`done`/`needs_check`, регистрация файла
|
||||
`results/…` в `s3_object`) — заглушка парсера и фикстуры ИС дают только ошибки; `stored_file.status` остаётся
|
||||
`UPLOADED`, `current_recognition_job_id` не заполняется (маппинг результата — `NoopMapper`, Срез 2); у ошибок
|
||||
`scan-gateway` `recognition_job.metrics` пустой (`tabular-parser` присылает `duration_ms`). Тестовые строки и объекты
|
||||
остаются в БД и MinIO; полная очистка — `docker compose --profile services down -v`.
|
||||
|
||||
## Переменные `.env`
|
||||
|
||||
Файл читают `docker compose` (из этого каталога) и, вторым блоком, сервисы при запуске из venv. Значения по умолчанию —
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user