WeChat's DAGOR overload control, published at SoCC 2018, profiles a server's load from the average waiting time of requests in its pending queue rather than from CPU utilisation, sheds by business and user priority, and propagates admission levels between services. Why is queuing time the right signal, and why does the propagation matter more than the shedding?
Show the full answer Hide the answer
The situation they were in
A microservice estate serving an extremely large messaging and payments product, with deep call graphs where one user action fans out across many services. Overload in such a graph is not a local event: a saturated service at depth five converts work already performed at depths one to four into waste, and that waste is itself a cause of the overload.
Why queuing time rather than CPU
CPU utilisation is ambiguous and lagging. Eighty percent can mean healthy pipelining or imminent collapse, and it says nothing about whether anyone is waiting. It also means different things on different hardware, for different workloads, and not at all for a service whose work is I/O bound.
Average waiting time in the pending queue measures the thing that matters directly. It is a backlog measurement rather than a proxy for one, it moves before saturation appears in utilisation, and the same threshold carries the same meaning on a bigger machine or a newer generation of hardware. That portability is what lets one mechanism be configured once for a whole estate rather than tuned per service, which the paper describes as being service agnostic.
Why propagation matters more than shedding
Local shedding protects the shedding service and wastes everything upstream. A request rejected at the fifth hop has already consumed four services' capacity, and under overload that wasted work is the largest remaining term.
DAGOR propagates the admission level back to callers, so upstream services stop sending requests that will be rejected. The shed then happens near the edge, costing one unit of work instead of five. This is the difference between load shedding, which is local and reactive, and overload control, which is a feedback loop across the fleet.
Why two dimensions of priority
- Business priority alone drops a whole feature for everyone, which is a blunt instrument and politically difficult to set.
- User priority alone, applied per request, admits a random fraction of every request — so a user who succeeded at step one fails at step three. The paper reports that session-oriented admission tends to degrade the user experience, and that user-oriented admission control is as effective for load control.
- Combined, the least important features are dropped first, and within a feature the same users are consistently admitted, so those who get through complete their journey rather than failing halfway.
What it costs
A priority scheme every service must honour and every product team must argue about · a priority marker that survives every hop, including asynchronous ones · a control loop that oscillates if the evaluation window is too short · and the political cost of writing down, in public, which features matter least.
Where copying it would be a mistake, and when not to adopt it
An organisation with twenty services and no overload problem gains nothing and acquires a cross-cutting protocol that every future service must implement. The mechanism earns its cost where call graphs are deep and the wasted upstream work dominates.
The cheaper 80% is available to almost everyone: per-dependency concurrency limits plus a deadline propagated from the edge. Concurrency limits stop a caller from queueing unbounded work at a struggling dependency, and a propagated deadline means each hop can refuse work that cannot finish in time — which recovers most of the "do not do work that will be thrown away" benefit without a fleet-wide priority protocol. Start there.