diff --git a/NOTEPAD.md b/NOTEPAD.md index a2737f8..8bb9732 100644 --- a/NOTEPAD.md +++ b/NOTEPAD.md @@ -12,6 +12,29 @@ Deferring because out of scope is fine, but don't mention deferring than. ### To be sorted +- BUG/FLOW: **a cancelled request can leave `FlowController._lock` held forever.** + Seeding nineteen flows over a client that timed out mid-request left the next + `POST /flows/{name}/start` waiting on the lock indefinitely — ten minutes, until + the container was restarted. A py-spy dump showed *no* thread in the reload path, + so the coroutine that holds it is suspended at an `await` inside `reload()`, most + likely in `_teardown()` awaiting a supervised task's cancellation. Everything else + kept working — health, reads, MQTT — so the engine looked fine and only anything + needing a rebuild hung. Two things worth doing: release the lock on cancellation + (`asyncio.timeout` around the teardown, or a `finally` that cannot be skipped), and + fail a `start` that waits more than a few seconds for the lock rather than hanging. +- BUG/INFRA: **475 zombie `git` processes** in the API container after a seeding + session. `FlowStore._git` uses `subprocess.run`, which reaps its own child — these + are the `git gc --auto` daemons `git commit` spawns, reparented to PID 1 when their + parent exits. PID 1 is the FastAPI process, which never reaps orphans. Harmless at + 484 processes against a 34719 limit, but it grows with every save, and a container + that commits per keystroke will get there. Fix: `git -c gc.auto=0 commit`, since a + store that never packs is a separate (and real) concern — 1898 commits had left + 5863 loose objects and no packs. +- PERF/API: seeding nineteen flows takes two reloads each — one to publish, one to + stop — so a full rebuild of every flow in the installation runs about forty times + for one seed. It is the slowest thing about standing an installation up, and a + `PUT` that could say "published, stopped" in one call would halve it. + - FEAT/SEC: `locked` is a read-only surface, not a permission — the server accepts a publish from a panel whose dashboard says locked. Making it real means carrying the flag into `_panel_may`.