CONCEPT Cited by 1 source
CUBIC epoch¶
Definition¶
The epoch (epoch_start in the Linux-kernel TCP CUBIC
implementation; congestion_recovery_start_time in quiche) is
CUBIC's reference
timestamp for its growth curve. The CUBIC window function
W_cubic(delta_t) = C·(delta_t − K)^3 + W_max is parameterised
on delta_t = now − epoch_start; the epoch is reset whenever
CUBIC restarts its growth function — most notably after a loss
event reduces cwnd. Between resets, delta_t grows
monotonically with wall-clock time, and cwnd traces the cubic
curve anchored at the epoch (Source:
sources/2026-05-12-cloudflare-when-idle-isnt-idle-how-a-linux-kernel-optimization-became-a-quic-bug).
Why the epoch is load-bearing¶
The epoch is not just a timestamp — it is the load-bearing
state variable that decides how aggressively CUBIC grows cwnd
at any moment. Two structural failure modes hinge on it:
- Epoch stale during application idleness. If the
application stops sending for a while,
delta_t = now − epoch_startgrows while no data moves. The next packet sent after idleness readsdelta_tas huge — and the cubic function computes an enormousbic_target, which CUBIC would try to satisfy by inflatingcwndto an unreasonable value. This is the Jana Iyengar 2017 problem that Linux kernel commit 30927520dbae addressed with "CUBIC after idle: shiftepoch_startforward by the idle duration, preserving the growth-curve shape." This was the correct fix — resetting would instead collapse the growth curve and trigger aggressive cwnd growth, as Neal Cardwell's review caught. - Epoch set into the future. If the idle-shift adjustment
sets
epoch_start > now, thenin_congestion_recovery()returnstrueon every incoming ACK (because recovery is nominally still ahead of us in time),cwndgrowth is skipped, and CUBIC locks the connection into congestion- recovery state. This is the 1-week-later Linux-kernel follow-up fix (commit c2e7204d180f) and the six-year-later quiche fix (Cloudflare 2026-05-12) — both addressing the same invariant violation.
The invariant¶
epoch_start ≤ now must hold at all times. Any adjustment
to the epoch that can push it forward past wall-clock time
violates the CUBIC state machine and causes the growth function
to behave pathologically. The 2017 Linux follow-up commit named
this explicitly in its message: "Let's simply not set
epoch_start in the future, otherwise bictcp_update() could
overflow and CUBIC would again grow cwnd too fast." The
2026-05-12 quiche fix enforces the same invariant via
if idle_start < now guard before advancing
congestion_recovery_start_time.
Naming difference: Linux TCP vs quiche¶
- Linux TCP CUBIC calls this state variable
epoch_startand updates it in both the ACK-processing path (bictcp_acked) and — via the 2017 fix — a dedicatedCA_EVENT_TX_STARTcallback for the idle-period shift. - quiche (Rust) names the corresponding state variable
congestion_recovery_start_time. Because QUIC's CCA runs in user space insideon_packet_sent(), there is no kernelCA_EVENT_TX_STARTcallback; the idle detection and the epoch shift are fused into the send path. That structural difference is the fault-line along which the 2026-05-12 bug hid for five years — see patterns/userspace-port-of-kernel-primitive-risk.
Seen in¶
- sources/2026-05-12-cloudflare-when-idle-isnt-idle-how-a-linux-kernel-optimization-became-a-quic-bug
— canonical wiki instance. The full arc: Jana Iyengar's 2017
initial "reset epoch" fix; Neal Cardwell's "no — shift
forward" correction; the second-bug follow-up week later
("don't set epoch in the future"); the 2020 quiche port that
missed the second fix; the 2026-05-12 three-line re-fix with
last_ack_timeas the secondary anchor.