Warm a worker before the node's clock starts, and refuse to delete a live flow

A node's timeout now covers its body only: the pool loads the source into the
worker it picked, off the node's budget, so imports that outlast the timeout no
longer make a node impossible to run. Draft checks compile without caching, so
saving does not evict what a busy node is serving calls from. Requests carry an
id the worker echoes and the pool checks, a reply is encoded once, and the
remote-exception cache is bounded.

DELETE /flows/{name} answers 409 while the flow has a running or queued run,
which is what was letting run_node rows outlive their run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
This commit is contained in:
2026-08-23 17:11:55 +02:00
co-authored by Claude Opus 5
parent ce465e7b0e
commit 084194f77b
7 changed files with 224 additions and 62 deletions
+21 -1
View File
@@ -281,7 +281,9 @@ def test_delete_flow(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
client.put(f"{PREFIX}/demo", headers=superuser_token_headers, json=a_flow())
db.add(Run(id="run-1", flow="demo", created_at=datetime.now(UTC)))
# Finished: a flow with a live run refuses to be deleted, which is its own
# test below.
db.add(Run(id="run-1", flow="demo", status="ok", created_at=datetime.now(UTC)))
db.add(RunNode(run_id="run-1", node="sensor"))
db.add(RunMetric(run_id="run-1", name="loss", step=-1))
db.add(RunArtifact(run_id="run-1", name="model.pt"))
@@ -301,6 +303,24 @@ def test_delete_flow(
assert db.exec(select(func.count()).select_from(model)).one() == 0
def test_delete_flow_is_refused_while_a_run_is_live(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
client.put(f"{PREFIX}/demo", headers=superuser_token_headers, json=a_flow())
db.add(
Run(id="run-live", flow="demo", status="running", created_at=datetime.now(UTC))
)
db.commit()
response = client.delete(f"{PREFIX}/demo", headers=superuser_token_headers)
assert response.status_code == 409
assert "run-live" in response.json()["detail"]
# Its driver is still writing node rows against that id, so nothing may go.
assert (
client.get(f"{PREFIX}/demo", headers=superuser_token_headers).status_code == 200
)
def test_node_types_are_listed(
client: TestClient, superuser_token_headers: dict[str, str]
) -> None: