Congestion Control — slow start, Reno, CUBIC, BBR
Flow and Congestion Control
১৯৮৬-এ internet গলাটা চেপে মরছিল — TCP-র congestion control-ই তা থেকে ফিরিয়ে এনেছে; slow start থেকে BBR পর্যন্ত তিন প্রজন্মের অ্যালগরিদম, sawtooth-এর ন্যায়বিচার, আর bufferbloat-এর আধুনিক সংকট।
আগে এটা বুঝি
অক্টোবর ১৯৮৬। 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-কে সহ্যযোগ্য রাখে।
আজকের প্রশ্ন তিনটা:
- Receiver-এর flow control আর network-এর congestion control — দুইটা কেন আলাদা সমস্যা?
- Slow start, AIMD, fast recovery — যন্ত্রগুলো ঠিক কীভাবে ঘোরে?
- Reno → CUBIC → BBR: তিন প্রজন্ম কেন জন্মাল, দর্শনে কোথায় আলাদা?
মূল ধারণা
দুই রকম থ্রটল, দুই রকম উদ্দেশ্য
tcp-reliability লেসনের window মনে আছে? Sender-এর আসল limit তিনটা constraint-এর সবচেয়ে কড়াটা:
| Flow control | Congestion control | |
|---|---|---|
| কাকে রক্ষা করে | Receiver-এর buffer | Network (router queue) |
| Signal source | Receiver-এর advertisement | Packet loss / delay (implicit!) |
| Variable | rwnd — wire-এ যায় | cwnd — kernel-এর private |
| কে ঠিক করে | Receiver application | Sender নিজে |
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 অর্ধেক:
এই 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 shareAdditive ধাপে gap কমে না, multiplicative পতনে gap অনুপাতে কমে — কিন্তু additive ধাপটা সবসময় সমান, তাই বেশি-নেওয়া flow-র share তুলনামূলক কমে আসে। কোনো server, কোনো coordinator নেই — ন্যায়বিচারটা অ্যালগরিদমের গণিত থেকেই আসে।
Fast recovery — Reno/NewReno-র মসৃণ পতন
৩-dup-ACK-তে (fast retransmit) পুরো slow start-এ ফেরা wasteful — packet তো এখনো চলছে! Reno-র পথ:
- ssthresh ← cwnd/2
- cwnd ← ssthresh + 3 (in-flight dup-ACK-দের জন্য credit)
- 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 বানানো —
(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
└──────────────────────────────────►সংখ্যা দিয়ে এক 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)ছবিতে
- Applicationwrite() করলেই পাঠানো হয় না — নিচের দুই gate-এর অপেক্ষায়
- min(rwnd, cwnd)effective window — receiver ও network-এর দুই ভোট
- rwndreceiver-এর buffer advertisement — flow control
- cwndsender-এর congestion estimate — slow start/AIMD/CUBIC/BBR
- 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-এনিজে চালিয়ে দেখুন
kernel-এর cwnd চোখের সামনে বাড়তে দেখা — আর (LAN হলে) fairness মাপা
ধাপ ১ — বড় 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-এ হাতে-কলমে।
নিজে বানান
Sawtooth data-generator — Reno vs CUBIC vs BBR, ASCII-তে
- Cycle-loop: প্রতি iteration = এক RTT
- Reno: slow start → avoidance (+1) → loss-এ halve (fast recovery)
- CUBIC: W(t) = 0.4·(t−K)³ + W_max, loss-এ W_max update
- BBR-style: constant pacing rate + periodic probe, loss উপেক্ষা
- 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-এর দর্শনগত পার্থক্য তখন পুরোদেহি ফুটবে।
নিজে বাড়ান:
LOSS_ATপরিবর্তন করে দেখুন — ঘন loss হলে BBR-এর advantage কোথায় বাড়ে- দুইটা Reno instance একসাথে চালিয়ে (shared bottleneck model: দুজনের যোগফল এক capacity, excess-এ proportional drop) — convergence সংখ্যায় প্রমাণ করুন
- 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 নয়।
বুঝেছেন কি না দেখুন
1rwnd, 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-এর ভোটের সবচেয়ে সংকোচকটা জেতে।
2cwnd = 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…
3AIMD কেন ন্যায্য, যুক্তি দিয়ে ব্যাখ্যা করুন — আর pure additive-
increase/additive-decrease (AIAD) কেন না?
যুক্তি
Multiplicative decrease দুজনের share-কে একই অনুপাতে কাটে — gap অনুপাতে অপরিবর্তিত; কিন্তু additive increase সমান absolute ধাপ — বেশি-নেওয়া flow-র relative লাভ প্রতি চক্রে খাটো হয়। সমান-gap-এর দিকে জ্যামিতিক convergence। AIAD-তে কম-নেওয়া flow-ও একই absolute হারে হারায় — প্রারম্ভিক অসমতা চিরস্থায়ী, converge করার force-ই নেই।
4CUBIC 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-ই বিজয়।
5Video 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-কে বাঁচানোর গল্প