metric

Slow-Call Rate

also called Slow Call Ratio, Latency-Based Trip Signal

The share of calls to a dependency that exceed a latency threshold, used as a circuit-breaker trigger because a dependency that is slow rather than erroring never raises the error rate.

circuit breakerresilience4jtimeoutssaturationthread pool

A breaker configured to open at 50% errors over ten seconds protects nothing against the most common dependency failure. The dependency is not returning errors; it is answering in 8 seconds. Error rate stays at zero, the breaker stays closed, and caller threads accumulate until the caller fails - which is the exact failure the breaker was installed to prevent.

Slow-call rate is the signal that closes that gap: the proportion of calls whose duration exceeds a configured threshold, trippable in its own right. Resilience4j implements it directly, with a slow-call duration threshold and a slow-call rate threshold alongside the familiar failure-rate threshold.

Why it matters

Every caller has finite concurrency, and Little's Law decides how fast it is consumed. At 200 requests per second against a dependency that moves from 50 ms to 3 seconds, in-flight calls rise from 10 to 600. A caller with a 100-thread pool is exhausted in well under a second, and it then fails requests that never touch the slow dependency at all, because the pool is shared. The outage spreads along the dependency graph without a single error being returned anywhere.

Measuring slow-call rate also makes the fault visible before it is fatal. Error rate is a step function; latency distribution is a gradient, and the gradient gives minutes of warning.

Implementation patterns

  • Set the threshold from the caller's budget, not the dependency's p99. If the caller must answer in 300 ms and has already spent 120 ms, a call that takes 400 ms is slow by definition, whatever the dependency considers normal.
  • The cheaper equivalent of a slow-call trigger is a correct timeout: a timeout below the caller's remaining budget converts slow into an error, after which an error-rate breaker works. Deadline propagation makes this automatic.
  • Trip on rate over a window with a minimum call count, so three slow calls at 3 a.m. do not open the breaker.
  • Pair with a bounded concurrency limit per dependency, which caps the damage even when no breaker has tripped yet.
  • Alert on the metric before the breaker acts: slow-call rate above a few per cent is a dependency-degradation signal worth a ticket, even when the breaker's threshold is 50%.

Industry example

Published engineering accounts of large-scale overload control treat latency rather than errors as the primary trigger for shedding. Tencent's DAGOR work for the WeChat microservice fleet (SoCC 2018) uses queueing delay inside each service to decide what to admit, and CoDel (Nichols and Jacobson, 2012) does the same for packet queues: the common finding is that time-in-system detects the fault while counting failures does not. Resilience4j's slow-call thresholds are the library-level version of the same insight, applied at the caller.

Failure scenarios

  • The classic: dependency at p99 of 8 seconds, zero errors, breaker closed, caller's thread pool exhausted, outage attributed to the caller.
  • Threshold set to the dependency's own p99, so the breaker only trips when the dependency is already far outside its own normal and the caller has long since saturated.
  • Breaker opens on a rate computed over too few calls, flapping between open and closed and making the dependency's recovery harder.
  • Retries counted as separate calls, so a retry storm lowers the apparent slow-call rate by flooding the window with fast failures.
  • Slow-call rate measured at the server, where it misses queueing, connection acquisition and DNS - the places the time is actually spent.

Trade-offs

Tripping on latency means cutting off a dependency that is still returning correct answers, which is a real cost: some callers would rather have a 4-second success than an immediate failure, particularly for a write. It also adds two parameters that must be revisited whenever the caller's budget changes, and a badly chosen pair produces an open breaker during a harmless garbage-collection pause. The alternative is worse: a breaker that only understands errors is blind to the failure mode that actually takes callers down.

When not to use it

For a dependency that is either healthy or hard-down - a DNS lookup, a connection-refused endpoint - error rate alone is sufficient and the extra configuration buys nothing. For a long-running operation whose duration is legitimately variable, such as a report generation or a model inference with a wide latency distribution, a slow-call trigger will fire on normal behaviour; move that work to a queue with its own admission control rather than putting a latency breaker in front of it. And if the service already enforces a propagated deadline on every hop, slow is already an error and the simpler error-rate breaker is enough.

Interview question

Q: Your circuit breakers are configured on error rate and a dependency's p99 just went from 60 ms to 9 seconds with no errors. Walk me through what happens in the next sixty seconds, and what you change so it does not happen again.

What a strong answer covers: the Little's Law arithmetic from arrival rate and service time to pool exhaustion; the spread to unrelated paths through the shared pool; the misleading green error-rate dashboard; the fix in order of deployability (a timeout under the remaining budget first, then slow-call rate on the breaker, then bounded per-dependency concurrency, then propagated deadlines); and the alerting change - slow-call rate and age of oldest in-flight call, not error rate.

Quick check

Quiz: Why does an error-rate circuit breaker fail to protect a caller from a slow dependency? Slow calls are not errors, so the rate never rises while caller threads pile up waiting.

Flashcard: Where does the slow-call duration threshold come from? The caller's remaining latency budget, not the dependency's own p99 - otherwise the breaker trips long after the caller has saturated.