Add optional bounded mailboxes to prevent unbounded memory growth.
MailboxOverflow enum: DropNewest (discard incoming) or DropOldest
(evict oldest to make room). Default capacity=0 preserves unbounded
behavior for full backward compatibility.
messages_dropped counter added to WorkerStats for observability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blind round-robin placement with load-aware strategy that reads
per-worker stats (actor count + mailbox depth) and biases toward lighter
workers. Falls back to round-robin when stats are equal (initial burst).
Deep research on work stealing across Tokio (steal-half, LIFO slot),
Go (M:N scheduler), BEAM (proactive migration), and ForkJoinPool.
Full actor migration is feasible but deferred due to message-loss window.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Research across ractor, tokio, Erlang/OTP BEAM, Linux CFS, and libuv
revealed that tick_all drained the entire mailbox per actor per tick,
allowing one hot actor to starve all others on the same worker.
- Add `actor_message_budget` to RuntimeConfig (default: 64 msgs/actor/tick)
- Modify tick_all to break after budget messages, yielding to next actor
- budget=0 restores unlimited (backward compatible) behavior
- 3 new fairness tests validating hot-cold actor scenarios
- New fairness benchmark group (cold_latency_under_pressure, throughput_by_budget)
- Fix RuntimeConfig struct literals across workspace crates
Inspired by BEAM's 4000-reduction budget and tokio's 128-op cooperative budget.
All 45 tests pass (42 original + 3 new).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>