Designed and built a replacement autoscaler
The fix was not faster containers — it was taking ownership of the decision. We designed and implemented a purpose-built autoscaler: a 256 MB Go Lambda that reads each tier’s queue depth and running count directly, computes that tier’s desired count itself, and writes it to the service. AWS’s managed policy was removed from the path rather than tuned, because the problem was its reaction model, not its configuration.
The control law is three steps, and the order matters: required capacity is depth divided by jobs per worker; clamp to the tier’s floor and ceiling; then clamp again to within five of what is currently running. Two of the inputs are where the correctness lives. Depth counts in-flight messages as well as visible ones, because visible-only collapses the fleet to its floor the instant workers pick work up. And running count is never added to demand — running workers are capacity, not work.
That second rule was learned the hard way. An earlier formulation added running count to depth and used running as the lower clamp; during a rolling deploy, where old and new tasks briefly overlap, that ratcheted the desired count upward by one every tick against an empty queue until it reached 432 tasks and exhausted the subnet’s free addresses. The regression is now pinned by a test that asserts a fleet above its own ceiling must drain, not hold. A second guard rejects a negative per-tick clamp at load time instead of coercing it, because that value inverts the clamp window and silently defeats the ceiling — and a Lambda that refuses to start freezes the fleet and trips an alarm, where a silently corrected one scales on a number nobody wrote.
The single symmetric clamp is the only damping in the system: no cooldown, no hysteresis, no smoothing. One number bounds how fast a burst can be answered and how fast an idle fleet drains. Because the scheduler’s floor is one minute, the Lambda loops eleven times at five-second spacing inside a single sixty-second invocation, skipping the trailing sleep so invocations can never overlap — a five-second decision cadence on a scheduler that only sells sixty. And because clamping to the ceiling is lossy exactly where an operator needs the truth, every tick also publishes the shortfall between demanded and permitted capacity, so a tier pinned at its maximum reports how far past it demand went.