[3] A Request-Count Autoscaling Metric Couldn’t Detect Token-Heavy Load

The trick: autoscale on the actual driver of load, not on request count. A custom, high-resolution CloudWatch metric can turn a three-minute scaling decision into a twenty-second one.

Issue

A GPU-backed LLM inference endpoint on SageMaker queued and ran at elevated latency for minutes during traffic bursts — even though autoscaling was configured correctly and did eventually add capacity.

Root Cause

The built-in target-tracking metric, SageMakerVariantInvocationsPerInstance, counts requests, not compute. Ten short requests and ten requests each generating thousands of output tokens look identical to it. Combined with CloudWatch’s 1-minute metric period and the consecutive breaches Application Auto Scaling requires before acting, a genuine GPU-saturating burst took roughly three minutes to trigger a scale-out — by which point the queue had already backed up twice over.

Solution

Publish a custom high-resolution CloudWatch metric directly from the inference container — queue depth weighted by expected output length — at StorageResolution: 1, roughly every 10–15 seconds, and point the scaling policy at that instead of the built-in metric. Scaling decisions dropped to about twenty seconds. Because the metric now tracked actual load instead of raw count, instance-hours fell 9–32% against the built-in policy by not over-provisioning for bursts of cheap requests.

💡 Takeaway

  • A request-count metric and a compute-cost metric look identical until load becomes heterogeneous — which for LLM serving, it always eventually does.
  • CloudWatch’s metric period and evaluation-periods setting are latency baked into your scaling loop; a coarser metric than your traffic pattern needs will always lag it.
  • The best autoscaling metric is rarely the one the platform ships by default — it’s the one that actually predicts the resource you’re trying to protect.