Introduction

Every router on the Internet has a queue: a short waiting room where packets sit before they are forwarded. When traffic arrives faster than the link can drain it, the queue grows. Fill it completely and new packets are dropped. Leave it only half-full and the link sits idle. Finding the right balance is one of the oldest problems in networking.

For most of the 1990s, routers used a strategy called tail-drop: accept packets until the buffer is full, then discard. Simple — but disastrous. A large buffer means many packets are accepted before any signal reaches the sender, so TCP backs off late and the whole connection spends minutes at high latency. That phenomenon is bufferbloat: the paradox where adding more buffer to a router makes your video call worse.

Active Queue Management (AQM) is the family of algorithms that solved this. Instead of waiting for the buffer to overflow, an AQM algorithm drops or marks packets early, while the queue is still short. The signal reaches TCP fast, senders slow down sooner, and the queue stays shallow — which keeps latency low for everyone sharing the link.

The two landmark algorithms are RED (Random Early Detection, Sally Floyd & Van Jacobson, 1993) and CoDel (Controlled Delay, Kathleen Nichols & Van Jacobson, 2012). RED was the first practical AQM and shipped in countless routers; CoDel is parameter-free and has become the modern default in Linux and home routers worldwide.

Try It

This simulator runs three queue strategies side by side. Packets arrive in bursts; each strategy decides whether to drop early or wait for the buffer to fill.

<!-- {{c_title_comment}} -->
<div class="controls">
  <label>{{label_burst}}: <input id="burst" type="range" min="1" max="20" value="10"> <span id="burst-val">10</span> {{unit_pkts}}</label>
  <label>{{label_buffer}}: <input id="bufsize" type="range" min="10" max="60" value="30"> <span id="buf-val">30</span> {{unit_pkts}}</label>
  <div class="btn-row">
    <button id="btn-run" type="button">{{btn_run}}</button>
    <button id="btn-pause" type="button" class="ghost" disabled>{{btn_pause}}</button>
    <button id="btn-reset" type="button" class="ghost">{{btn_reset}}</button>
  </div>
</div>
<div class="panels">
  <div class="panel" id="panel-tail">
    <div class="panel-title">{{title_tail}}</div>
    <canvas id="cv-tail" width="220" height="120"></canvas>
    <div class="metric">{{label_avg_lat}}: <span id="lat-tail">0</span> ms</div>
    <div class="metric">{{label_dropped}}: <span id="drop-tail">0</span></div>
  </div>
  <div class="panel" id="panel-red">
    <div class="panel-title">{{title_red}}</div>
    <canvas id="cv-red" width="220" height="120"></canvas>
    <div class="metric">{{label_avg_lat}}: <span id="lat-red">0</span> ms</div>
    <div class="metric">{{label_dropped}}: <span id="drop-red">0</span></div>
  </div>
  <div class="panel" id="panel-codel">
    <div class="panel-title">{{title_codel}}</div>
    <canvas id="cv-codel" width="220" height="120"></canvas>
    <div class="metric">{{label_avg_lat}}: <span id="lat-codel">0</span> ms</div>
    <div class="metric">{{label_dropped}}: <span id="drop-codel">0</span></div>
  </div>
</div>
<div id="status-bar"></div>
/* {{c_css_comment}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: 14px .5rem .5rem; }
.controls { display: flex; flex-direction: column; gap: .4rem; margin-bottom: .6rem; }
.controls label { font-size: .85rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
input[type=range] { width: 110px; }
.btn-row { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .2rem; }
button { font: 600 13px system-ui; padding: .35rem .8rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: not-allowed; }
.panels { display: flex; gap: .6rem; flex-wrap: wrap; }
.panel { background: #f4f7fa; border: 1px solid #cdd9e3; border-radius: 10px;
         padding: .5rem .6rem; min-width: 200px; flex: 1; }
.panel-title { font-weight: 700; font-size: .85rem; margin-bottom: .3rem; color: #1d3557; }
canvas { display: block; width: 100%; height: auto; border-radius: 5px; background: #fff; border: 1px solid #e0e6ed; }
.metric { font-size: .8rem; margin-top: .25rem; color: #444; }
.metric span { font-weight: 700; color: #1d3557; }
#status-bar { font-size: .82rem; margin-top: .5rem; min-height: 1.2em; color: #555; }
// Code not found

Notice how Tail-Drop (no AQM) lets the queue fill completely before dropping — latency climbs to the buffer limit and stays there. RED starts dropping probabilistically once the average queue crosses a threshold, keeping latency lower on average. CoDel targets a minimum delay (5 ms here) and drops only when packets sit longer than that — the queue stays near-empty and latency is consistently low.

The key insight: a packet dropped early by AQM costs one retransmission. A full buffer delayed by tail-drop costs hundreds of milliseconds for every packet in the queue.

The Real Complexity

Both RED and CoDel are solved, deployed algorithms — the engineering challenge was finding the right signal and making the control loop stable.

RED — Random Early Detection (Floyd & Jacobson, 1993)

RED computes an exponential weighted moving average of the queue length, qˉ\bar{q}:

qˉ(1wq)qˉ+wqq\bar{q} \leftarrow (1 - w_q)\,\bar{q} + w_q\,q

where wqw_q is a small weight (typically 0.0020.002) and qq is the instantaneous queue size. When qˉ\bar{q} crosses a minimum threshold minth\min_{th}, RED begins dropping packets with probability pp, rising linearly to pmaxp_{max} at maxth\max_{th}:

p=pmaxqˉminthmaxthminthp = p_{max} \cdot \frac{\bar{q} - \min_{th}}{\max_{th} - \min_{th}}

Randomness spreads the drops across different TCP connections so no single flow is punished while others go unchecked. RED's weakness: those three parameters (wqw_q, minth\min_{th}, maxth\max_{th}) must be tuned per link — and they are hard to set correctly.

CoDel — Controlled Delay (Nichols & Jacobson, 2012)

CoDel sidesteps the parameter problem by measuring the sojourn time — how long each packet actually waits inside the queue. Every packet is timestamped on arrival; when it is dequeued, CoDel records its wait. If the minimum sojourn time over a sliding window of length TT (100 ms) exceeds a target τ\tau (5 ms), CoDel declares the queue is persistently full and begins dropping. The drop rate increases as 1/t1/\sqrt{t}, where tt is time since the overload began — matching the square-root throughput of TCP.

The elegance: CoDel needs no per-link tuning. The 5 ms and 100 ms constants work across a vast range of access speeds because they track time, not queue length in bytes — a metric that is invariant to link speed.

Both algorithms exploit the same feedback loop: early signals → TCP slows → queue drains → latency falls. The difference is in what they measure and how they adapt.

Where It Matters

AQM moved from research papers into every corner of networking:

  • Home routers: fq-CoDel (fair-queue CoDel) ships in OpenWrt, DD-WRT, and most ISP-supplied routers since 2014. Enabling it on a congested home connection can cut ping times from 200 ms to under 10 ms without reducing throughput.
  • Linux kernel: tc qdisc fq_codel is the default queueing discipline in Linux since kernel 3.5 and is used on billions of Android and desktop devices.
  • Data-center switches: DCTCP (Data Center TCP) pairs with ECN-capable AQM (marking instead of dropping) to keep queues near-empty in fat-tree networks, dramatically reducing tail latency for distributed databases.
  • Video calls and gaming: a single large download sharing a link with a video call becomes tolerable under AQM, because the call's small packets are no longer stuck behind a buffer full of bulk data.
  • Streaming and CDNs: AQM at the network edge helps ABR (adaptive bitrate) streaming algorithms converge faster because the bandwidth signal they receive reflects current congestion, not a queue that has been full for several seconds.

AQM is also the complement to max-flow thinking: where max-flow asks "how much can the network carry?", AQM asks "how do we keep the carrying fast and fair for everyone?"

Conclusion

Active Queue Management turns an apparent paradox into a solution: deliberately dropping packets early keeps the Internet faster and fairer than letting buffers fill. RED showed in 1993 that probability-based early detection could replace tail-drop without sacrificing throughput. CoDel showed in 2012 that you do not even need tuning — just measure sojourn time, and the queue manages itself.

The next time your video call stays smooth while someone else downloads a large file on the same connection, thank the router's AQM algorithm. It is doing something counter-intuitive at wire speed: choosing to discard data so that less data gets stuck.

For a deeper look at how networks route those packets before the queue even forms, see the related article on max-flow.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/active-queue-management/Content licensed under CC BY-NC 4.0.