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>
2.7 KiB
2.7 KiB
Progress Log
Current Stage: Phase 1 — Research + First Improvement Cycle
Status: Cycle 1 COMPLETE
Plan Overview
- Phase 0: Codebase audit — understand current swactor architecture, existing tests, benchmarks ✅
- Phase 1: Broad survey + interleaved improvements
- Phase 2: Deeper improvements based on findings
- Phase 3: Testing methodology improvements
- Phase 4: Final evaluation & documentation
Completed This Session
Cycle 1: Fairness (Message Budget)
- Research: Studied ractor, tokio, Erlang/OTP BEAM, Linux CFS/EEVDF, libuv
- Finding:
tick_alldrained ENTIRE mailbox per actor per tick — critical fairness bug- BEAM uses 4000 reduction budget, tokio uses 128-op cooperative budget
- Swactor had zero budget — one hot actor could starve all others on same worker
- Implementation: Added
actor_message_budgettoRuntimeConfig(default: 64)- Modified
tick_allto break afterbudgetmessages per actor budget=0means unlimited (backward compatible)
- Modified
- Tests: 3 new fairness tests (hot_actor_does_not_starve_cold_actor, unlimited_budget_drains_all, budget_messages_drain_across_multiple_ticks)
- Benchmarks: Added fairness benchmark group (cold_latency_under_pressure, throughput_by_budget)
- Fixes: Updated RuntimeConfig struct literals across crates (python, runtime-dashboard, mt_benchmarks)
- Result: 45 tests pass (42 original + 3 new), all workspace crates compile
Research Notes
- Full analysis in
CLAUDE/notes/research_synthesis.md - Baseline benchmarks in
CLAUDE/notes/baseline_benchmarks.md - Constraints in
CLAUDE/notes/constraints.md
Next Steps
- Cycle 2: Stress testing + property-based tests
- Concurrent spawn+send stress tests
- Multi-threaded fairness validation
- Property: message ordering preserved under budget
- Property: all messages eventually delivered with budget > 0
- Cycle 3: Adaptive backoff with thread parking
- Replace spinning with condvar-based parking (from tokio parker design)
- Benchmark latency improvement under varying load
- Cycle 4: Enhanced benchmarks
- Message size sensitivity (8B, 64B, 256B, 1KB)
- Latency percentiles (p50, p99, p999)
- Many-to-one fanin contention
- Cross-worker vs same-worker delivery comparison
- Cycle 5: Work stealing exploration
- Evaluate feasibility of actor migration between workers
- BEAM two-tier approach: reactive steal + periodic migration
Open Questions
- Should budget be configurable per-actor (not just per-runtime)?
- Is 64 the right default budget? Benchmarks show budget=32 slightly faster for throughput
- Thread parking: how to handle the notification mechanism without adding deps?
Blockers
- (none)