opencode-config/docs/decisions/075-pr-178-draw-image-race-condition-tmp-render-svg.md
Sergey 39de98dc95
fix(draw-image): race condition on shared .tmp-render.svg breaks parallel renders (#178)
* fix(draw-image): use pid-based tmp svg and cleanup in finally

* test(draw-image): decouple e2e.optional from leftover tmp svg

* test(draw-image): add cleanup unit test for tmp svg lifecycle

* docs(draw-image): add handoff and ADR for tmp svg race fix

* docs(handoff): set PR number

* docs(project-map): update after structural changes

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-31 21:38:54 +03:00

26 lines
No EOL
2.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-075: Race condition on shared .tmp-render.svg breaks parallel renders
## Статус
Accepted (2026-07-31)
## Контекст
`cli.ts` рендерил SVG-обложки через двухфазный процесс:
1. `writeFileSync(path.join(__dirname, ".tmp-render.svg"), svg)` — запись в единственный fixed-path файл в каталоге пакета.
2. `spawnSync("node", ["render.mjs", tmpSvg, outPath])` — дочерний процесс читает файл и рендерит PNG через sharp.
При параллельном запуске нескольких CLI-процессов (vitest concurrently запускает несколько test-файлов, каждый вызывает `runCli` через `spawnSync`) процессы разделяли один `.tmp-render.svg`: один процесс обнулял/перезаписывал файл, пока другой его читал → `render.mjs` падал с `Input Buffer is empty` (пустой буфер SVG). Дополнительно leftover `.tmp-render.svg` от прошлых ручных прогонов засорял `git status` и мог содержать неактуальный SVG, вызывая ложные падения тестов.
## Решение
Использовать уникальный temp-файл с PID в имени под системным `os.tmpdir()`:
```ts
const tmpSvg = path.join(os.tmpdir(), `draw-image-${process.pid}.svg`)
```
и удалять его в `finally`-блоке после рендера (cleanup гарантирован даже при `process.exit(1)`). `render.mjs` не изменён — он уже принимает path к SVG как argv, уникальность обеспечивает `cli.ts`. `.gitignore` уже содержал `.tmp-render.svg` для обратной совместимости.
## Альтернативы
- **Передача SVG через stdin в `render.mjs`**: избегает temp-файла целиком, но требует переписывания `render.mjs` (чтение из stdin вместо `readFileSync(argv)`), что выходит за рамки спеки #160 и ломает контракт «`render.mjs` принимает path как argv».
- **Уникальный temp-каталог на процесс вместо PID-суффикса**: `fs.mkdtempSync(path.join(os.tmpdir(), "draw-image-"))` — даёт больше изоляции, но избыточен для одного SVG-файла; PID-суффикс достаточен, т.к. `spawnSync` синхронный и один процесс не запускает два рендера одновременно.
- **Atomic write (write-rename)**: `writeFileSync(tmp + ".part")` + `rename(tmp + ".part", tmp)` — не решает race (два процесса всё равно пишут в один путь), нужен уникальный путь.