fix: docs directory cleaning

This commit is contained in:
Developer 2026-02-13 14:52:41 +07:00
parent 7f05b03a76
commit b432ea557d
7 changed files with 0 additions and 189 deletions

View file

@ -1,74 +0,0 @@
# Dead-Node Reprobe — Design & Rationale
## Problem
When a network partition heals, SWIM nodes on both sides may have declared each
other Dead. The `alive_members()` filter excludes Dead nodes from probing
targets, so neither side initiates communication — creating a **permanent split**
even after connectivity is restored.
The existing refutation mechanism (incarnation bump on learning of own death
declaration) handles resurrection correctly, but depends on someone *telling*
the dead-declared node about its status. With no probes to dead nodes, nobody
does.
## Solution: Independent Dead-Node Reprobe Cycle
Added a lightweight reprobe mechanism to `SwimProbe` that periodically pings
dead nodes. The existing piggyback + refutation mechanism handles the rest:
1. **Reprober** pings dead node with piggybacked "you are Dead(inc=N)"
2. **Target** receives piggyback, sees it's declared Dead → refutes → bumps incarnation
3. **Target** replies with Ack carrying piggybacked "I'm Alive(new_inc)"
4. **Reprober** applies piggyback → target transitions Dead→Alive
Key insight: we re-enqueue the death declaration in the dissemination queue
before packing the reprobe ping's piggyback. Without this, the original death
declaration's transmit budget would be long exhausted, and the piggyback would
carry no useful membership info.
## Design Decisions
### Why inside SwimProbe (not SwimNode)?
- SwimProbe owns the tick counter, sequence counter, and member list access
- All probe-related logic stays in one place
- The reprobe is a simple independent cycle — doesn't interfere with ProbePhase
### Why no new wire messages?
- `SwimAction::SendPing` works identically for normal probes and reprobes
- The ack from a reprobe targets a different sequence than the current probe
cycle, so the probe state machine ignores it — but the piggyback is applied
at the SwimNode layer before the probe state machine sees the ack
### Configuration
- `dead_reprobe_interval: u64` (default: 50 ticks, ~5× probe_interval)
- Set to 0 to disable completely
- Existing test configs use 0 to avoid interference with timing
## Files Changed
| File | Change |
|------|--------|
| `crates/distribution/src/swim/probe.rs` | `dead_reprobe_interval` in config, `maybe_reprobe_dead()` method |
| `crates/distribution/src/swim/member_list.rs` | Added `dead_members()` |
| `crates/distribution/src/swim/node.rs` | Re-enqueue death declaration in `translate_probe_actions` |
| All `SwimConfig` struct literals | Added `dead_reprobe_interval` field |
## Edge Cases
- **Truly dead nodes**: Reprobe ping is lost (no ack), no harm done
- **All members dead**: Reprobe cycles through them round-robin
- **Concurrent reprobe + normal probe**: Independent, different sequences
- **Dissemination budget**: Death re-enqueued fresh each reprobe, not stale
## Alternatives Considered
1. **Dead node grace period + resurrection timer**: More complex, adds new
state tracking alongside suspicion timers. Rejected for simplicity.
2. **Periodic re-join via seed nodes**: Requires seed node availability,
doesn't work when seed is itself dead-declared. Rejected.
3. **Direct liveness inference from Ping reception**: Would require changing
`handle_ping` to special-case pings from dead nodes. More invasive.

View file

@ -1,115 +0,0 @@
# Wasm Actor
The `swactor-wasm-actor` crate runs WebAssembly guest code inside a swactor
actor. The Wasm instance is sandboxed by [wasmtime](https://wasmtime.dev/).
## Architecture
```
┌─ Runtime ──────────────────────────────────────────────────────────────┐
│ │
│ ┌─ WasmActor ──────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Store<HostState> -- wasmtime store with outbox │ │
│ │ Memory -- guest linear memory │ │
│ │ alloc: TypedFunc -- guest allocator │ │
│ │ handle: TypedFunc -- guest message handler │ │
│ │ │ │
│ │ impl ActorInterface for WasmActor │ │
│ │ Incoming = ByteMessage │ │
│ │ Response = () │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Native Actors ─────────────────────────────────────────────────┐ │
│ │ (can exchange ByteMessage with WasmActors normally) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
```
## Message Flow
```
Host Guest (Wasm)
──── ────────────
ByteMessage arrives
│
├─1─ call alloc(len) ──────────► bump-allocate, return ptr
│
├─2─ write bytes at ptr ───────► (memory updated)
│
├─3─ call handle(ptr, len) ────► process message
│ │
│ ◄── swactor.send() ────────────┤ (0..N times)
│ (buffered in HostState.outbox) │
│ │
├─4─ drain outbox ◄────────────── handle returns
│
v
ctx.send(dest, ByteMessage) for each outbox entry
```
## Guest Contract
Guests are standalone `wasm32-unknown-unknown` modules. They export three
symbols and may import one:
| Direction | Module | Symbol | Signature |
|-----------|--------|--------|-----------|
| **export** | — | `memory` | linear memory |
| **export** | — | `alloc` | `(i32) -> i32` |
| **export** | — | `handle` | `(i32, i32) -> ()` |
| **import** | `swactor` | `send` | `(i32, i32, i32) -> ()` |
The `send` import takes `(dest_ptr, payload_ptr, payload_len)` where
`dest_ptr` points to a 32-byte `ActorAddress` in guest memory.
## Usage
```rust
use swactor::runtime::{Runtime, RuntimeConfig};
use swactor_wasm_actor::{ByteMessage, SharedEngine, WasmActorBuilder};
// Create a shared engine (once)
let engine = SharedEngine::new().unwrap();
// Build an actor from .wasm bytes
let wasm_bytes = std::fs::read("my_guest.wasm").unwrap();
let actor = WasmActorBuilder::new(engine, wasm_bytes)
.build()
.unwrap();
// Use it like any other actor
let rt = Runtime::new(RuntimeConfig::default());
let addr = rt.spawn(actor).unwrap();
rt.send_to(addr, ByteMessage(b"hello".to_vec())).unwrap();
rt.tick();
```
## Sandboxing
The `SharedEngine` disables all optional Wasm proposals:
- Threads — disabled
- SIMD / relaxed SIMD — disabled
- Reference types — disabled
- Multi-value — disabled
- Bulk memory — **enabled** (required by most Rust/LLVM toolchains)
No WASI imports are linked. Guests have no access to the filesystem, network,
clock, or random number generator. The only host function available is
`swactor.send`.
## Where Things Live
| File | Purpose |
|------|---------|
| `crates/wasm-actor/src/lib.rs` | `ByteMessage` + re-exports |
| `crates/wasm-actor/src/engine.rs` | `SharedEngine` — sandboxed wasmtime config |
| `crates/wasm-actor/src/builder.rs` | `WasmActorBuilder` — compile, link, instantiate |
| `crates/wasm-actor/src/actor.rs` | `WasmActor` — `ActorInterface` impl |
| `crates/wasm-actor/src/error.rs` | `WasmActorError` |
| `crates/wasm-actor/tests/guests/` | Three test guest crates (echo, double, silent) |
| `crates/wasm-actor/tests/wasm_actor.rs` | 7 integration tests |