swactor/crates/data-plane/DATA_PLANE_ACTOR_SPEC.md

524 lines
15 KiB
Markdown
Raw Normal View History

# Data Plane Actor Architecture Specification
**Status:** implemented architecture contract for the `data-plane` crate.
This document describes the behavioral boundary between reusable data-plane actors
and MVP-specific orchestration/runtime code. It is intentionally architectural: it
names responsibilities, actor roles, message families, and ownership boundaries
without prescribing file layout or migration steps.
---
## 1. Purpose
`data-plane` owns the behavior required to move model objects between stages and
between a node process and its local GPU worker process.
The crate defines the actor protocol for:
- provisioning logical data edges;
- distinguishing network/wire edges from node-local IPC rings;
- leasing, installing, readying, faulting, stopping, quiescing, and releasing
local rings;
- binding logical edges to transport endpoints and local worker rings;
- parsing, validating, sequencing, loading, producing, and reporting objects;
- gating readiness and object visibility on the correct lifecycle transitions;
- translating low-level arena, transport, and worker observations into coarse
data-plane outcomes.
`mvp-system` uses `data-plane` as a reusable actor subsystem. It provides run
intent, concrete runtime actor addresses, and MVP-specific report sinks. It does
not own the fine-grained data-plane state machine.
---
## 2. Core boundary
Swactor actors carry control, lifecycle, and identity messages. Payload bytes do
not move through actor mailboxes.
Payload bytes move through:
- arena-backed shared-memory rings for node-local IPC;
- transport byte streams for node-to-node data edges;
- GPU-worker-owned device allocations for compute-ready objects.
The data-plane actors decide when these byte paths are established, readable,
writable, faulted, stopped, and safe to release. Runtime adapter actors execute
concrete effects and report observations back.
---
## 3. Edge and ring model
### 3.1 Wire edge
A wire edge is a logical run-plan connection between a producer endpoint and a
consumer endpoint.
It carries:
- run identity;
- edge identity;
- producer and consumer node identity;
- edge kind, such as token input, activation, or token output;
- object contract;
- transport contract;
- optional remote endpoint and remote actor identity.
A wire edge answers: "which logical data stream connects these stage endpoints?"
It does not answer: "which local arena offset or worker-process ring is being
used on this node?"
### 3.2 Local IPC ring
A local IPC ring is a node-local buffer used by a Rust node process and its owned
GPU worker process.
It carries:
- ring identity allocated by the local arena manager;
- arena layout and capacity;
- worker-process generation;
- local role port, such as input or output;
- direction relative to the worker process;
- object contract installed into the worker;
- quiescence and release state.
A local IPC ring answers: "how does this node exchange bytes with its local
worker process for a specific edge endpoint?"
It does not answer: "which remote node or distributed route owns the other side
of the logical edge?"
### 3.3 Binding
A wire edge endpoint may bind to zero or more local resources depending on its
role:
```text
inbound wire edge endpoint
-> recv transport endpoint
-> local worker ingress ring
-> device object handle
outbound wire edge endpoint
-> local worker egress ring
-> send transport endpoint
local-only edge endpoint
-> local producer/consumer binding
-> optional worker ring
```
The binding is owned by data-plane state. MVP code may observe the binding only
through coarse reports such as edge ready, object loaded, object produced, edge
faulted, and edge stopped.
---
## 4. Actor topology
The target topology is actor-oriented.
### 4.1 Data-plane node actor
One data-plane node actor owns the data-plane state for one local node within one
active run.
It owns:
- local node identity;
- run-scoped edge table;
- mapping from wire edge endpoints to local rings;
- mapping from ring ids to edge endpoints;
- object sequence state;
- device-handle visibility state;
- data-plane child actor addresses;
- MVP report sink addresses.
It receives provisioning intent from MVP code and observations from runtime
adapters. It emits actor messages to arena, worker, transport, and MVP report
sinks.
### 4.2 Wire edge actor
A wire edge actor owns the lifecycle of one logical edge endpoint on the local
node.
It owns:
- provisioning state;
- transport establishment state;
- send/receive pump readiness;
- stream faults;
- logical edge readiness;
- stop and fault propagation for that edge endpoint.
It does not own worker-process state or arena layout details except through a
binding supplied by the data-plane node actor or ring actor.
### 4.3 Local worker ring actor
A local worker ring actor owns one local IPC ring lifecycle.
It owns:
- arena lease request and result;
- worker ring installation;
- worker ring readable/writable notifications;
- ring fault and quiescence observations;
- release proof collection;
- arena lease release.
It does not own remote endpoint routing. It can be bound to a wire edge endpoint
by edge id, but the ring lifecycle remains local.
### 4.4 GPU worker control adapter
The GPU worker control adapter is the actor-facing boundary to the owned Python
worker process.
It owns or fronts:
- worker process generation;
- command serialization to the worker;
- stdout/stderr event parsing;
- device handle generation checks;
- worker stop/crash/restart observations.
The data plane treats this as an actor endpoint. Worker-process JSON and Python
helper details are not exposed to MVP stage logic.
### 4.5 Transport adapter actors
Transport adapter actors own concrete wire byte movement.
They own or front:
- accepted edge streams;
- outbound edge streams;
- edge preamble validation;
- byte read/write readiness;
- transport-specific stream faults;
- pump stop observations.
The data plane treats transport events as observations on a wire edge. Transport
actors do not decide stage readiness or object admission.
### 4.6 MVP report sink
The MVP report sink receives coarse data-plane outcomes and maps them to
MVP-specific control messages.
Examples:
- inbound edge ready;
- outbound edge ready;
- object loaded for stage execution;
- object produced for downstream transport;
- edge faulted;
- local edges stopped.
The sink does not inspect ring cursors, arena leases, worker generations, or
transport pump internals.
---
## 5. Actor API surface
Concrete Rust names are schematic. The contract is the message shape and
ownership boundary.
### 5.1 Provisioning input
MVP sends provisioning intent to the data-plane node actor:
```text
ProvisionDataPlaneRun {
run_id,
local_node_id,
arena_actor,
worker_actor,
transport_actor,
report_sink,
}
ProvisionWireEdgeEndpoint {
run_id,
edge_id,
direction,
edge_kind,
local_role_port,
local_node_id,
peer_node_id,
peer_endpoint,
object_spec,
transport_spec,
local_ring_spec,
}
```
`direction` is relative to the local node's stage role: inbound means the local
stage consumes objects from the edge; outbound means the local stage produces
objects to the edge.
Provisioning is declarative. MVP describes the intended edge endpoint and the
actors available to execute effects. It does not prescribe lease/install/driver
ordering.
### 5.2 Runtime observations
Runtime adapters report observations back to data-plane actors:
```text
ArenaRingLeased
ArenaRingLeaseRejected
ArenaRingReleased
ArenaRingReleaseRejected
WorkerReady
WorkerRingInstalled
WorkerRingFaulted
WorkerRingQuiesced
WorkerRingReadable
WorkerRingWritable
WorkerObjectLoaded
WorkerObjectProduced
WorkerObjectFailed
WorkerStopped
WorkerFaulted
TransportEdgeReady
TransportBytesReceived
TransportBytesSent
TransportStreamClosed
TransportStreamFaulted
TransportPumpStopped
```
Observations are facts, not commands. The data plane decides the next state and
any follow-up messages.
### 5.3 Data-plane effects
The data plane sends effect requests to runtime adapter actors:
```text
LeaseArenaRing
CancelArenaRingLease
ReleaseArenaRingLease
InstallWorkerRing
UninstallWorkerRing
NotifyWorkerRingReadable
NotifyWorkerRingWritable
LoadObjectFromWorkerRing
ExecuteWorkerStep
ReleaseWorkerDeviceObject
EstablishWireSend
EstablishWireRecv
WriteWireObject
StopWirePump
```
Effects are actor messages. The receiving adapter owns the concrete mechanism:
memfd/mmap, JSON stdin/stdout, process supervision, iroh streams, or test doubles.
### 5.4 Data-plane reports
The data plane reports only stable semantic outcomes to MVP:
```text
InboundEdgeReady { edge_id }
OutboundEdgeReady { edge_id }
ObjectLoaded { edge_id, object_id, sequence, device_handle }
ObjectProduced { edge_id, object_id, sequence, extent }
EdgeFaulted { edge_id, reason }
EdgeStopped { edge_id }
LocalEdgesStopped { run_id }
WorkerDataPlaneFaulted { reason }
```
Reports are the only data-plane messages MVP stage/orchestrator actors should
need for normal stage progression.
---
## 6. Behavior owned by data-plane
### 6.1 Edge establishment
For each provisioned edge endpoint, data-plane actors own the establishment
sequence.
Inbound endpoint:
```text
provision endpoint
-> lease local ingress ring
-> install ring into worker input port
-> establish receive transport if the edge is remote
-> report inbound edge ready
```
Outbound endpoint:
```text
provision endpoint
-> lease local egress ring when worker output is required
-> install ring into worker output port
-> establish send transport if the edge is remote
-> report outbound edge ready
```
Readiness is reported only after every required local and wire resource for that
endpoint is ready. A local-only endpoint may omit transport establishment. A
wire-only endpoint may omit worker-ring establishment when it terminates outside
the local GPU worker.
### 6.2 Object ingress
For inbound data, data-plane actors own object admission.
The data plane:
- associates incoming bytes with the correct wire edge and stream;
- validates object framing and object spec constraints;
- preserves sequence ordering required by the edge contract;
- writes or exposes the object through the local ingress ring;
- asks the worker to load the object to device;
- waits for a valid worker object-loaded observation;
- reports object loaded to MVP only after the device handle is current and the
logical object is complete.
MVP does not parse object headers, track ingress buffers, reload cursors, or gate
object-loaded visibility.
### 6.3 Object egress
For outbound data, data-plane actors own object production and forwarding.
The data plane:
- receives compute/output observations from the worker;
- binds produced objects to the correct outbound edge and sequence;
- validates object extent and object contract;
- publishes readable/writable state to the worker and transport actors;
- forwards complete object records on the wire when the edge is remote;
- reports object produced or step-visible outcomes to MVP at semantic
boundaries, not cursor boundaries.
MVP does not decide when a local output ring is readable, when a transport stream
should consume it, or when a produced object is safe to expose downstream.
### 6.4 Faults
The data plane owns data movement fault classification and propagation.
Fault sources include:
- arena lease rejection or release rejection;
- worker ring installation failure;
- worker ring fault;
- malformed object framing;
- sequence violation;
- worker object load/produce failure;
- transport stream read/write/protocol failure;
- pump stop before quiescence;
- stale worker generation or device handle.
A fault on one edge endpoint must not silently corrupt another endpoint. The data
plane maps local faults to edge-scoped or worker-scoped reports, starts the
required stop/quiescence path, and emits the appropriate MVP report.
### 6.5 Stop, quiescence, and release
The data plane owns teardown ordering for local resources.
For a bound edge/ring pair, stop requires:
```text
stop transport pump if present
-> uninstall or quiesce worker ring if installed
-> prove no local reader/writer still uses the ring
-> release arena lease
-> report edge stopped
```
Arena release must be gated by quiescence proof. MVP may request run or edge
stop, but it does not supply low-level release proof or decide when a ring is
safe to release.
---
## 7. MVP-system utilization
`mvp-system` remains responsible for MVP orchestration and stage semantics.
It owns:
- run planning and edge assignment;
- stage provisioning authority;
- membership/readiness gates outside the data plane;
- weight loading and role configuration intent;
- stage controller behavior;
- prompt injection and token consumption;
- mapping data-plane reports to MVP lifecycle messages;
- selecting concrete runtime adapters for arena, worker process, and transport.
For data movement, MVP code acts as a client:
1. Spawn or obtain actor addresses for the data-plane node actor and required
runtime adapters.
2. Send run and edge provisioning intent to the data-plane node actor.
3. Forward runtime observations from concrete adapter actors when those adapters
are MVP-owned.
4. Receive coarse data-plane reports.
5. Translate those reports into stage-controller or orchestrator messages.
MVP must not rely on private edge states such as waiting-for-lease,
waiting-for-worker-ring, waiting-for-driver, pump-stopped, ring-quiesced, or
release-ready. Those are data-plane implementation states.
---
## 8. Required invariants
- `EdgeId` names a logical run-plan edge, not a local ring allocation.
- `RingId` names a local arena-backed IPC ring, not a distributed edge.
- A ring may be bound to an edge endpoint, but the identifiers are not
interchangeable.
- Actor messages carry control and identities, not tensor payload bytes.
- Object-loaded reports are emitted only for complete, validated objects with
current-generation device handles.
- Edge-ready reports are emitted only after required wire and local IPC resources
are ready.
- Arena lease release is gated by local quiescence proof.
- Worker process generation is part of every device-handle validity decision.
- Transport faults and worker faults are classified by data-plane before they
become MVP reports.
- MVP stage logic observes semantic outcomes, never ring cursor mechanics.
---
## 9. Non-goals
`data-plane` does not own:
- global run planning;
- placement optimization;
- model layer assignment;
- weight download or weight loading semantics;
- prompt tokenization or output token policy;
- membership convergence;
- provider provisioning;
- concrete iroh endpoint construction;
- concrete Python helper implementation.
The crate defines reusable actor protocols and data-movement behavior. Concrete
runtime adapters may live beside MVP code, inside reusable support crates, or in
tests, as long as they satisfy the actor contracts above.