Foundationপ্রথম নীতি থেকে
LEVEL 7লেসন ১৩/২২রিসার্চ-লেভেল১ ঘণ্টা ৫ মিনিট

Congestion Control — slow start, Reno, CUBIC, BBR

Flow and Congestion Control

১৯৮৬-এ internet গলাটা চেপে মরছিল — TCP-র congestion control-ই তা থেকে ফিরিয়ে এনেছে; slow start থেকে BBR পর্যন্ত তিন প্রজন্মের অ্যালগরিদম, sawtooth-এর ন্যায়বিচার, আর bufferbloat-এর আধুনিক সংকট।

এই লেসন শেষে আপনি পারবেন

  • Flow control (receiver-এর rwnd) বনাম congestion control (network-এর জন্য cwnd)-এর distinction স্পষ্ট করতে পারবেন — effective sending rate min(rwnd, cwnd) কেন, তা যুক্তি দিয়ে ব্যাখ্যা করতে পারবেন
  • Slow start-এর exponential growth, congestion avoidance-এর additive increase আর multiplicative decrease — AIMD-র পুরো চক্রটা cycle-by-cycle হিসাব করতে পারবেন
  • AIMD-র sawtooth কেন fairness-এর দিকে ঝোঁকে — দুই flow-র share convergence-এর জ্যামিতিক যুক্তি আঁকতে পারবেন
  • CUBIC-র cubic window function আর long-RTT friendliness, আর BBR-এর model-based paradigm (bandwidth×RTprop estimate vs loss-signal) — তিন প্রজন্মের দর্শন তুলনা করতে পারবেন
  • Bufferbloat সমস্যাটা ব্যাখ্যা করতে পারবেন — oversized buffer queue latency বাড়ায় কীভাবে, আর FQ-CoDel/AQM কী ভূমিকা নেয়

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

অক্টোবর ১৯৮৬। NSFNET backbone-এ LBL (Berkeley) থেকে NCAR (Colorado)-এ data transfer চলছে — path-এ কয়েকটা gateway, মিলিয়ে মাত্র কয়েক hop। Throughput ছিল 32 kbps। হঠাৎ পড়ে গেল 40 bits per second-এ — হাজার গুণ collapse, মানে প্রায় পুরোপুরি বন্ধ। Router-গুলো ভাঙেনি, link-গুলো ঠিকই ছিল — network শ্বাসরোধে মরছিল তার নিজের users-দের traffic-এই।

কারণটা সরল ও ভয়ংকর: তখনকার TCP packet হারালে retransmit করত, কিন্তু ধীর হতো না। Congested router-এর queue ভরলে packet ফেলে — ফেলে যত বেশি, retransmit তত বেশি — queue আরও ভরে — congestion collapse: পুরো capacity retransmitted duplicate- দের কবরস্থান।

Van Jacobson-এর সমাধান (১৯৮৮): TCP-কে নিজের চারপাশের অবস্থা শেখানো। Loss মানেই congestion-এর whisper — তখন থামো, ধীরে শুরু করো, সাবধানে বাড়ো। এই এক paper-ই আজকের internet-এর সব traffic-কে সহ্যযোগ্য রাখে।

আজকের প্রশ্ন তিনটা:

  1. Receiver-এর flow control আর network-এর congestion control — দুইটা কেন আলাদা সমস্যা?
  2. Slow start, AIMD, fast recovery — যন্ত্রগুলো ঠিক কীভাবে ঘোরে?
  3. Reno → CUBIC → BBR: তিন প্রজন্ম কেন জন্মাল, দর্শনে কোথায় আলাদা?

মূল ধারণা

দুই রকম থ্রটল, দুই রকম উদ্দেশ্য

tcp-reliability লেসনের window মনে আছে? Sender-এর আসল limit তিনটা constraint-এর সবচেয়ে কড়াটা:

EffectiveWindow=min⁡(rwnd, cwnd)\text{EffectiveWindow} = \min(\text{rwnd},\ \text{cwnd})

Flow controlCongestion control
কাকে রক্ষা করেReceiver-এর bufferNetwork (router queue)
Signal sourceReceiver-এর advertisementPacket loss / delay (implicit!)
Variablerwnd — wire-এ যায়cwnd — kernel-এর private
কে ঠিক করেReceiver applicationSender নিজে

Slow start — নাম-বিপরীত বিস্ফোরণ

Connection শুরুতে cwnd ছোট (আধুনিক default IW=10 MSS)। প্রতিটা ACK-এ sender ২টা segment-এর অনুমতি জমায় — ফলে প্রতি RTT-তে window দ্বিগুণ:

RTT:    1    2    3    4     5     6
cwnd:  10 → 20 → 40 → 80 → 160 → 320   ← exponential!

“Slow”-start নামটা historical — পুরনো behavior ছিল পুরো receiver window দিয়ে এক নিঃশ্বাসে ঢোকানো; exponential বৃদ্ধি তার তুলনায় slow। Slow start-এর কাজ: অজানা network-এর ধারণক্ষমতা খুঁজে বের করা দ্রুত, কিন্তু overflow-র আগে।

কতদূর বাড়বে? ssthresh (slow-start threshold) পর্যন্ত — এর পরে ভাঙা-চোরা বৃদ্ধি শুরু।

Congestion avoidance — AIMD-র সমতল রাস্তা

cwnd ≥ ssthresh হলে প্রতি RTT-তে বাড়ে মাত্র ১ MSS (additive):

RTT:    1   2   3   4   5   6   7
cwnd:  16 →17→ 18→ 19→ 20→ 21→ 22   ← linear, ধীর ও সতর্ক

আর loss এলে multiplicative decrease — Reno-তে cwnd অর্ধেক:

ssthresh←cwnd2;cwnd←ssthresh (+3 for Reno’s fast recovery)\text{ssthresh} \leftarrow \frac{\text{cwnd}}{2};\quad \text{cwnd} \leftarrow \text{ssthresh}\ (+3\ \text{for Reno's fast recovery})

এই AIMD — Additive Increase, Multiplicative Decrease — এর উৎপাদ সেই বিখ্যাত sawtooth: ধীরে ওঠা, হঠাৎ অর্ধেকে পড়া, আবার ওঠা।

Sawtooth কেন ন্যায্য — convergence যুক্তি

দুই flow একই bottleneck ভাগ করছে। AIMD-র জাদু: additive ধাপ দুজনের জন্য সমান (একই absolute amount), multiplicative পতন অনুপাতে (relative gap অপরিবর্তিত)। জ্যামিতিতে এর মানে —

flow-B share
 │ A'●            ●      ●
 │   ╲          ╱   ╲  ╱   ╲     ← প্রতিটা তীর: +x উপরে, −50% কোণে নিচে
 │     ╲      ╱        ●
 │  B' ● ╲  ╱
 │       ●╳                 ← সমান-ভাগ রেখা (45°) ধরে ধরে converge
 └──────────────────────► flow-A share
AIMD-র convergence: যে-ই বেশি নিচ্ছে, loss-এ তার পতনই বড়; কয়েক দাঁড়িতেই দুজন সমান share-এর কাছাকাছি — কোনো central coordinator ছাড়াই।

Additive ধাপে gap কমে না, multiplicative পতনে gap অনুপাতে কমে — কিন্তু additive ধাপটা সবসময় সমান, তাই বেশি-নেওয়া flow-র share তুলনামূলক কমে আসে। কোনো server, কোনো coordinator নেই — ন্যায়বিচারটা অ্যালগরিদমের গণিত থেকেই আসে।

Fast recovery — Reno/NewReno-র মসৃণ পতন

৩-dup-ACK-তে (fast retransmit) পুরো slow start-এ ফেরা wasteful — packet তো এখনো চলছে! Reno-র পথ:

  1. ssthresh ← cwnd/2
  2. cwnd ← ssthresh + 3 (in-flight dup-ACK-দের জন্য credit)
  3. Retransmit করে congestion avoidance-এই থেকে যাওয়া (fast recovery) — slow start skip

NewReno-তে একটা সূক্ষ্ম সংশোধন: এক RTT-তে একাধিক segment হারালে (প্রথমটার ACK এলে দ্বিতীয়টার dup-ও আসে) — partial ACK দেখে recovery চালিয়ে যাওয়া, প্রতিটা নতুন dup-এ half না করা।

CUBIC — Linux-এর বর্তমান default

Long-fat network-এ (বড় bandwidth × বড় RTT) Reno-র linear বৃদ্ধি আত্মঘাতী: 10 Gbps, 100ms path-এ অর্ধেক হওয়ার পরে আগের জায়গায় ফিরতে ঘণ্টা লাগে। CUBIC-র সমাধান: window-কে time-এর cubic function বানানো —

W(t)=C (t−K)3+WmaxW(t) = C\,(t-K)^3 + W_{max}

(C=0.4, W_max = শেষ loss-এর window, K = সেখানে পৌঁছানোর সময়)। আকৃতিটা: loss-এর পরে দ্রুত নিচে নেমে, plateau-তে সাবধানী (W_max- এর কাছে ধীর — আগের ceiling সম্মান!), তারপর আবার ত্বরান্বিত। দূরত্ব যত বড়, cubic-র acceleration তত কাজের — long-distance transfer-এ Reno-কে চিত করে। 2006 থেকে Linux-এর default।

BBR — loss নয়, model

Google-এর BBR (2016) paradigm shift: loss-কে congestion-এর signal মানাটাই ভুল হতে পারে (wireless-এ loss ≠ congestion!)। BBR সরাসরি model বানায়: বর্তমান Bottleneck Bandwidth আর minimum RTprop মেপে — BDP (Bandwidth-Delay Product) ঠিক এতটাই পাঠায়, queue না জমিয়ে। Pacing দিয়ে smooth delivery; periodic probe দিয়ে model refresh। ফল: bufferbloat-prone path-এ Reno/CUBIC-র চেয়ে কম latency-তে একই throughput।

Bufferbloat — আধুনিক সংকট

Router-গুলোতে memory সস্তা হওয়ায় queue-গুলো বিশাল — ভরা পর্যন্ত drop হয় না। কিন্তু ভরা queue-এর প্রতিটা packet দাঁড়িয়ে অপেক্ষা করে: 100 Mbps × 200ms-deep buffer = লাখো byte লাইনে। ফল: throughput ঠিক, কিন্তু latency বিস্ফোরিত — video call জমে, web মরে, অথচ speed-test দেখায় “সব ঠিক!”

সমাধান: AQM (Active Queue Management) — queue ভরার আগেই drop/mark করা, আর FQ-CoDel — per-flow queueing + intelligent drop, bufferbloat ভাঙার আধুনিক মান। ECN (Explicit Congestion Notification) আরও ভদ্র: drop না করে IP header-এ bit mark করে “ভরছি” বলা, TCP তা ECN-Echo দিয়ে স্বীকার করে — loss-এর অন্ধকার signal-এর প্রথম আলো।

ভেতরে কী ঘটছে

Cycle-by-cycle: একটা numeric জীবন

MSS=1000B, IW=10, ssthresh=16, loss ঘটবে cwnd=24-এ:

phase        RTT-তে cwnd
slow start:  10, 20 → ssthresh 16 ছুঁলে avoidance-এ
avoidance:   16, 17, 18, ..., 24
loss (3dup): ssthresh ← 12; cwnd ← 12 (+3 credit = Reno-তে কার্যত 15 থেকে বাড়তে শুরু)
avoidance:   15, 16, 17, ...   ← নতুন sawtooth-এর দাঁত

Timeout-path-এ কিন্তু আলাদা শাস্তি: cwnd ← 1 MSS — পুরো slow start আবার, কারণ timeout মানে network সম্ভবত মারাত্মক অসুস্থ।

ss -ti — kernel-এর ভেতরটা পড়া

Transfer চলাকালীন ss -ti dport = :443 output-এর গুরুত্বপূর্ণ counters:

cwnd:25          ← বর্তমান congestion window (MSS unit)
ssthresh:16      ← slow-start threshold
bytes_acked:482000
segs_out:340 segs_in:310
retrans:2/12     ← total/fast-recovery retransmits
delivery_rate:94.1Mbps
rtt:12.4/0.9     ← SRTT/mdev (µs নয়, ms!)

এই এক লাইনই আজকের পুরো লেসনের live X-ray — experiment-এ এটাই পড়ব।

উদাহরণ

Sawtooth হাতে আঁকা — Reno বনাম CUBIC বনাম BBR

এক loss event (cycle 30), 60-cycle জীবন:

cwnd
 40│           Reno
 35│          ╱╲        ╱╲        ╱╲
 30│         ╱  ╲      ╱  ╲      ╱  ╲
 25│        ╱    ╲    ╱    ╲    ╱
 20│       ╱      ╲  ╱      ╲  ╱
 15│      ╱        ╲╱        ╲╱        ← প্রতি loss-এ অর্ধেক
 10│ ▁▂▄▂╱
   └──────────────────────────────────► RTT/cycle
 40│              CUBIC
 35│             ╱╲__       ╱╲__
 30│            ╱     ╲___ ╱     ╲__   ← cubic curve: দ্রুত-ধীর-দ্রুত
 25│           ╱          ╲╱
 20│          ╱
 15│      ___╱
 10│ ▁▄█▄▁╱
   └──────────────────────────────────►
 40│              BBR (pacing rate সমানুপাতিক)
 30│ ──────────────────────────────    ← loss-ও তাকে নাড়ায় না;
 20│                                    মডেল মনে করে loss wireless-এর
 10│ ─probe↗                            ← মাঝে মাঝে bandwidth probe
   └──────────────────────────────────►
তিন প্রজন্মের window-জীবন এক loss-এর আগে-পরে: Reno-র ধারালো দাঁত, CUBIC-র বাঁকা regrowth, BBR-এর অবিচল রেখা।

সংখ্যা দিয়ে এক cycle-set (Reno): cwnd 20-তে ৩-dup-ACK loss →

ssthresh ← 20/2 = 10
cwnd     ← 10 + 3 = 13   (fast recovery credit)
পরের ACK-গুলোতে: 14, 15, 16 ... (avoidance resume)

ছবিতে

Sender-এর সিদ্ধান্ত-স্তর — কোন window আসলে শাসন করে
  1. Applicationwrite() করলেই পাঠানো হয় না — নিচের দুই gate-এর অপেক্ষায়
  2. min(rwnd, cwnd)effective window — receiver ও network-এর দুই ভোট
  3. rwndreceiver-এর buffer advertisement — flow control
  4. cwndsender-এর congestion estimate — slow start/AIMD/CUBIC/BBR
  5. ACK-clockপ্রতিটা advance-এর সুইচ — reliability-র engine

আর state machine-টা সংক্ষেপে:

                cwnd < ssthresh
        ┌──────── slow start ────────┐
        │   প্রতি RTT-তে ×2            │
        ▼                             │ cwnd ≥ ssthresh
   [IW=10]                     congestion avoidance
        │                        প্রতি RTT-তে +1
        │                             │
        │      ৩-dup ACK              │ timeout!
        │   ┌── fast retransmit ──┐   │
        │   │ ssthresh←cwnd/2     │   ▼
        └──►│ cwnd←ssthresh(+3)   │  cwnd ← 1 MSS
            │ (fast recovery)      │  (পুরো slow start আবার)
            └──────┬───────────────┘
                   ▼
          ফিরে যাও avoidance-এ
Slow start ↔ avoidance-র দুই-রাস্তা মোড় — loss-এর ধরন ঠিক করে দেয় কোন শাস্তি হবে।

নিজে চালিয়ে দেখুন

EXPERIMENT

kernel-এর cwnd চোখের সামনে বাড়তে দেখা — আর (LAN হলে) fairness মাপা

Linux (ss দরকার); macOS-এ netstat fallback· ২০ মিনিট

ধাপ ১ — বড় transfer + live watch:

# এক terminal-এ বড় download শুরু করুন (যেকোনো বড় file URL):
curl -o /dev/null http://speedtest.tele2.net/100MB.zip
# আরেক terminal-এ ২ সেকেন্ড পরপর cwnd ছাপুন:
while :; do ss -ti state established '( dport = :80 or dport = :443 )' \
  | grep -E '^      cwnd' ; sleep 2; done

(macOS-এ ss নেই — netstat -p tcp -v দিয়ে approx; বা Docker container-এ Linux চালান।)

দেখার বিষয়: প্রথম কয়েক পড়ায় cwnd দ্রুত লাফায় (slow start), তারপর ssthresh-এর কাছাকাছি ছোট ধাপে (avoidance); retrans counter বাড়লে cwnd হঠাৎ কমে যাওয়াও দেখতে পারেন — সরাসরি sawtooth!

ধাপ ২ (ঐচ্ছিক, LAN) — iperf3 দিয়ে fairness:

# এক machine-এ (LAN peer):  iperf3 -s
# এই machine-এ — একসাথে ৪ parallel flow:
iperf3 -c <peer> -P 4 -t 30
# প্রতিটা flow-র শেষ summary তুলনা করুন — চারটার SUM প্রায় সমান ভাগ
# হয়েছে কি না দেখুন; AIMD-র convergence বাস্তব সংখ্যায়।

LAN না থাকলে বিকল্প: নিচের build-এর simulator-ই deterministic fairness/sawtooth worksheet — cycle-by-cycle CSV-তে একই প্রমাণ।

ধাপ ৩ — আপনার system-এর কোন CC চলছে?

sysctl net.ipv4.tcp_congestion_control   # Linux: প্রায়ই 'cubic'
sysctl net.ipv4.tcp_available_congestion_control   # bbr আছে কি না দেখুন
এটা কী প্রমাণ করে

ss -ti-র cwnd counter আসলেই slow start-এ দ্রুত বেড়ে ssthresh ছুঁয়ে avoidance-এ ধীর হয় — textbook-এর phase-দুটো kernel-এর private variable-এ হাতে-কলমে।

নিজে বানান

BUILD IT

Sawtooth data-generator — Reno vs CUBIC vs BBR, ASCII-তে

Python · ●●●○○
  1. Cycle-loop: প্রতি iteration = এক RTT
  2. Reno: slow start → avoidance (+1) → loss-এ halve (fast recovery)
  3. CUBIC: W(t) = 0.4·(t−K)³ + W_max, loss-এ W_max update
  4. BBR-style: constant pacing rate + periodic probe, loss উপেক্ষা
  5. CSV emit + sparkline render — তিন দর্শন পাশাপাশি দেখুন
BLOCKS = "▁▂▃▄▅▆▇█"

def spark(vals, lo=None, hi=None):
    """সংখ্যা-তালিকা → এক-লাইনের ASCII sparkline"""
    lo = min(vals) if lo is None else lo
    hi = max(vals) if hi is None else hi
    span = max(hi - lo, 1e-9)
    return "".join(BLOCKS[min(int((v - lo) / span * 7.999), 7)]
                   for v in vals)

def reno(cycles, loss_at, iw=10, init_ssthresh=16):
    cwnd, ssthresh = iw, init_ssthresh
    out = []
    for c in range(cycles):
        out.append(cwnd)
        if c == loss_at:                      # ৩-dup loss
            ssthresh = max(cwnd // 2, 2)
            cwnd = ssthresh + 3               # fast recovery credit
        elif cwnd >= ssthresh:
            cwnd += 1                         # additive increase
        elif cwnd < ssthresh:
            cwnd = min(cwnd * 2, ssthresh)    # slow start (per-RTT সরলীকরণ)
    return out

def cubic(cycles, loss_at, C=0.4, beta=0.7):
    wmax, t_last_loss, out = None, 0, []
    cwnd = 10
    for c in range(cycles):
        out.append(round(cwnd))
        if c == loss_at:
            wmax, t_last_loss, k = cwnd, c, ((cwnd * (1 - beta)) / C) ** (1 / 3)
            cwnd = cwnd * beta                # multiplicative decrease
        elif wmax is not None:
            t = c - t_last_loss
            cwnd = C * (t - k) ** 3 + wmax    # cubic window function
        else:
            cwnd *= 2                          # pre-first-loss: slow start-ish
    return out

def bbr(cycles, loss_at, bw=28, probe_every=8):
    out, base_rate = [], float(bw)
    for c in range(cycles):
        bump = 1.25 if 0 <= (c % probe_every) < 1.5 else 1.0   # probe cycle
        out.append(round(base_rate * bump))
        if c % (probe_every * 4) == 0:                          # RTprop window
            base_rate = bw * (0.97 if c > loss_at else 1.0)     # model refresh
    return out

CYCLES, LOSS_AT = 60, 30
r, cu, bb = reno(CYCLES, LOSS_AT), cubic(CYCLES, LOSS_AT), bbr(CYCLES, LOSS_AT)

print("cycle,reno,cubic,bbr")
for c in range(CYCLES):
    print(f"{c},{r[c]},{cu[c]},{bb[c]}")

print("\n— sparkline (cycle 0→59, এক █ = বেশি cwnd) —")
top = max(max(r), max(cu), max(bb))
print("Reno :", spark(r, hi=top))
print("CUBIC:", spark(cu, hi=top))
print("BBR  :", spark(bb, hi=top))
print(f"\nloss @cycle {LOSS_AT}: Reno/CUBIC halve, BBR নিরবিচ্ছিন্ন")

প্রত্যাশিত sparkline-আকৃতি:

Reno : ████████▇▆▅▄▃▂▁▁▂▃▄▅▆▇████▇▆...
CUBIC: ████████▆▃▁▂▄▆██▇▅▃▂▃▅▇█████...
BBR  : ███████████████▊█████████████...   ← loss-এ কোনো dent-ই নেই

CSV-টা spreadsheet/graph tool-এ নিয়েও আঁকতে পারবেন — তিনটা curve-এর দর্শনগত পার্থক্য তখন পুরোদেহি ফুটবে।

নিজে বাড়ান:

  1. LOSS_AT পরিবর্তন করে দেখুন — ঘন loss হলে BBR-এর advantage কোথায় বাড়ে
  2. দুইটা Reno instance একসাথে চালিয়ে (shared bottleneck model: দুজনের যোগফল এক capacity, excess-এ proportional drop) — convergence সংখ্যায় প্রমাণ করুন
  3. Timeout-event যোগ করুন — cwnd←1 আর slow-start restart-এর গভীর খাঁজ capture করুন

বাস্তব সিস্টেমে

বাস্তব জগতে congestion control

Home router-এর bufferbloat। ISP-দের দেওয়া cheap router-এর বিশাল dumb queue-ই আপনার Zoom-call freeze-এর প্রধান suspect। OpenWrt-তে FQ-CoDel/sqm enable করলে latency 300ms→10ms নামার measurement সাধারণ — AQM আর theory-র সরাসরি জেতা।

Google আর Netflix-এর BBR। Google-এর internal + YouTube traffic, Netflix-এর Open Connect appliances — BBR production-scale প্রমাণ যে model-based control কাজ করে। Linux-এ sysctl দিয়ে চালু করা যায়, তবে fairness-critic (Reno-flow-র সাথে অসম share) তর্কও আছে।

Starlink আর cellular। High-varying RTT, random wireless loss — loss-based CC-র দুই নরক। BBRv2/v3 আর modern AQM ঠিক এই environment- এর জন্যই বেড়ে ওঠা research frontier।

The 1986 lesson, আজও। প্রতিটা নতুন transport (QUIC সহ) নিজের congestion control বহন করে — RFC 8085 UDP-app-দেরও একই দাবি করে। Internet-এর stability কোনো regulator-এর না — প্রতিটা sender-এর voluntary discipline-এর।

যে ভুলগুলো সবাই করে

“Slow start ধীর — তাই connection শুরুতে অল্প data যায় অনেকক্ষণ।”

Exponential বৃদ্ধি মানে 10 RTT-তেই IW=10 → 10,000+ MSS — বেশিরভাগ web response slow start-এর ভেতরেই শেষ হয়ে যায়। নামটা ১৯৮৮-এর তুলনায় historical (“open loop-এ সব window ঢালা”-র চেয়ে slow)। বাস্তব সমস্যা উল্টো: initial window ছোট বলে ছোট transfer-ই RTT-র grip-এ থাকে — এজন্যই IW 1→10 করা হয়েছে (RFC 6928)।

“Packet loss মানেই অবশ্যই congestion — তাই কমালেই network সুস্থ।”

Wireless/Wi-Fi-এ bit-error, handover, interference — loss-এর অনেক non-congestive উৎস। Loss-based CC সেখানে ভুলভাবে window কেটে নিজের throughput দান করে। BBR-এর মূল প্রেরণাই এটা — signal (loss) নয়, measurement (bandwidth+RTprop)। তবে সাবধান: এর উল্টো extreme-ও ভুল — সব loss উপেক্ষা করলে wired bottleneck-এ collapse ফিরে আসে।

“Bufferbloat মানে buffer বড় রাখা ভালো — drops কম, throughput বেশি।”

Bigger buffer = বেশি জায়গা, কিন্তু queue-তে দাঁড়ানো প্রতিটা byte latency। ভরা buffer-এ packet ফেলা হয় না বলে sender-ও বুঝে না overload হয়েছে — cwnd বাড়তেই থাকে, queue আরও লম্বা — “buffer full হলে তবেই signal” policy-ই bloat তৈরি করে। সমাধান ছোট-smart queue (CoDel): standing time মাপে, না কি size।

“CUBIC/BBR-ই best — সব OS/app-এ ওটাই set করা উচিত।”

Environment-dependent: long fat pipe-এ CUBIC দুর্দান্ত, shallow- buffer datacenter-এ DCTCP/BBR ভালো, mixed Reno-world-এ aggressive algorithm fairness নষ্ট করে (BBRv1-এর known criticism)। Kernel default (CUBIC) সাধারণ ভালো মধ্যম; algorithm-switch করার আগে path-characteristics মাপুন — cargo-cult tuning নয়।

বুঝেছেন কি না দেখুন

1

rwnd, cwnd, ssthresh — তিনটার মালিক কে, আর effective sending limit কীভাবে বের হয়?

স্মরণ

rwnd — receiver advertise করে (wire-এ যায়, header-এর window field); cwnd — sender-এর kernel-private congestion estimate; ssthresh — sender-এর slow-start↔avoidance মোড়ের সীমা। Effective limit = min(rwnd, cwnd) — receiver-এর ভোট আর network-এর ভোটের সবচেয়ে সংকোচকটা জেতে।

2

cwnd = 32, ssthresh = 16। এখন slow start না avoidance? ৩ RTT loss-free-তে cwnd কত হবে? তারপর ৩-dup loss হলে?

প্রয়োগ

32 > 16 তাই avoidance। ৩ RTT-তে +3: cwnd = 35। Loss-এ: ssthresh ← 35/2 = 17 (integer), cwnd ← 17+3 = 20 (Reno fast recovery credit) — পরের RTT থেকে avoidance-এ 21, 22…

3

AIMD কেন ন্যায্য, যুক্তি দিয়ে ব্যাখ্যা করুন — আর pure additive- increase/additive-decrease (AIAD) কেন না?

যুক্তি

Multiplicative decrease দুজনের share-কে একই অনুপাতে কাটে — gap অনুপাতে অপরিবর্তিত; কিন্তু additive increase সমান absolute ধাপ — বেশি-নেওয়া flow-র relative লাভ প্রতি চক্রে খাটো হয়। সমান-gap-এর দিকে জ্যামিতিক convergence। AIAD-তে কম-নেওয়া flow-ও একই absolute হারে হারায় — প্রারম্ভিক অসমতা চিরস্থায়ী, converge করার force-ই নেই।

4

CUBIC long-distance transfer-এ Reno-কে কেন ছাড়িয়ে যায় — W_max plateau-র ভূমিকাসহ?

যুক্তি

Reno linear (+1/RTT) — 10Gbps×100ms path-এ অর্ধেক-পতন থেকে ফিরতে হাজার RTT। CUBIC time-ভিত্তিক cubic — RTT যা-ই হোক, নির্দিষ্ট সময়ে বেশি বাড়ে; তার উপর W_max-এর চারপাশে plateau — আগের ceiling- এর কাছে সতর্ক (নতুন capacity আছে কি না gently probe), নিচে দ্রুত recovery। Long-RTT-তে এই time-based nature-ই বিজয়।

5

Video call-এ upload speed-test দুর্দান্ত, তবু প্রতি কয়েক সেকেন্ডে frame freeze — latency spike প্যাটার্ন। সন্দেহ কী, এবং fix-এর দিক?

প্রয়োগ

Classic bufferbloat: upload stream (cloud backup ইত্যাদি) router-এর বিশাল queue ভরে ফেলেছে — throughput ঠিক, কিন্তু call-এর packet queue-র পেছনে দাঁড়িয়ে 200–500ms দেরিতে আসে। Fix-দিক: router-এ SQM/FQ-CoDel enable, queue ছোট-বুদ্ধিমান করা; app-side pacing/DSCP। Speed-test ধরে না — কারণ সেটা throughput মাপে, standing-queue latency না।

6

আপনি একটা bulk-backup app design করছেন যেটা user-এর interactive traffic-কে যতটা সম্ভব বিরক্ত না করবে। TCP-র congestion control কোন বৈশিষ্ট্যগুলো কাজে লাগাবেন (৩টা), আর কেন raw-UDP-র ঝুঁকি?

ডিজাইন

(১) TCP-র built-in AIMD/CUBIC — idle হলে capacity-র বাকি অংশ নেয়, interactive flow-এর চাপে multiplicative পতনে সঙ্গে সঙ্গে ছেড়ে দেয়; (২) pacing — burst না ঢেলে smooth, AQM-বান্ধব; (৩) small initial window + slow-start restart সম্মান — অলস connection-এর হঠাৎ বিস্ফোরণ এড়ানো। Raw-UDP ঝুঁকি: congestion control নিজে বানাতে হবে — না করলে একই link-এর TCP traffic চূর্ণ করবে (RFC 8085 violation), আর middlebox-গুলো unresponsive-UDP rate-limit করে drop করতেই পারে। QUIC-র পথ ছাড়া শর্টকাট নেই।

এরপর কী

এরপর কী

Theory-টা এখন মজবুত: window, clock, sawtooth। কিন্তু এসব কোথায় লেখা আছে? Application programmer ঠিক কোন API ডাকে, kernel ভেতরে কোন পথে data নেয়?

পরের লেসন — Sockets API ও kernel stack:

  • socket/bind/listen/accept/connect — প্রতিটা call কোন TCP state touch করে
  • sockaddr, htons, endianness — struct-এর ভেতরের সংখ্যা-রাজনীতি
  • write() থেকে NIC DMA পর্যন্ত send-এর পূর্ণ kernel-journey
  • SO_REUSEADDR-এর রহস্যভেদ, আর strace দিয়ে syscall গণনা

আজ আপনি ss -ti-তে cwnd পড়েছেন — পরের লেসনে সেই socket-টাই নিজের হাতে open করবেন।

আরও পড়ুন

  • RFC 5681 — TCP Congestion Control — M. Allman, V. Paxson, E. Blanton · Slow start, congestion avoidance, fast recovery-র canonical specification
  • RFC 8312 — CUBIC for Fast and Long-Distance Networks — S. Ha, I. Rhee, L. Xu · Linux-এর default congestion control-এর specification
  • "Congestion Avoidance and Control" (1988) — Van Jacobson · সেই ঐতিহাসিক paper — slow start ও AIMD-র জন্ম, internet-কে বাঁচানোর গল্প