Techzone/In Practice: Bucket is a Finite Queue that Outputs at a Finite Rate

In Practice: Bucket is a Finite Queue that Outputs at a Finite Rate

5 min readArticle

The theoretical leaky bucket is clean: infinite bucket, constant drain. In reality, every implementation uses a finite queue (bounded buffer) and a finite, configurable drain rate. These two constraints define the practical behavior — and the failure modes.

The Finite Queue (Bounded Buffer)

The "bucket" in practice is a FIFO queue with a maximum depth — measured in packets or bytes:

shell
Queue depth B:   maximum packets (or bytes) the buffer holds
Drain rate R:    packets per second output to the network
Arrival rate A:  incoming packet rate (variable)

If A <= R:   queue stays empty or small, no drops
If A > R:    queue fills at rate (A - R)
If queue full AND A > R:  OVERFLOW — incoming packets are dropped

The finite queue means burst absorption is limited. If a burst exceeds the bucket depth, packets are lost. This is by design: the algorithm enforces a contract.

Overflow Behavior: Drop vs Mark

When the finite queue is full, two options:

Drop (Tail Drop)

The simplest — incoming packets that arrive at a full queue are discarded silently. The sender has no signal other than eventual timeout or NACK.

bash
# Tail drop is the default in Linux tc qdisc
# View drops with
ip -s link show eth0
# TX:  bytes packets errors dropped overrun mcast

Active Queue Management (AQM) — Mark Instead of Drop

Modern implementations use AQM like RED (Random Early Detection) or CoDel to:

  • Start randomly dropping (or ECN-marking) packets before the queue is full
  • Signal congestion early to TCP senders so they reduce cwnd before a full overflow
  • Prevent "synchronization" where all TCP flows back off simultaneously
bash
# Use CoDel (Controlled Delay) AQM instead of tail-drop
sudo tc qdisc add dev eth0 root codel

# Use FQ-CoDel (Fair Queue CoDel) — better for multi-flow environments
sudo tc qdisc add dev eth0 root fq_codel

# Check parameters
tc qdisc show dev eth0

Linux tc Implementation: tbf (Token Bucket Filter)

Linux's primary rate-shaping qdisc is tbf. Despite the name, it implements leaky bucket behavior for shaping (and token bucket internally for the math):

bash
# Add tbf qdisc: rate 10mbit, burst 32kbit, max latency 50ms
sudo tc qdisc add dev eth0 root tbf \
    rate 10mbit \
    burst 32kbit \
    latency 50ms

# Parameters explained:
#   rate    = drain rate R (desired output rate)
#   burst   = minimum burst size the kernel can handle (relates to HZ)
#   latency = max time a packet waits in queue before being dropped
#             (determines effective queue depth: latency * rate)

# Queue depth in bytes = latency * rate
# 50ms * 10Mbit = 0.05s * 1,250,000 B/s = 62,500 bytes

# View stats including drops and overlimits
tc -s qdisc show dev eth0
# Output:
# qdisc tbf 8001: root refcnt 2 rate 10Mbit burst 32Kb lat 50.0ms
#  Sent 8192000 bytes 5461 pkt (dropped 1200, overlimits 1200 requeues 0)
#  rate 9.84Mbit 8Kpps
#  backlog 0b 0p requeues 0

Policing vs Shaping

These are two different uses of the finite queue + finite rate model:

Shaping Policing
Mechanism Queue packets, delay them Drop or remark packets immediately
Burst handling Absorbs into queue Drops immediately on overflow
Adds delay Yes (queue latency) No
Location Typically egress Typically ingress
Linux tool tc tbf, tc htb tc police, iptables -m limit
bash
# SHAPING on egress (delay packets to conform to rate)
sudo tc qdisc add dev eth0 root tbf rate 10mbit burst 32kbit latency 50ms

# POLICING on ingress (drop packets that exceed rate)
sudo tc qdisc add dev eth0 handle ffff: ingress
sudo tc filter add dev eth0 parent ffff: protocol ip u32 match ip src 0.0.0.0/0 \
    police rate 10mbit burst 32kbit drop flowid :1

# iptables rate limiting (simpler policing, per-connection)
sudo iptables -A INPUT -p tcp --dport 80 -m limit --limit 100/second --limit-burst 200 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j DROP

Finite Rate and Clock Precision

The output rate is only as accurate as the system scheduler's clock resolution. Linux uses HZ (timer interrupt frequency — typically 250 or 1000 Hz) as the minimum timing granularity:

bash
# Check kernel HZ setting
grep CONFIG_HZ /boot/config-$(uname -r)
# -> CONFIG_HZ=250   means 4ms timer resolution

# At 1Mbit, packet spacing = 1.5ms → timer resolution matters
# Use HFSC or FQ schedulers for sub-HZ accuracy

Netem — Finite Queue with Exact Rate Control

For testing and emulation (not production shaping), netem gives fine-grained control:

bash
# Simulate 1Mbit link with finite queue of 100 packets
sudo tc qdisc add dev eth0 root netem \
    rate 1mbit \
    limit 100      # queue depth = 100 packets (finite!)

# Add packet loss on overflow behavior (already default with limit)
sudo tc qdisc add dev eth0 root netem \
    rate 1mbit \
    limit 100 \
    loss 0.1%    # also add 0.1% random loss to simulate real link

# Show queue stats
tc -s qdisc show dev eth0

Reference

  • Linux tc-tbf man page: man tc-tbf
  • Linux tc-netem: man tc-netem
  • lartc.org — Linux Advanced Routing and Traffic Control HOWTO
  • RFC 2697 / 2698 — Three Color Marking (leaky bucket extensions)
techzonesite.comUnlock Your IT Potential