Process-based cancellation of in-flight query computations.
On the BEAM, cancellation is clean: Process.exit(pid, :kill) terminates
the process immediately. No unwinding, no partial state — writes are
buffered until successful completion, so killed tasks leave no trace in
the memo table.
Cancellation protocol
When an input changes, the caller invokes cancel_dependents/2 with the
input's query key. This walks forward from all active tasks, checking
whether each task's memo entry transitively depends on the changed key.
Affected tasks are killed and their dedup/registry entries cleaned up.
This is Option A from the subsystem spec: forward walk from active tasks. The number of active tasks is bounded by CPU cores, and dependency depth is typically 3-5, making this efficient in practice.
See D3 for the Task-based process model.
Summary
Functions
Waits for an in-flight task to complete, or cancels it on timeout.
Cancels all in-flight tasks. Called on database shutdown or full reset.
Cancels all in-flight tasks that transitively depend on the given query key.
Registers an in-flight query task in the task registry.
Removes a task registration from the task registry.
Functions
@spec await_or_cancel(Roux.Database.t(), Roux.Memo.query_key(), timeout()) :: {:ok, term()} | :cancelled
Waits for an in-flight task to complete, or cancels it on timeout.
Looks up the task pid from the task registry and monitors it. Returns
{:ok, value} if the task completes and leaves a memo entry, or
:cancelled if the task is killed, crashes, or times out.
Handles the race condition where a task completes between registry
lookup and Process.monitor (:noproc DOWN message) by checking
the memo table before returning :cancelled.
If no task is registered, checks the memo table directly.
@spec cancel_all(Roux.Database.t()) :: :ok
Cancels all in-flight tasks. Called on database shutdown or full reset.
Uses telemetry reason :shutdown.
@spec cancel_dependents(Roux.Database.t(), Roux.Memo.query_key()) :: :ok
Cancels all in-flight tasks that transitively depend on the given query key.
Called when an input is set or changed. For each registered task, checks
whether its memo entry's dependencies transitively include the target key.
Affected tasks are killed with Process.exit(pid, :kill).
Uses telemetry reason :input_changed.
@spec register_task(Roux.Database.t(), Roux.Memo.query_key(), pid()) :: :ok
Registers an in-flight query task in the task registry.
Called by Runtime when spawning a task for query execution. Overwrites any previous registration for the same key.
@spec unregister_task(Roux.Database.t(), Roux.Memo.query_key()) :: :ok
Removes a task registration from the task registry.
Called on task completion. No-op if no registration exists for the key.