107 lines
5.4 KiB
Markdown
107 lines
5.4 KiB
Markdown
# Phase 1 actor prototype
|
|
|
|
Run the feasibility probes and two-actor demonstration with:
|
|
|
|
```sh
|
|
cargo run -p runa
|
|
mise run pre-commit
|
|
```
|
|
|
|
The executable loads [counter.rn](../runa-std/actors/counter.rn), spawns two
|
|
actors sharing one implementation, and sends a structured message. The sender
|
|
commits state 1 and sends a message that moves the receiver from state 10 to 15.
|
|
|
|
## Current contracts
|
|
|
|
- `runa-actor` owns actor, mailbox and implementation IDs, structured messages,
|
|
snapshots, statuses and lifecycle events.
|
|
- `runa-rune` owns compilation and typed bounded invocation execution.
|
|
- `runa-runtime` owns implementation registration, queues, private committed
|
|
state, scheduling, message effects and lifecycle coordination.
|
|
- `runa` composes the experiment and demonstration.
|
|
|
|
An implementation is published once under an ID. Actors reference that exact
|
|
implementation. IDs and incarnation counters use checked increments.
|
|
|
|
Prototype state is an `i64`; messages contain integer `kind` and `value`
|
|
fields. Rune handlers have this deliberately restricted interface:
|
|
|
|
```text
|
|
handle(state, kind, value) -> (new_state, [(target_actor_id, kind, value), ...])
|
|
```
|
|
|
|
The returned effect vector describes sends; handlers cannot access the runtime
|
|
or directly mutate committed state. General schema/versioned values and
|
|
capability-bearing contexts are future work.
|
|
|
|
## Scheduling and lifecycle
|
|
|
|
`tick(nonzero_budget)` gives each runnable actor one bounded slice in ID order.
|
|
FIFO queues are bounded, including the reserved message in capacity accounting.
|
|
A queued message is reserved when its invocation starts. Subsequent messages
|
|
stay queued, including while the actor awaits. A wake marks an awaiting actor
|
|
runnable; the caller still needs to drive ticks.
|
|
|
|
Successful handlers validate every send target and its aggregate queue
|
|
capacity before updating state, acknowledging the reserved message, and
|
|
enqueueing effects. Invalid effects fail the actor without partial sends or a
|
|
state commit. This is a **logical, single-worker in-memory atomicity guarantee**:
|
|
observers using the runtime see either the transition before commit or after
|
|
commit, not an intermediate state/effect set. It is not crash-safe or durable.
|
|
External side effects performed by native callbacks are outside this boundary
|
|
and cannot be rolled back.
|
|
|
|
Runtime errors retain the last committed state and reserved message, set an
|
|
observable failed status, and emit a failure event. The failed message is not
|
|
silently acknowledged or automatically retried. External sends return errors
|
|
for unknown, unavailable, or full actors.
|
|
|
|
Stopping drops the invocation, retaining committed state, mailbox and reserved
|
|
message. Restarting a stopped or failed actor preserves its actor ID, mailbox
|
|
ID, implementation ID, committed state, and queued-message order; it increments
|
|
its incarnation and puts the reserved message at the front of the queue.
|
|
Restart is explicit. Repeated failure of the same reserved message can therefore
|
|
block later messages until an operator changes the implementation or otherwise
|
|
resolves it; there is no automatic poison-message policy or supervision.
|
|
|
|
### Transition and retry guarantees
|
|
|
|
| Situation | State | Reserved input | Effects | Status |
|
|
| --- | --- | --- | --- | --- |
|
|
| Handler returns a valid transition | New state commits | Acknowledged | All validated sends enqueue | `Idle` |
|
|
| Rune execution or transition decoding fails | Last committed state retained | Retained | None from the returned transition | `Failed` |
|
|
| Any target is unknown, stopped, failed, or over capacity | Last committed state retained | Retained | No sends from the transition enqueue | `Failed` |
|
|
| Stop during an invocation | Last committed state retained | Retained | None from the incomplete transition | `Stopped` |
|
|
| Restart after stop or failure | Retained until retried | Moved to queue front | None until retry completes | `Idle` |
|
|
|
|
Mailbox capacity counts queued plus reserved messages. Before applying a
|
|
transition, the runtime validates all effects, including aggregate sends to a
|
|
single target. For a self-send, the completing input is acknowledged as part
|
|
of the same transition, freeing its reserved slot before the new effects are
|
|
enqueued. An invalid target or over-capacity effect rejects the entire returned
|
|
transition.
|
|
|
|
These are process-local guarantees only. A host crash loses state, mailboxes,
|
|
reservations and events. External/native callback effects may already have
|
|
occurred even when the Rune invocation later fails or is stopped.
|
|
|
|
Lifecycle tests verify that a failed reservation retries from the last
|
|
committed state, that stop/restart preserves reserved-first FIFO order, and
|
|
that a running actor's queued messages are not processed until the reservation
|
|
finishes. Transition tests cover malformed results, runtime failures, invalid
|
|
targets, aggregate capacity, and successful message effects.
|
|
|
|
## Limits
|
|
|
|
This is not yet a full image: no persistent mailboxes, transactional outbox,
|
|
supervisors, hot upgrades, API server, GUI, or automatic image bootstrap.
|
|
The example default script is bundled through `include_str!`.
|
|
Messages are not accepted by stopped/failed actors.
|
|
|
|
Native functions must be nonblocking. Dropping a future proves cleanup only
|
|
for the controlled test operation, not rollback of arbitrary external effects.
|
|
Instruction slices cannot interrupt a native callback.
|
|
|
|
The [execution decision](decisions/0001-worker-local-execution.md) records the
|
|
scheduling trade-offs. Heap profiling and source-level debugging remain open.
|