docs(scan-gateway): README и спека — запись .part по №32/№35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
cb8c71fc26
commit
1c39ca1b73
17
README.md
17
README.md
@ -64,11 +64,14 @@ ack (с `attempt` повторного запроса); файл в ИС вто
|
||||
`<SCAN_TMP_DIR>` (по умолчанию `Tmp`), затем он публикуется в `Input` под настоящим именем. `Tmp` лежит рядом с `Input`
|
||||
и `Output`, на той же ФС/шаре: публикация атомарна, а ИС не может забрать недописанный файл (временных файлов в
|
||||
`Input` нет — решение владельца 06.10.2026, у FileWorker `.tmp` лежит рядом с назначением). В режиме `local` публикация —
|
||||
жёсткая ссылка `.part` → `Input/<имя>` (`FileExistsError`, если назначение есть) и удаление `.part`; на файловой
|
||||
системе без жёстких ссылок (`EPERM`/`EXDEV`/`ENOTSUP`) — `os.replace`, если назначения ещё нет (решение №17). В
|
||||
режиме `smb` — `smbclient.rename` (поднимает `FileExistsError`, если назначение есть). Если `Input/<job_id>.<ext>`
|
||||
уже существует (повторная публикация задания после потери ack в другом процессе), файл считается доставленным:
|
||||
назначение не перезаписывается, задание продолжается опросом `Output/<job_id>/`. Каталоги `Input` и `Tmp`
|
||||
жёсткая ссылка `.part` → `Input/<имя>` и удаление `.part`; на файловой системе без жёстких ссылок
|
||||
(`EPERM`/`EXDEV`/`ENOTSUP`/`EOPNOTSUPP` от самой ссылки) — `os.replace`, если назначения ещё нет (решение №17).
|
||||
`.part` создаётся заново: остаток прежней записи удаляется (ошибка удаления подавляется), файл открывается с `O_EXCL`;
|
||||
поверх оставшегося `.part` пишем только если это не жёсткая ссылка (`st_nlink == 1`) — иначе исключение (решение №32),
|
||||
чтобы не испортить уже опубликованный файл. В режиме `smb` — `smbclient.rename` (поднимает `FileExistsError`, если
|
||||
назначение есть). Если `Input/<job_id>.<ext>` уже существует (повторная публикация задания после потери ack в другом
|
||||
процессе), файл считается доставленным: ничего не пишется, назначение не перезаписывается, `.part` не трогается (при
|
||||
`SCAN_CLEANUP=true` существующий `.part` снимается), задание продолжается опросом `Output/<job_id>/` (решение №35). Каталоги `Input` и `Tmp`
|
||||
создаются при необходимости (без родителей), корень горячей папки сам не создаётся: нет корня — `RECOGNITION_FAILED`.
|
||||
- Ответ ИС — каталог `<SCAN_OUTPUT_DIR>/<job_id>/`: ИС кладёт туда **все** распознанные документы задания одновременно,
|
||||
по одному `*.json` на файл (`Output/<job_id>/*.json`). Шлюз читает **все** `*.json` каталога и склеивает их в один
|
||||
@ -105,8 +108,8 @@ ack (с `attempt` повторного запроса); файл в ИС вто
|
||||
от `SCAN_CLEANUP` (в `local` — удаление после жёсткой ссылки, ошибка удаления подавляется; в `smb` `.part`
|
||||
переименовывается во входящий и отдельного удаления не требует): иначе при выключенной очистке на каждое задание
|
||||
оставалась бы лишняя ссылка, а ошибка удаления на шаре с неподтверждёнными правами превращала бы доставленный файл в
|
||||
`RECOGNITION_FAILED`. **Флаг управляет остатками:** осиротевшие `.part` (оборванная запись), `.part` при уже
|
||||
существующем назначении, каталог ответа, входящий файл и `.done`. При `SCAN_CLEANUP=false` сервис не удаляет ничего из
|
||||
`RECOGNITION_FAILED`. **Флаг управляет остатками:** осиротевшие `.part` (оборванная запись, `.part` при уже
|
||||
существующем назначении), каталог ответа, входящий файл и `.done`. При `SCAN_CLEANUP=false` сервис не удаляет ничего из
|
||||
этого и не пишет в лог об осиротевших `.part` (решение №27): они накапливаются в `Tmp` и убираются вручную или
|
||||
включением флага.
|
||||
- Права на SMB: для очистки учётной записи шары нужны права на удаление в `Input`, `Output` (включая каталог
|
||||
|
||||
@ -129,7 +129,7 @@
|
||||
`<Input>/<job_id>.<ext>`, его вариант `<Input>/<job_id>.<ext>.done` и временный `<Tmp>/.<job_id>.<ext>.part`, если они
|
||||
существуют. При `SCAN_CLEANUP=false` (по умолчанию) сервис MUST NOT удалять ничего, включая осиротевшие временные файлы (`.part`, ставший
|
||||
целью успешной публикации, снимается самой записью — это завершение «rename без замены», а не очистка; ошибка его снятия
|
||||
запись не прерывает, решение №26). При
|
||||
запись не прерывает, решение №26; запись собственного `.part` заново удаляет прежний остаток под тем же именем, решение №32). При
|
||||
`TIMEOUT`, `FILE_READ_ERROR`, `RECOGNITION_FAILED` записи очистка MUST NOT выполняться. Ошибка удаления SHALL быть
|
||||
записана в лог одной строкой на задание и MUST NOT менять конверт, `metrics.warnings` и `/healthcheck`.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user