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
+5 -1
View File
@@ -793,11 +793,15 @@ class FlowController:
Loaded on the machine that will run it: a node bound to a device is
checked against that worker's packages, because a missing import here
says nothing about whether it is missing there.
The source is usually a draft, and the node is usually still running
the published one — so ``keep=False``: compile it to answer the
question, and leave the worker holding what it is serving calls from.
"""
if device and self.remote is not None:
return self.remote.compile(device, flow, node_id, code)
if self.workers is not None:
return self.workers.compile(flow, node_id, code)
return self.workers.compile(flow, node_id, code, keep=False)
try:
load_function(flow, node_id, code)
except Exception as exc: