Sway watches an OpenSearch cluster for hot spots and moves the minimum number of shards needed to relieve them — but only after a circuit breaker checks cluster health, search latency, and in-flight relocations. Every run below is real, deterministic output from the actual simulator, not a mockup.
This replays a captured transcript of ./rebalancer --simulate --cycles 3 —
the simulator is deterministic, so running it yourself produces these exact numbers.
Replay ↻
Each layer exists because the one before it isn't sufficient on its own. If any layer blocks, everything downstream is skipped for that cycle.
dry_run is true in every factory config. The engine plans and logs every move it would make without touching the cluster until you explicitly opt in.
Health, latency, and relocating-shard checks run fresh before every cycle. One failure opens the circuit and aborts the cycle — no partial moves, no half-open state.
max_moves_per_cycle (default 5) and skew_reduction_target (default 25%) mean the engine never tries to fully rebalance in one pass.
A primary and its replica are never planned onto the same destination node, and a move is skipped if it would push the destination's disk past 90%.
opensearch.Client is the only interface the rest of the system depends on — real HTTP client and deterministic simulator both implement it.
_cluster/reroute call.Sway does one job: decide which shards to move, and refuse to move them under the wrong conditions. It's not a chaos-engineering harness, a general agent framework, or a debugging proxy — those are different problems, and they live in different projects.