fix: docs directory cleaning
This commit is contained in:
parent
7f05b03a76
commit
b432ea557d
7 changed files with 0 additions and 189 deletions
|
|
@ -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.
|
|
||||||
|
|
@ -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 |
|
|
||||||
Loading…
Reference in a new issue