এই পর্বে যা শিখবেন
- কখন CDN লাগে, আর miss থেকে hit পর্যন্ত একটি অনুরোধের পুরো পথ
- কোন content CDN-এ যাবে, কোনটি নয় — আর তার header, cache key আর TTL
- Hit ratio থেকে origin-এর ভার আর egress হিসাব, Zipf জনপ্রিয়তায় cache-এর আকারের প্রভাব, আর origin shield-এর অঙ্ক
- TTL, staleness আর origin-এর ভারের দর-কষাকষি;
stale-while-revalidate,stale-if-errorআর tag ধরে purge - Dynamic acceleration, edge computing-এর সীমা, edge-এ নিরাপত্তা, multi-CDN আর খরচ — আর AI-এর CDN-পরামর্শ যাচাইয়ের উপায়
১ · CDN কেন — দূরত্ব, origin-এর ভার, bandwidth-এর খরচ আর হঠাৎ ভিড়
একটি কাল্পনিক অনলাইন সংবাদ-সাইট: পাঠকের বেশিরভাগ বাংলাদেশে, সার্ভার ইউরোপের একটি cloud region-এ। প্রতিটি পাতায় কয়েক ডজন ছবি, CSS আর JavaScript ফাইল, আর প্রতিটি অনুরোধ সমুদ্রের নিচের cable ধরে হাজার হাজার কিলোমিটার যায়-আসে। এমন দূরত্বে RTT আলোর গতির কারণেই কয়েকশো মিলিসেকেন্ডের কাছাকাছি — হিসাবটি পর্ব ৭-এর অংশ ৮-এ।
সমাধান: জনপ্রিয় জিনিসের কপি ব্যবহারকারীর কাছের শহরে। এমন সার্ভারের জাল CDN; প্রতিটি শহরের সার্ভার-গুচ্ছ একটি edge বা PoP (point of presence). Caching header আর CDN-এর ভেতরের কৌশল protocol ধরে শেখানো হয়েছে Computer Networks-এর Web cache, proxy আর CDN পর্বে। এখানে প্রশ্ন নকশাকারের: কী রাখব, কতক্ষণ, কোন নামে — আর তাতে origin, খরচ আর ব্যবহারকারীর কী লাভ বা ঝুঁকি।
খাতুনগঞ্জের পাইকারি আড়ত হলো origin, পাড়ার খুচরা দোকান edge — জনপ্রিয় জিনিস তাকে থাকে (hit), না থাকলে আনিয়ে দেয় (miss). দেশের ৫০০ দোকান আলাদা করে আড়তে ফোন করলে আড়তের ফোন বন্ধ হবে; তাই মাঝে জেলা শহরের ডিস্ট্রিবিউটর, যিনি এক ট্রাকে সবার মাল আনেন — এটিই origin shield. আর নাম লেখা কেকের মতো একজনের জন্য বানানো জিনিস সাধারণ তাকে রাখা চলে না।
চারটি লাভ — নকশার ভাষায়
Latency
Hit-এ উত্তর কাছের শহর থেকে, RTT হয়তো ১০ ms; handshake-ও সেখানে শেষ। Miss-এ কাজে লাগে origin-এর সাথে আগে খোলা connection (অংশ ৮)।
দূরত্ব কমানোOrigin offload
Hit ratio ৯৫% হলে প্রতি ১০০টি অনুরোধের মাত্র ৫টি origin-এ যায়। Origin ছোট রাখা যায়, আর তার latency-ও স্থির থাকে।
কম সার্ভারBandwidth-এর খরচ
Cloud থেকে বেরোনো ডেটায় — egress — অনেক প্রদানকারী দাম নেয়; hit-এর byte origin থেকে বেরোয় না, যদিও CDN-এর নিজের দাম আছে (অংশ ৮)।
খরচ সরে যায়ভিড় আর DDoS সামলানো
ঈদের আগে বাসের টিকিটের ভিড়ের মতো flash crowd বা বন্যা-traffic বহু edge-এ ভাগ হয়; origin দেখে সামান্য অংশ।
ধাক্কা শোষণপুরো কাঠামো নিচের ছবিতে: কাছের edge, না পেলে আঞ্চলিক shield, সেখানেও না পেলে তবেই origin.
ব্যবহারকারী → edge (anycast) → origin shield → origin
Users reach the nearest edge; edge misses go to a regional shield; only shield misses reach the origin
ব্যবহারকারী দূরে, একই জিনিস বহু মানুষ চান, বড় ফাইল, বা হঠাৎ ভিড়ের সম্ভাবনা — যেকোনো একটি থাকলেই CDN প্রায় সবসময় লাভজনক। অফিসের ভেতরের ছোট অ্যাপ, বা প্রতিটি উত্তর একেকজনের জন্য আলাদা আর origin পাশের শহরেই — সেখানে cache-এর লাভ সামান্য, যদিও TLS আর DDoS সুরক্ষার জন্য অনেকে তবু CDN রাখেন।
২ · একটি অনুরোধের পথ — কাছের edge খোঁজা, miss, shield, origin, তারপর hit
কাছের edge-এ পৌঁছানোর দুই পথ, দুটোই পর্ব ৭-এ দেখেছেন। DNS দিয়ে: সাইটের নাম CNAME দিয়ে CDN-এর নামে যায়, আর CDN-এর DNS প্রশ্নকর্তার এলাকা দেখে কাছের edge-এর IP দেয় — GeoDNS-এর মতো। Anycast দিয়ে: সব edge একই IP ঘোষণা করে, routing নিজেই কাছেরটিতে পাঠায়। দুর্বলতা আর TTL-এর প্রভাব পর্ব ৭-এর অংশ ৩-এ, পুরো DNS-যাত্রা CN-এর CDN অংশে।
Edge-এ পৌঁছানোর পর কী হয়? ঢাকার একজন পাঠক চাইছেন /img/banner-42.webp; origin বলে Cache-Control: public, max-age=86400 — এক দিন। RTT ধরে নেওয়া, গোল করা; tier-গুলোর connection আগে থেকে খোলা।
| সময় | |
| পথের খরচ | |
| Origin-এ মোট অনুরোধ | |
| Cache-এর খবর (header, সরল করে) |
- অনুরোধ পৌঁছাল ঢাকার edge-এ। Anycast বা DNS পাঠক ক-কে কাছের edge-এ এনেছে, RTT প্রায় ১০ ms. Edge তার cache key (এখানে host আর path) দিয়ে খোঁজে — কপি নেই, miss.
- Edge জিজ্ঞেস করল shield-কে। RTT প্রায় ৪০ ms. সেখানেও কপি নেই — ছবিটি এইমাত্র প্রকাশিত।
- Shield গেল origin-এ — পুরো যাত্রায় একবারই। RTT প্রায় ১৬০ ms, কাজ ২০ ms. উত্তর
200 OK, সাথেCache-Control: public, max-age=86400আর একটি ETag. - দুই স্তরেই কপি, তারপর উত্তর। Shield আর edge কপি রাখল এক দিনের জন্য। পাঠক ক পেলেন ১০ + ৪০ + ১৬০ + ২০ = ২৩০ ms-এ — CDN ছাড়ার চেয়ে দ্রুত নয়, বরং দুটি বাড়তি hop. লাভ পরের অনুরোধে।
- তিন মিনিট পরে — hit. ঢাকার পাঠক খ একই ছবি চাইলেন; কপির বয়স ১৮০, মেয়াদ ৮৬,৪০০ — fresh. উত্তর প্রায় ১১ ms-এ, origin জানলই না।
Ageবলে কপি কত সেকেন্ড পুরোনো; hit-miss জানাতে মানসম্মতCache-Statusheader (RFC 9211; তালিকায় origin-এর কাছের cache আগে), আর অনেক CDN নিজের নামের header-ও দেয়। - চট্টগ্রাম থেকে — shield-এর লাভ। পাঠক গ-এর edge-এ কপি নেই, কিন্তু shield-এ আছে: উত্তর ১০ + ৪০ + ১ = প্রায় ৫১ ms-এ, origin-এ এখনো মোট ১টি অনুরোধ। Shield না থাকলে প্রতিটি নতুন শহরের প্রথম miss origin পর্যন্ত যেত।
শিক্ষা: CDN গড় দেরি কমায় hit দিয়ে; প্রথম বা কদাচিৎ চাওয়া অনুরোধ বরং সামান্য ধীর। গড় latency ≈ h × thit + (১ − h) × tmiss; h = ০.৯৫-এ ০.৯৫ × ১১ + ০.০৫ × ২৩০ ≈ ২২ ms. Origin-এর চাপের অঙ্ক অংশ ৫-এ।
Origin কোথায়? বড় ফাইলের origin প্রায়ই সরাসরি object storage (পর্ব ২৬), HTML আর API-র origin reverse proxy বা API gateway (পর্ব ১১); CDN path ধরে ভাগ করে: /img/* storage-এ, /api/* gateway-তে।
৩ · CDN-এ কী রাখবেন — static asset, ছবি-ভিডিও, API-র উত্তর আর ব্যক্তিগত পাতা
প্রতিটি content-এর জন্য তিন প্রশ্ন: উত্তর কি সবার জন্য এক? কত দ্রুত বদলায়, পুরোনো দেখালে ক্ষতি কত? বদলালে পুরোনো কপি কীভাবে সরবে? উত্তর লেখা হয় Cache-Control header-এ; নির্দেশগুলোর অর্থ CN-এর freshness অংশে।
| Content | সবার জন্য এক? | প্রচলিত নীতি | বদলালে |
|---|---|---|---|
| নামে hash-ওয়ালা JS, CSS, font | হ্যাঁ | public, max-age=31536000, immutable | নতুন নাম — purge লাগে না |
| HTML পাতার কাঠামো | প্রায়ই হ্যাঁ | ব্রাউজারে প্রতিবার যাচাই (max-age=0); CDN-এ সেকেন্ড থেকে মিনিট (s-maxage) | ছোট TTL বা purge |
| ছবি | হ্যাঁ, কয়েকটি রূপে | দীর্ঘ TTL; রূপ নিয়ন্ত্রিত | নতুন URL বা tag-purge |
| ভিডিওর segment | হ্যাঁ | segment দীর্ঘ TTL; playlist ছোট | segment বদলায় না |
| সবার জন্য এক API-র উত্তর | হ্যাঁ | ছোট TTL + stale-while-revalidate + stale-if-error | TTL, বা ঘটনা ধরে purge |
| লগইন-করা ব্যবহারকারীর পাতা বা API | না | private বা no-store; CDN-এ cache নয় | প্রযোজ্য নয় |
Static asset — নামে hash, এক বছরের মেয়াদ
Build-এর সময় বিষয়বস্তুর hash নামে বসে — app.3f9a2c.js; একে বলে fingerprinting. একটি নামের বিষয়বস্তু কখনো বদলায় না, তাই এক বছর রাখা নিরাপদ, আর immutable (RFC 8246) reload-এও যাচাই এড়ায়। এই cache busting-এর খুঁটিনাটি CN পর্বের অংশ ৬-এ; এখানে দেখি এর আসল লাভ — নিরাপদ deploy.
| ধাপ | |
| ফল |
- সংস্করণ ১ চলছে।
index.html-এর মেয়াদ ছোট (CDN-এ ৬০ সেকেন্ড, ব্রাউজারে যাচাই), আর সেটি চায়app.3f9a2c.js, যা সবখানে এক বছরের জন্য রাখা। - Deploy-এর ধাপ ক: নতুন asset আগে। নতুন
app.b71e04.jsorigin বা storage-এ তোলা হলো, পুরোনোটি না মুছে। কোনো HTML এখনো এর নাম জানে না, তাই কিছু বদলায়নি। - Deploy-এর ধাপ খ: তারপর HTML.
index.htmlবদলাল; CDN-এ তার কপি purge হলো, বা ৬০ সেকেন্ডে ফুরাবে। ক্রমটিই মূল — উল্টো হলে নতুন HTML এমন ফাইল চাইবে যা তখনো নেই। - রুমার ট্যাব পুরোনো — তবু ঠিক। রুমার ব্রাউজারে দশ মিনিট আগের HTML, যা
app.3f9a2c.jsচায় — ফাইলটি এখনো আছে। পুরোনো HTML আর পুরোনো JS একটি মেলানো জোড়া। - সজীব নতুন — তিনিও ঠিক। সজীব নতুন HTML পেলেন, যা চায়
app.b71e04.js; প্রথমবার miss, তারপর সবার hit. নতুন HTML আর নতুন JS — আরেকটি মেলানো জোড়া। - তুলনা: যদি নাম না বদলাত। একই নাম
app.js-এ নতুন কোড overwrite হলে কোনো edge-এ পুরোনোapp.jsতখনো fresh — নতুন HTML পেল পুরোনো JS; আবার রুমার পুরোনো HTML পেল নতুন JS. সংস্করণ মিশে গেল, আর বাগটি দেখা দেয় শুধু কিছু শহরে, কিছুক্ষণ — খুঁজে পাওয়া কঠিন। - পরিষ্কার: পুরোনো asset পরে মোছা। পুরোনো ফাইল রাখুন অন্তত সবচেয়ে পুরোনো সম্ভাব্য HTML-এর আয়ু (খোলা ট্যাব, অ্যাপের ভেতরের পাতা) আর rollback-এর সময় পর্যন্ত; অনেক দল কয়েক সপ্তাহ রাখে। Rollback তখন শুধু পুরোনো HTML ফেরানো। Canary আর blue-green deploy পর্ব ৫৩-এ।
ছবি আর ভিডিও
ছবি সাধারণত সবচেয়ে বেশি byte খায়, তাই byte hit ratio-র বড় অংশ এখানে। প্রতিটি আকার (w=400, w=800) আর format (WebP, AVIF, JPEG) আলাদা কপি, তাই রূপ সীমিত রাখুন — যেকোনো width চাইতে দিলে key অসীম, hit ratio পড়ে, আর বাইরের কেউ origin-কে অগণিত resize করাতে পারে। ভিডিও আসে ছোট segment-এ; segment কখনো বদলায় না, বদলায় শুধু playlist. পুরো নকশা পর্ব ৫৯-এর case study-তে।
Cache করা যায় এমন API-র উত্তর
দামের তালিকা, স্কোর, পরীক্ষার ফল — সবার জন্য এক, কিন্তু বদলায়। নীতি: ছোট TTL, সাথে RFC 5861 (২০১০)-এর দুটি নির্দেশ:
- stale-while-revalidate=N: মেয়াদ ফুরানোর পরেও N সেকেন্ড পুরোনো কপি সাথে সাথে, আর পেছনে নতুন কপি আনা — কেউ origin-এর জন্য অপেক্ষা করেন না।
- stale-if-error=N: origin error দিলে বা অনুপলব্ধ হলে N সেকেন্ড পর্যন্ত পুরোনো কপিই — graceful degradation-এর সবচেয়ে সস্তা রূপ (পর্ব ১৮)।
s-maxage শুধু shared cache-এর মেয়াদ, তাই ব্রাউজারকে ছোট আর CDN-কে বড় মেয়াদ দেওয়া যায়। কোন CDN কোন নির্দেশ মানে, বা RFC 9213-এর CDN-Cache-Control-এর মতো আলাদা header সমর্থন করে কি না — সেবাভেদে আলাদা; documentation দেখে আর মেপে নিশ্চিত হোন। চার ধরনের উত্তরের header:
# ১. নামে hash-ওয়ালা JavaScript
GET /assets/app.3f9a2c.js
Cache-Control: public, max-age=31536000, immutable
# ২. HTML: ব্রাউজার প্রতিবার যাচাই করে (304 সস্তা), CDN-এ ৬০ সেকেন্ড
# (no-cache দিলে মান অনুযায়ী CDN-ও প্রতিবার যাচাই করত)
GET /news/4821
Cache-Control: max-age=0, s-maxage=60
ETag: "a91f"
# ৩. সবার জন্য এক API
GET /api/v1/prices?category=rice
Cache-Control: public, max-age=10, s-maxage=30, stale-while-revalidate=30, stale-if-error=600
# ৪. ব্যক্তিগত তথ্য — কোনো shared cache-এ নয়
GET /api/v1/me/orders
Cache-Control: private, no-storeব্যক্তিগত content — কেন সাধারণত cache হয় না
যে উত্তরে নাম, ব্যালান্স বা token আছে, তা shared cache-এ জমলে পরের জন অন্যের তথ্য দেখবেন (ভাবনার প্রশ্ন ২)। নিরাপদে রাখলেও লাভ প্রায় নেই: উত্তর cookie বা ব্যবহারকারীর ওপর নির্ভর করলে key-তে ব্যবহারকারী লাগে, আর এক লাখ ব্যবহারকারী মানে এক লাখ কপি, প্রতিটি হয়তো একবার চাওয়া। Vary: Cookie ঠিক এটাই করে — hit ratio প্রায় শূন্য।
সমাধান পাতা ভাগ করা: সবার জন্য এক কাঠামো CDN থেকে, আর ব্যক্তিগত অংশ — “স্বাগতম, রুমা”, cart-এর সংখ্যা — ছোট একটি private API call-এ, যা TLS আর WAF-এর জন্য CDN-এর ভেতর দিয়ে গেলেও cache হয় না। মাঝামাঝি পথ: ব্যক্তির বদলে দল ধরে রূপ — দেশ, ভাষা, mobile-desktop. Session আর token-এর নকশা পর্ব ১৫-এ।
Authorization আর public. HTTP caching-এর মান (RFC 9111) অনুযায়ী Authorization-যুক্ত অনুরোধের উত্তর shared cache সাধারণত রাখে না — কিন্তু উত্তরে public, s-maxage বা must-revalidate থাকলে রাখতে পারে। তাই “সব API-তে public, s-maxage=60” পরামর্শ ব্যক্তিগত API-কে shared cache-এ ঠেলে দিতে পারে। ব্যক্তিগত endpoint-এ স্পষ্ট private বা no-store, আর CDN-এর নিয়মেও এসব path cache-বহির্ভূত।
৪ · Cache key-এর নকশা — query string, Vary, cookie আর tracking param-এর ফাঁদ
Cache কপি খোঁজে একটি key দিয়ে; ডিফল্টে সাধারণত scheme, host, path আর পুরো query string (CDN-ভেদে আলাদা)। নকশার নিয়ম এক বাক্যে:
দুই দিকেই ভুল হয়। বেশি রাখলে — যে অংশ উত্তর বদলায় না, তা-ও key-তে — একই উত্তরের অসংখ্য কপি, hit ratio পড়ে যায়, origin-এ চাপ বাড়ে। কম রাখলে — যে অংশ উত্তর বদলায় তা বাদ — একজনের জন্য বানানো উত্তর আরেকজন পান: ভুল ভাষা, ভুল দাম, কখনো অন্যের তথ্য। দ্বিতীয় ভুলের ইচ্ছাকৃত অপব্যবহারের নাম web cache poisoning; প্রতিরক্ষা CN পর্বের অংশ ৬-এ।
ডাকঘরে চিঠি বাছাই ঠিকানা ধরে। ঠিকানার পাশে “ফেসবুকের বিজ্ঞাপন দেখে পাঠালাম” লেখাটিকেও ঠিকানা ধরলে একই বাড়ির চিঠি দশ থলিতে যাবে; আবার ফ্ল্যাট নম্বর বাদ দিলে এক ফ্ল্যাটের চিঠি অন্যের হাতে। Key normalization মানে ঠিকানা মানসম্মত করা — দরকারি অংশ ঠিক ক্রমে, বাকিটা বাদ।
Query string normalization
একই খবরের লিংক আসে ?utm_source=fb, ?fbclid=… বা ?gclid=… নিয়ে। এগুলো marketing-এর হিসাব, উত্তর বদলায় না, অথচ key-তে থাকলে প্রতিটি লিংক আলাদা কপি — ভাইরাল খবরের দিনেই origin-এ সবচেয়ে বেশি চাপ। ক্রমও বিষয়: ?a=1&b=2 আর ?b=2&a=1 একই উত্তর, ভিন্ন key. প্রচলিত সমাধান, CDN-এর সেটিং বা edge function দিয়ে:
ALLOWED = { "/img/": ["w", "fmt"], # শুধু এই param উত্তর বদলায়
"/api/v1/prices": ["category"],
"/news/": [] } # খবরে কোনো param নয়
function cacheKey(req):
host = lowercase(req.host)
path = req.path # path বদলাবেন না, শুধু মানসম্মত
keep = ALLOWED[prefix(path)] # allow-list: জানা param-ই রাখা
q = [(k, v) for (k, v) in req.query if k in keep]
q = sort(q by k) # ক্রম মানসম্মত
if "w" in q: q["w"] = nearest(q["w"], [200, 400, 800, 1600]) # রূপ সীমিত
enc = "br" if "br" in req.acceptEncoding else ("gzip" if "gzip" in req.acceptEncoding else "id")
return host + path + "?" + join(q) + "|" + enc # cookie, User-Agent key-তে নেইনিয়মটি allow-list: জানা param রাখা, অচেনা সব বাদ; উল্টো পথ (জানা tracking param বাদ) ভঙ্গুর, কারণ নতুন param আসতেই থাকে। ঝুঁকি: অ্যাপে উত্তর-বদলানো নতুন param (যেমন page=2) এল, কিন্তু তালিকায় উঠল না — সবাই প্রথম পাতাই পাবেন। তাই key-র নিয়ম কোডের মতো version, review আর test করুন।
Vary, cookie আর header
Accept-Encoding: উত্তর gzip বা Brotli-তে চাপা, তাই key-তে লাগে — তবে কাঁচা মান হাজার রকম, কয়েকটিতে মানসম্মত করুন (উপরেরenc).Vary: User-Agentএড়ান: প্রায় প্রতিটি অনুরোধ আলাদা key. দরকারে edge-এ ছোট শ্রেণি (mobile, desktop) বানিয়ে শুধু সেটি key-তে।- Cookie key-তে নয়: static path-এ cookie উপেক্ষা আর
Set-Cookieসরান; অনেক CDNSet-Cookie-যুক্ত উত্তর cache করেই না, ফলে একটি অপ্রয়োজনীয় cookie পুরো path-এর hit শূন্য করতে পারে। - Negative caching: 404 অল্প সময় রাখলে অস্তিত্বহীন URL-এর বন্যা origin-এ যায় না; মেয়াদ বেশি হলে নতুন জিনিসও “নেই” দেখায় — তাই পর্ব ৬-এর ফল প্রকাশের নকশায় negative caching বন্ধ বা খুব ছোট রাখা হয়েছিল।
৫ · Hit ratio-র অঙ্ক — origin-এর ভার, egress আর origin shield
মোট অনুরোধ R প্রতি সেকেন্ডে, edge-এর hit ratio h. প্রতিটি অনুরোধ হয় hit, নয় miss; miss-এর অনুরোধগুলোই origin-এর দিকে যায়। তাই shield ছাড়া:
খেয়াল করুন origin দেখে miss ratio. h = ০.৯০ মানে miss ০.১০; h = ০.৯৯ মানে miss ০.০১. Hit ratio মাত্র ৯ শতাংশ-বিন্দু বাড়ল, অথচ origin-এর ভার কমল ১০ গুণ; ৯৯% থেকে ৯৯.৯% আবার ১০ গুণ। তাই hit ratio-কে “কয়টি নয়” দিয়ে ভাবা হয়, availability-র মতো (পর্ব ৫)। উল্টোটাও সত্য: ৯৯% থেকে ৯৮%-এ নামা শুনতে সামান্য, কিন্তু origin-এ চাপ দ্বিগুণ।
Shield-এর অঙ্ক — প্রতিটি edge স্বাধীনভাবে miss করলে
Edge E-টি, আর প্রত্যেকের নিজের cache. ধরুন একটি TTL-এর সময়-জানালায় প্রতিটি edge স্বাধীনভাবে একটি নির্দিষ্ট object-এর জন্য ওপরের স্তরে যেতে বাধ্য হয় p সম্ভাবনায় — সেখানে কেউ সেটি চেয়েছেন আর কপি নেই। তাহলে:
- Shield ছাড়া: জানালাপ্রতি origin-এ গড়ে E × p অনুরোধ (প্রতিটি edge আলাদা)।
- Shield থাকলে: shield প্রতি জানালায় object-টি একবারই আনে, যদি অন্তত একটি edge চায়; তার সম্ভাবনা ১ − (১ − p)E. একই মুহূর্তে কয়েকটি edge-এর miss এলে shield সেগুলো এক করে একটিই অনুরোধ পাঠায় — request collapsing (coalescing).
দুই প্রান্ত। p = ১ — সব edge-এ জনপ্রিয়, যেমন হোমপেজের JS: গুণক ১/E, ৫০টি edge-এ ভার ৫০ গুণ কম। p খুব ছোট — long tail: (১ − p)E ≈ ১ − E·p, তাই গুণক প্রায় ১; shield প্রায় কিছু বাঁচায় না, শুধু একটি বাড়তি hop. বাড়তি লাভ shield-এর বড় cache, যা edge থেকে সরে যাওয়া জিনিসও ধরে রাখে। একই অনুমানে হিসাব:
// Origin load behind a CDN. ASSUMED inputs: change them and run again.
const R = 100000; // total requests per second arriving at the CDN
const SIZE_KB = 50; // average response size, decimal KB (1 KB = 1000 bytes)
const EDGES = 50; // edge locations, each with its own cache
const gbps = (rps) => rps * SIZE_KB * 1000 * 8 / 1e9; // req/s -> Gbit/s
const col = (v, w) => String(v).padStart(w);
// Shield model: in one TTL window each edge, INDEPENDENTLY, needs a given object
// from upstream with probability p. No shield: the origin gets EDGES*p fetches.
// With a shield (one fetch per object per window): 1 - (1-p)^EDGES fetches.
const factor = (p) => (1 - Math.pow(1 - p, EDGES)) / (EDGES * p);
console.log(`users receive ${R} req/s = ${gbps(R).toFixed(1)} Gbit/s from the edges`);
console.log(col("edge hit", 8) + col("origin req/s", 14) + col("Gbit/s", 8) + " |" +
col("shield p=1", 12) + col("p=0.1", 8) + col("p=0.02", 8));
for (const h of [0, 0.5, 0.9, 0.95, 0.99, 0.999]) {
const o = R * (1 - h); // every edge miss goes upstream
// h = 0 means nothing is cacheable, so a shield cannot help either
const s = (p) => (h === 0 ? "-" : (o * factor(p)).toFixed(0));
console.log(col((h * 100).toFixed(1) + "%", 8) + col(o.toFixed(0), 14) +
col(gbps(o).toFixed(2), 8) + " |" + col(s(1), 12) + col(s(0.1), 8) + col(s(0.02), 8));
}
console.log("\n" + col("p", 6) + col("no shield", 12) + col("shield", 9) + col("factor", 9));
for (const p of [1, 0.5, 0.1, 0.02]) {
const none = EDGES * p, sh = 1 - Math.pow(1 - p, EDGES);
console.log(col(p, 6) + col(none.toFixed(1), 12) + col(sh.toFixed(3), 9) +
col(factor(p).toFixed(3), 9));
}আউটপুট:
users receive 100000 req/s = 40.0 Gbit/s from the edges
edge hit origin req/s Gbit/s | shield p=1 p=0.1 p=0.02
0.0% 100000 40.00 | - - -
50.0% 50000 20.00 | 1000 9948 31792
90.0% 10000 4.00 | 200 1990 6358
95.0% 5000 2.00 | 100 995 3179
99.0% 1000 0.40 | 20 199 636
99.9% 100 0.04 | 2 20 64
p no shield shield factor
1 50.0 1.000 0.020
0.5 25.0 1.000 0.040
0.1 5.0 0.995 0.199
0.02 1.0 0.636 0.636
ব্যবহারকারীরা পান মোট ৪০ Gbit/s — এটি বদলায় না, শুধু কোথা থেকে আসে তা বদলায়। Edge-এর hit ৯০% হলে origin-এ সেকেন্ডে ১০,০০০ অনুরোধ আর ৪ Gbit/s; ৯৯%-এ ১,০০০ আর ০.৪ Gbit/s — ঠিক ১০ গুণ কম; ৯৯.৯%-এ আরও ১০ গুণ, ১০০ অনুরোধ। Shield-এর কলাম: সব edge যা চায় (p = ১), সেখানে ৯৯%-এর ১,০০০ নেমে আসে মাত্র ২০-এ — গুণক ০.০২০, অর্থাৎ ১/৫০. p = ০.১-এ গুণক ০.১৯৯ (প্রায় ৫ গুণ লাভ), আর p = ০.০২-এ ০.৬৩৬ — long tail-এ shield-এর লাভ সামান্য। শেষ টেবিলে p = ০.০২-এ shield ছাড়া জানালাপ্রতি গড়ে ১টি fetch, shield-সহ ০.৬৩৬ — কারণ দুটি edge একই জানালায় চাইলে তবেই একটি বাঁচে। 0 সারিতে shield-এর ঘর ফাঁকা: কিছুই cache-যোগ্য না হলে shield-ও কিছু রাখতে পারে না। EDGES ২০০ করে দেখুন p = ১-এ লাভ কত হয়।
মডেলের সীমা। বাস্তবে edge-গুলো পুরো স্বাধীন নয়, জনপ্রিয়তাও বদলায়। Shield বিকল হলে বিকল্প পথ লাগে — নইলে সেটি নতুন single point of failure. আর ব্যক্তিগত উত্তরে collapsing একজনের উত্তর আরেকজনকে দিতে পারে, তাই CDN সেখানে এটি বন্ধ রাখে। সাধারণ cache-এর stampede আর তার প্রতিকার পর্ব ২৯-এ।
এই সাজানোর নাম tiered caching; application-এর বহু-স্তরের cache পর্ব ৩০-এ।
৬ · Hit ratio কোথা থেকে আসে — Zipf জনপ্রিয়তা আর cache-এর আকার
ওয়েবের চাহিদা সমান ভাগে ছড়ানো নয়: অল্প কিছু জিনিস বারবার, বিপুল সংখ্যক জিনিস কদাচিৎ। প্রচলিত মডেল Zipf-ধাঁচের বণ্টন — জনপ্রিয়তার ক্রমে k-তম জিনিস চাওয়ার সম্ভাবনা ১/kα-এর সমানুপাতিক। ১৯৯৯ সালে Breslau ও সহকর্মীদের একটি পরিচিত গবেষণায় web proxy-র trace-এ α মোটামুটি ০.৬ থেকে ০.৮-এর ঘরে পাওয়া গিয়েছিল; আপনার সাইটে মান আলাদা হবে, তাই নিজের log থেকে মাপুন। α যত ছোট, তত লম্বা long tail, আর cache দিয়ে hit ratio বাড়ানো তত কঠিন।
আদর্শ সীমা কত? ধরুন cache সবচেয়ে জনপ্রিয় k-টি জিনিস সবসময় ধরে রাখে। তখন hit ratio = প্রথম k-টির ওজন ÷ মোট ওজন। যোগফলকে integral দিয়ে আন্দাজ করলে, α ≠ ১ হলে:
α = ১-এ ফলটি চোখে পড়ার মতো: cache ১০ গুণ বড় করলে hit ratio বাড়ে একটি নির্দিষ্ট পরিমাণ (ln ১০ ÷ ln N), গুণ হারে নয়। N = ২০,০০০ হলে প্রতি ১০ গুণে মাত্র প্রায় ২৩ শতাংশ-বিন্দু। বাস্তবের cache সবচেয়ে জনপ্রিয়গুলো জানে না, সাম্প্রতিকতা দেখে আন্দাজ করে — যেমন LRU. LRU আর LFU-র তুলনা CN পর্বের অংশ ৮-এ, আর OS-এর page replacement-এ একই প্রশ্ন। নিচে একটি edge-এর LRU cache, ২০,০০০টি জিনিসের catalogue আর α = ০.৮; seeded PRNG বলে প্রতিবার একই ফল:
// One edge, LRU cache, Zipf-like popularity: object of rank k is requested with
// weight 1/k^ALPHA. Seeded PRNG (mulberry32), so every run prints the same numbers.
const N = 20000, ALPHA = 0.8, WARM = 20000, REQS = 200000;
function mulberry32(a) { return () => { a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
const cdf = new Float64Array(N); let sum = 0;
for (let k = 1; k <= N; k++) { sum += 1 / Math.pow(k, ALPHA); cdf[k - 1] = sum; }
function pick(u) { // binary search: first rank whose cdf >= u*sum
let lo = 0, hi = N - 1; const x = u * sum;
while (lo < hi) { const m = (lo + hi) >> 1; if (cdf[m] < x) lo = m + 1; else hi = m; }
return lo + 1;
}
function lruHitRatio(cap) {
const rnd = mulberry32(17), cache = new Map(); let hits = 0;
for (let i = 0; i < WARM + REQS; i++) {
const obj = pick(rnd());
if (cache.has(obj)) { cache.delete(obj); cache.set(obj, 1); if (i >= WARM) hits++; }
else { cache.set(obj, 1); if (cache.size > cap) cache.delete(cache.keys().next().value); }
}
return hits / REQS;
}
console.log(`catalogue ${N} objects, Zipf alpha ${ALPHA}, ${REQS} requests after ${WARM} warm-up`);
console.log("cache size % of catalogue LRU hit top-k bound origin share");
for (const cap of [20, 200, 1000, 2000, 6000]) {
const lru = lruHitRatio(cap), bound = cdf[cap - 1] / sum; // keep the cap most popular forever
console.log(String(cap).padStart(10) + ((cap / N * 100).toFixed(1) + "%").padStart(16) +
(lru * 100).toFixed(1).padStart(8) + "%" + (bound * 100).toFixed(1).padStart(12) + "%" +
((1 - lru) * 100).toFixed(1).padStart(13) + "%");
}আউটপুট:
catalogue 20000 objects, Zipf alpha 0.8, 200000 requests after 20000 warm-up
cache size % of catalogue LRU hit top-k bound origin share
20 0.1% 3.9% 14.8% 96.1%
200 1.0% 17.4% 31.4% 82.6%
1000 5.0% 34.6% 48.6% 65.4%
2000 10.0% 44.9% 57.9% 55.1%
6000 30.0% 66.7% 75.6% 33.3%
এক, catalogue-এর মাত্র ১%-এ আদর্শ cache অনুরোধের ৩১.৪% সামলাত — জনপ্রিয়তার ঘনত্বের শক্তি; কিন্তু এই long tail-এ ৯০%-এর বেশি hit-এর জন্য catalogue-এর বড় অংশই লাগে। দুই, LRU সীমার নিচে — ২০০-তে ১৭.৪% বনাম ৩১.৪% — কারণ একবার-আসা জিনিসও জনপ্রিয়দের সাময়িক সরায়; cache বড় হলে ফাঁক কমে (৬,০০০-এ ৬৬.৭% বনাম ৭৫.৬%). তিন, cache তিন গুণ (২,০০০ থেকে ৬,০০০) করলে origin-এর ভাগ ৫৫.১% থেকে ৩৩.৩%. এখানে TTL নেই; বাস্তবে মেয়াদ ফুরানোর miss-ও যোগ হবে। ALPHA ১.০ করলে ২০০-র cache-এ LRU পায় ৪৩.২% — চাহিদা যত ঘন, cache তত কাজের।
Hit ratio বাড়ানোর হাতল চারটি: cache বড় করা (shield-এর বড় cache), মেয়াদ বাড়ানো (অংশ ৭), key-র অপচয় কমানো (অংশ ৪), আর চাহিদা কম জায়গায় জড়ো করা — ৫০টির বদলে ১০টি বড় edge-এ প্রতিটি জিনিস বেশি বার দেখা হয়, তবে latency বাড়ে। আর hot set এক edge-এর memory-তে ধরলে সবচেয়ে সস্তা লাভ সেখানেই।
৭ · TTL, staleness আর purge — কতক্ষণ রাখবেন, কীভাবে ভোলাবেন
বড় TTL মানে বেশি hit আর কম origin-এর ভার, কিন্তু পুরোনো তথ্য দেখানোর ঝুঁকি — staleness. ছোট TTL উল্টো। ঠিক কতটা? ধরুন origin প্রতি P সেকেন্ডে উত্তরটি বদলায়, আর edge প্রতি T সেকেন্ডে নতুন কপি আনে (অনুরোধ ঘন ঘন এলে)। একটি অনুরোধের মুহূর্তে কপির বয়স a, যা ০ থেকে T-এর মধ্যে মোটামুটি সমভাবে ছড়ানো; আর a সময়ের মধ্যে একটি বদল ঘটে যাওয়ার সম্ভাবনা a/P (a ≤ P হলে)। গড় করলে:
T = P = ৬০ সেকেন্ডে প্রায় অর্ধেক উত্তর পুরোনো, আর পুরোনোগুলো গড়ে T/৩ সেকেন্ড পিছিয়ে; origin-এ edgeপ্রতি ঘণ্টায় প্রায় ৩৬০০ ÷ T অনুরোধ। নিচে সূত্রগুলোর যাচাই, সাথে SWR আর stale-if-error. উত্তরটি একটি দামের তালিকা, প্রতি ৬০ সেকেন্ডে বদলায়; edge-এ সেকেন্ডে ১০টি অনুরোধ; origin থেকে আনতে ০.৩ সেকেন্ড, তখন যারা আসে তারা সেই fetch-এর অপেক্ষায়। বদলের মুহূর্ত মিনিটের ১০টি ভিন্ন জায়গায় রেখে গড় করা, যাতে কাকতালীয় মিল ফল না বদলায়:
// One edge, one API response; the origin publishes a new version every 60 s. Requests: seeded
// Poisson, 10/s, one hour. An origin fetch takes 0.3 s; requests arriving meanwhile wait for it.
const RATE = 10, FETCH = 0.3, UPDATE = 60, HOUR = 3600;
function mulberry32(a) { return () => { a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
function run(ttl, swr, sie, outage, off) { // off = when, inside a minute, updates happen
const rnd = mulberry32(42), up = (x) => !(outage && x >= 1800 && x < 1900); // 100 s outage
let t = 0, have = false, ver = 0, at = 0, pend = -1, pv = 0, s = [0, 0, 0, 0, 0, 0];
for (;;) { // s = requests, fetches, stale, lag, waited, errors
t += -Math.log(1 - rnd()) / RATE; if (t >= HOUR) break; s[0]++;
if (pend >= 0 && t >= pend) { have = true; ver = pv; at = pend; pend = -1; }
const cur = Math.floor((t + off) / UPDATE), age = have ? t - at : Infinity; let served = ver;
if (age >= ttl) { // copy expired, or no copy yet
if (pend < 0 && up(t)) { pend = t + FETCH; pv = cur; s[1]++; }
if (age < ttl + swr || (!up(t) && age < ttl + sie)) served = ver; // stale, no wait
else if (pend >= 0) { s[4]++; served = pv; } // wait for fetch
else { s[5]++; continue; } // error
}
if (served < cur) { s[2]++; s[3] += t + off - UPDATE * (served + 1); }
}
return s;
}
function show(name, ttl, swr = 0, sie = 0, outage = false) { // average over 10 phases
const a = [0, 0, 0, 0, 0, 0];
for (let off = 0; off < 60; off += 6) run(ttl, swr, sie, outage, off).forEach((v, i) => a[i] += v / 10);
console.log(name.padEnd(22) + a[1].toFixed(0).padStart(8) + (a[1] * 50).toFixed(0).padStart(10) +
((a[2] / a[0] * 100).toFixed(1) + "%").padStart(7) + ((a[3] / a[2]).toFixed(1) + " s").padStart(8) +
a[4].toFixed(0).padStart(8) + a[5].toFixed(0).padStart(7));
}
console.log("policy origin/h x50 edges stale avg lag waited errors");
for (const ttl of [5, 30, 60, 300]) show(`max-age=${ttl}`, ttl);
show("30 + swr 30", 30, 30);
console.log("-- origin down from t=1800 s to t=1900 s --");
show("30 + swr 30", 30, 30, 0, true);
show("30 + swr 30 + sie 600", 30, 30, 600, true);আউটপুট:
policy origin/h x50 edges stale avg lag waited errors
max-age=5 668 33400 4.5% 1.8 s 2647 0
max-age=30 119 5950 25.1% 10.1 s 474 0
max-age=60 60 3000 49.9% 20.0 s 263 0
max-age=300 12 600 89.7% 135.4 s 51 0
30 + swr 30 119 5950 25.7% 10.2 s 3 0
-- origin down from t=1800 s to t=1900 s --
30 + swr 30 116 5800 25.9% 10.6 s 9 479
30 + swr 30 + sie 600 116 5800 27.2% 12.8 s 9 0
সূত্রের সাথে মিলিয়ে দেখুন। পুরোনো উত্তরের ভাগ: TTL ৫-এ ৪.৫% (সূত্রে ৫ ÷ ১২০ ≈ ৪.২%, বাকিটা ০.৩ সেকেন্ডের fetch), ৩০-এ ২৫.১% (২৫%), ৬০-এ ৪৯.৯% (৫০%), ৩০০-এ ৮৯.৭% (১ − ৬০ ÷ ৬০০ = ৯০%). গড় পিছিয়ে থাকা ১.৮, ১০.১, ২০.০ সেকেন্ড — প্রায় T/৩. Origin-এ ঘণ্টায় এক edge থেকে ৬৬৮, ১১৯, ৬০, ১২টি fetch; shield ছাড়া ৫০টি edge-এ ৫০ গুণ। waited: TTL ৩০-এ ৪৭৪ জন origin-এর জন্য দাঁড়িয়ে, কিন্তু SWR দিলে মাত্র ৩ (শুধু একদম প্রথম fetch-এ) — origin-এর ভার একই ১১৯. শেষ দুই সারিতে origin ১০০ সেকেন্ড বন্ধ: শুধু SWR-এ গড়ে ৪৭৯টি error; stale-if-error=600 দিলে শূন্য — দাম, কিছু উত্তর আরও পুরোনো (গড়ে ১২.৮ সেকেন্ড)।
TTL বেছে নেওয়ার নিয়ম
- আগে ব্যবসার প্রশ্ন: পুরোনো তথ্য কতক্ষণ চলে? হোমপেজে এক মিনিট, দামে হয়তো ৩০ সেকেন্ড, টাকা পাঠানোর পর ব্যালান্সে শূন্য — সেটি cache-এ নয়।
- তারপর অঙ্ক: সর্বোচ্চ পিছিয়ে থাকা ≈ TTL + SWR; গড় পুরোনো ভাগ T ÷ ২P; ঘন চাওয়া জিনিসে origin-এর ভার ঘণ্টায় ≈ edge-সংখ্যা × ৩৬০০ ÷ T, shield-সহ প্রায় ৩৬০০ ÷ T.
- বদলের খবর জানা থাকলে: দীর্ঘ TTL আর বদলের মুহূর্তে purge — staleness প্রায় শূন্য, ভারও কম; TTL তখন শুধু নিরাপত্তা-জাল।
Purge — URL ধরে, না tag ধরে
Purge মানে মেয়াদ শেষের আগেই CDN-কে কপিটি ভুলে যেতে বলা। URL ধরে purge সরল, কিন্তু একটি শিরোনাম থাকে খবরের পাতা, হোমপেজ, বিভাগ, API আর RSS-এ — সব URL অ্যাপের জানা কঠিন। তাই অনেক CDN দেয় surrogate key বা cache tag: origin প্রতিটি উত্তরে একটি header-এ লেখে সেটি কোন জিনিসের ওপর নির্ভর করে, আর পরে একটি tag ধরে সব মেলানো কপি একসাথে purge. Header-এর নাম, সীমা আর ছড়ানোর সময় CDN-ভেদে আলাদা।
| সময় | |
| কী হলো |
- প্রস্তুতি: প্রতিটি উত্তরে tag. খবরের পাতা আর API-তে
article-4821, হোমপেজেhomeআর সেখানে দেখানো সব খবরের tag. CDN-এ মেয়াদ এক দিন (s-maxage=86400), ব্রাউজারে ৬০ সেকেন্ড। - শিরোনাম সংশোধন। সম্পাদক শিরোনাম ঠিক করলেন, ডেটাবেসে লেখা হলো। CDN কিছু জানে না — কেউ না বললে পুরোনো শিরোনাম এক দিন চলবে।
- Tag ধরে purge. লেখা নিশ্চিত হওয়ার পরে অ্যাপ purge API-তে পাঠাল
article-4821— কোন কোন URL, সেই হিসাব tag-এ। এটি soft purge: কপি মুছে না ফেলে stale চিহ্নিত, যাতে origin বিকল হলেstale-if-errorতখনো কাজ করে। - সব জায়গায় ছড়াল। কয়েক সেকেন্ডে (CDN-ভেদে আলাদা) সব edge আর shield-এ তিনটি কপি stale.
/news/4790-এর tag আলাদা, তাই অক্ষত — পুরো সাইট purge-এর ঢল হলো না। - পরের অনুরোধে নতুন কপি। ঢাকায় কেউ হোমপেজ খুললেন: কপি stale, তাই edge নতুনটি আনল (SWR থাকলে ওই একজন হয়তো শেষবার পুরোনোটি পেলেন)। কেউ না চাইলে কপি চাওয়ার মুহূর্তে হালনাগাদ হবে।
- ব্রাউজারের কপি purge-এর নাগালের বাইরে। ১০:০৫-এ যিনি হোমপেজ খুলেছেন, তাঁর পুরোনো কপি ৬০ সেকেন্ড fresh. তাই CDN-এ দীর্ঘ মেয়াদ আর purge, ব্রাউজারে ছোট মেয়াদ; সবচেয়ে খারাপ ক্ষেত্রে পুরোনো দেখানো ≈ purge ছড়ানো + ব্রাউজারের max-age.
Purge-এর দুটি ফাঁদ। এক, ক্রমের দৌড়: লেখা primary-তে, সাথে সাথে purge; কিন্তু origin পড়ে read replica থেকে, যেখানে লেখা তখনো পৌঁছায়নি (পর্ব ২২)। পরের miss পুরোনো তথ্য পড়ে আবার এক দিনের জন্য cache-এ বসাল! প্রতিকার: লেখা পড়ার পথে দেখা যাওয়ার পরে purge, বা কয়েক সেকেন্ড পরে দ্বিতীয়বার, বা সংস্করণ-যুক্ত key. দুই, “সব purge”: সব জনপ্রিয় জিনিসের প্রথম miss একসাথে origin-এ — নিজের হাতে বানানো thundering herd; দরকারে soft purge, ধাপে ধাপে।
সম্পাদনায় purge চলে, কিন্তু খুব ঘন ঘন বদলানো জিনিসে (লাইভ স্কোর) ছোট TTL আর SWR সহজ ও সস্তা। Invalidation-এর সাধারণ কৌশল পর্ব ২৮ আর ২৯-এ।
৮ · Cache-এর বাইরে edge — push বনাম pull, dynamic acceleration, edge computing, নিরাপত্তা, multi-CDN আর খরচ
Push বনাম pull CDN
| Pull CDN | Push CDN | |
|---|---|---|
| কপি কীভাবে আসে | প্রথম miss-এ edge নিজে origin থেকে টেনে আনে | আপনি আগেই CDN-এর storage-এ তুলে দেন |
| প্রথম অনুরোধ | ধীর (miss) | দ্রুত, যদি আগে ছড়ানো থাকে |
| পরিচালনা | সহজ — শুধু origin-এর ঠিকানা আর header | upload, মোছা আর সংস্করণের হিসাব আপনার |
| উপযুক্ত | বেশিরভাগ ওয়েবসাইট আর API | বড়, আগে জানা ফাইল — app-এর update, ভিডিও-লাইব্রেরি |
বাস্তবে সীমারেখা ঝাপসা: pull CDN-এর origin প্রায়ই object storage, যেখানে আপনি ফাইল “push” করেন; আর জানা ভিড়ের আগে জনপ্রিয় URL একবার করে চেয়ে cache ভরা যায় — cache warming (পর্ব ৬)।
Dynamic acceleration — cache না হলেও CDN কেন দ্রুত
ব্যক্তিগত API বা checkout-এর উত্তর cache হয় না, তবু CDN-এর ভেতর দিয়ে প্রায়ই দ্রুত — dynamic acceleration. কারণ: handshake শেষ হয় কাছের edge-এ (TLS termination); edge থেকে origin-এ আগে খোলা, গরম connection (keep-alive, slow start পেরোনো); আর কিছু CDN নিজের network-এ পথ বাছে (লাভ পথভেদে আলাদা)। পর্ব ৭-এর নিয়মে নতুন HTTPS-এ প্রথম byte পর্যন্ত ৩ RTT; ধরা যাক ব্যবহারকারী ↔ origin RTT ২০০ ms, ব্যবহারকারী ↔ edge ১০ ms, edge ↔ origin ২০০ ms, origin-এর কাজ ৩০ ms:
- সরাসরি origin-এ: ৩ × ২০০ + ৩০ = ৬৩০ ms.
- Edge দিয়ে: handshake ২ × ১০ = ২০, তারপর অনুরোধ ১০ + edge থেকে গরম connection-এ ২০০ + কাজ ৩০ = ২৪০; মোট ২৬০ ms — cache ছাড়াই ৩৭০ ms কম।
- একই connection-এ পরের অনুরোধ: সরাসরি ২০০ + ৩০ = ২৩০ ms, edge দিয়ে ১০ + ২০০ + ৩০ = ২৪০ ms — এখানে edge বরং ১০ ms বাড়ায়। লাভ handshake-এ, প্রতিটি অনুরোধে নয়।
Edge computing — edge-এ ছোট প্রোগ্রাম
অনেক CDN edge-এ আপনার ছোট কোড চালাতে দেয় — নাম সেবাভেদে আলাদা, যেমন edge function বা worker; ধারণাটির নাম edge computing. ভালো কাজগুলোর মিল একটাই: সিদ্ধান্তের তথ্য অনুরোধের ভেতরেই আছে।
- Redirect আর rewrite: পুরোনো URL থেকে নতুনে, ভাষা বা দেশ দেখে সঠিক পাতায়; URL shortener-এর redirect-ও (পর্ব ৫৪)।
- A/B পরীক্ষা: ব্যবহারকারীর id-র hash দেখে দল বাছা, আর দলটি cache key-তে — দলপ্রতি একটি কপি, প্রতিজনের নয়।
- প্রাথমিক auth যাচাই: JWT-এর মতো স্বাক্ষর-করা token edge-এ যাচাই করে জাল বা মেয়াদোত্তীর্ণ অনুরোধ সেখানেই ফেরানো; চূড়ান্ত অনুমতি origin-এ (পর্ব ৫২)।
- Header আর key: অংশ ৪-এর normalization, নিরাপত্তা-header, অপ্রয়োজনীয় cookie সরানো।
সীমা (CDN আর plan-ভেদে আলাদা, লেখার সময়ের documentation দেখুন): অনুরোধপ্রতি CPU-সময় খুব কম, প্রায়ই মিলিসেকেন্ডের ঘরে; memory আর library সীমিত; আর সবচেয়ে বড় কথা — ডেটাবেস দূরে. প্রতিটি query দূরের region-এ একটি পূর্ণ round trip, তাই পরপর কয়েকটি query-তে edge উল্টো ধীর (ভাবনার প্রশ্ন ৪)। Edge-এর key-value store দ্রুত পড়া যায়, কিন্তু লেখা ছড়াতে সময় নেয় — eventual consistency (পর্ব ৪০); টাকা বা stock-এর কাজ তাই region-এ।
Edge-এ নিরাপত্তা — প্রতিরক্ষার প্রথম দেয়াল
সব traffic edge পেরিয়ে আসে, তাই প্রতিরক্ষার অনেকটা সেখানে বসানো সহজ:
- DDoS শোষণ: বন্যা-traffic anycast-এ বহু শহরে ভাগ হয়, network-স্তরের বন্যা edge-এই ছাঁকা হয়। পূর্ণ ছবি CN-এর DDoS-এর প্রতিরক্ষা অংশে।
- WAF আর rate limit: পরিচিত ক্ষতিকর ধরন আর অতিরিক্ত অনুরোধ edge-এ থামে। WAF নিরাপদ কোডের বিকল্প নয়; rate limit-এর algorithm পর্ব ৪৯-এ।
- TLS: আধুনিক version আর certificate-এর নবায়ন এক জায়গায়; edge থেকে origin-এর পথও এনক্রিপ্ট রাখুন।
- Origin আড়াল করা: origin-এর ঠিকানা জানলে কেউ CDN এড়িয়ে সরাসরি যেতে পারে। তাই origin শুধু CDN-এর অনুরোধ নেবে — ঠিকানা-তালিকা, গোপন header বা পারস্পরিক certificate যাচাই দিয়ে — আর তার IP প্রকাশ্য থাকবে না।
Authentication, secrets আর এনক্রিপশনের পুরো নকশা পর্ব ৫২-এ।
Multi-CDN — দুই CDN, দুই গুণ নিশ্চিন্ত?
একটি CDN-এর বিভ্রাট পুরো সাইটের বিভ্রাট। তাই বড় সেবা কখনো দুই বা বেশি CDN রাখে — multi-CDN: DNS বা steering সেবা দিয়ে ভাগ, অঞ্চলভেদে দ্রুতটি, বিকল হলে অন্যটি; সিদ্ধান্তে প্রায়ই আসল ব্যবহারকারীর মাপজোখ (RUM). দাম: দুটি cache আলাদা গরম হয়, তাই long tail-এ miss বাড়ে; শুধু সাধারণ সুবিধাগুলো ব্যবহার করা যায়; config, purge আর certificate দুই জায়গায় মেলাতে হয়; আর DNS দিয়ে সরানো DNS TTL-এ বাঁধা (পর্ব ৭)। সিদ্ধান্তটি availability আর খরচের তুলনা — সবার দরকার নেই।
খরচের মডেল
দাম প্রদানকারী, অঞ্চল আর চুক্তিভেদে আলাদা আর বদলায়, তাই এখানে সংখ্যা নয়, শুধু কাঠামো: CDN থেকে ব্যবহারকারীর দিকে প্রতি GB (অঞ্চলভেদে হার আলাদা), কখনো অনুরোধের সংখ্যা ধরে, আর edge function বা WAF-এর মতো সুবিধার আলাদা দাম। অন্য দিকে origin-এর cloud থেকে বেরোনো byte-এর egress — কিছু প্রদানকারী নিজেদের CDN-এ যাওয়া byte-এ ছাড় দেয়, যাচাই করুন।
Byte hit ৯০% থেকে ৯৯% হলে origin-এর egress ১০ গুণ কমে, কিন্তু CDN-এর বিল প্রায় একই — ব্যবহারকারী একই byte পান। সেই বিল কমায় ছোট ছবি আর ভালো compression. Bandwidth আর egress-এর হিসাব পর্ব ৩-এ।
আরও গভীরে: একটি edge-এর কয়েকশো সার্ভারে কোন URL কোথায়, তা ঠিক করতে consistent hashing ব্যবহার হয় — উৎস Karger ও সহকর্মীদের ১৯৯৭ সালের গবেষণাপত্র (পর্ব ১৬)। Disk-cache-এ একবার-আসা জিনিস জায়গা নষ্ট না করুক বলে অনেক নকশা দ্বিতীয়বার চাওয়ার পরেই জমায়, আর eviction-এ ARC বা TinyLFU ধরনের নিয়ম সাম্প্রতিকতা ও পুনরাবৃত্তি দুটোই মাপে। HTTP caching-এর বর্তমান মান RFC 9111 (২০২২); CDN-এর বাড়তি নিয়ম মানের বাইরে।
পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা system design ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন আসতে পারে:
- Hit ratio ৯০% থেকে ৯৯% হলে origin-এর ভার কত কমে? ভার R(১ − h), miss ০.১ থেকে ০.০১ — ১০ গুণ।
- Origin shield কী মেটায়? E-টি edge-এর আলাদা miss একই জিনিসে origin-এ E বার; shield আর collapsing তা একবারে নামায়।
- CSS এক বছর cache করেও হালনাগাদ তাৎক্ষণিক? নামে hash,
immutable; HTML ছোট মেয়াদে; আগে asset, পরে HTML. - Purge ব্রাউজারের কপি সরায়? না — তাই ব্রাউজারে ছোট max-age, CDN-এ বড় s-maxage আর purge.
- stale-while-revalidate কী দেয়? মেয়াদ শেষেও কিছুক্ষণ পুরোনো কপি সাথে সাথে, পেছনে refresh (RFC 5861).
৯ · AI-এর যুগে — CDN আর caching-এর পরামর্শ যাচাই
“সাইট ধীর, ভিড় সামলাতে পারছে না” — AI প্রায়ই বলে “CDN বসান, সব cache করুন, Redis আর Kafka যোগ করুন”। CDN প্রায়ই উত্তরের অংশ, কিন্তু caching-এর ভুল নিঃশব্দ: একজনের তথ্য আরেকজন দেখেন, বা সংশোধন এক দিন পৌঁছায় না। চেনা ভুল:
| AI-এর ভুল | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
“সব API-তে public, s-maxage=60” | ব্যক্তিগত উত্তর shared cache-এ যেতে পারে, Authorization থাকলেও | প্রতিটি endpoint-এর জন্য “উত্তর কি সবার জন্য এক?” প্রশ্ন |
| “Hit ratio ৯০% থেকে ৯৫% — সামান্য উন্নতি” | Origin-এর ভার অর্ধেক হলো (১০% থেকে ৫% miss) | R × (১ − h) নিজে হিসাব |
| “Shield দিলে origin-এর ভার সবসময় E গুণ কমবে” | শুধু সব edge-এ জনপ্রিয় জিনিসে; long tail-এ প্রায় কিছু না | অংশ ৫-এর গুণক [১ − (১ − p)E] ÷ Ep |
| “Purge করলেই সবাই নতুন দেখবেন” | ব্রাউজারের কপি থাকে; replica-র lag-এ পুরোনো আবার cache হয় | max-age আর purge-এর ক্রম যাচাই |
| “পুরো অ্যাপ edge-এ চালান” | প্রতিটি query একটি দূরের round trip | পরপর query গুনে হিসাব |
| CDN-এর সীমা, দাম বা header-এর নাম আত্মবিশ্বাসে বলা; বিকল হলে কী — তার উল্লেখ নেই | প্রায়ই ভুল বা পুরোনো; ৯৯% hit-এ CDN সরলে origin-এ ১০০ গুণ | documentation, নিজের পরীক্ষা আর failure-mode review |
প্রশ্নে সংখ্যা, content-এর ধরন আর ঝুঁকি দিয়ে দিন, আর প্রতিটি সিদ্ধান্তের কারণ চান:
AI-এর CDN-পরিকল্পনা হাতে পেলে — যাচাইয়ের চেকলিস্ট
Miss গুনুন, key পড়ুন, ব্যক্তিগত তথ্য shared cache থেকে দূরে রাখুন।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। Header-এর খুঁটিনাটি নয়, নকশার সিদ্ধান্তের পেছনের কারণগুলোই আসল লক্ষ্য।
প্র ০১১৯৯০-এর দশকের শেষে জনপ্রিয় সাইট হঠাৎ ভিড়ে বসে যেত। CDN কীভাবে এল, আর এক শহরের শত সার্ভারে কোন ফাইল কোথায় থাকবে — এই প্রশ্ন থেকে কোন ধারণা জন্মাল?
তখন ওয়েবের একটি ঠাট্টার নাম ছিল “World Wide Wait”. জনপ্রিয় কোনো সাইটে লিংক পড়লেই ছোট সাইট ভিড়ে বসে যেত — বলা হতো “Slashdot effect”, একটি সংবাদ-সাইটের নামে। সমস্যাটি flash crowd: চাহিদা আগে জানা নেই, আসে মুহূর্তে।
MIT-তে Tom Leighton আর Danny Lewin-সহ একদল গবেষক প্রশ্নটি গাণিতিকভাবে দেখলেন: বহু cache সার্ভারে জিনিস কীভাবে ছড়ালে হঠাৎ জনপ্রিয় জিনিস কোনো এক সার্ভারকে ডোবাবে না? ফল ১৯৯৭ সালের গবেষণাপত্র — Karger, Lehman, Leighton, Panigrahy, Levine ও Lewin-এর “Consistent Hashing and Random Trees” — আর ১৯৯৮ সালে লেখকদের কয়েকজনের হাতে Akamai প্রতিষ্ঠা।
সাধারণ hashing কেন চলে না? URL-এর hash নিয়ে hash mod n দিয়ে সার্ভার বাছলে সব ঠিক থাকে, যতক্ষণ n বদলায় না। কিন্তু ১০০টি সার্ভারের একটি বসে গেলে বা একটি যোগ হলে mod-এর ফল প্রায় সব key-র জন্য বদলায় — ১০০ থেকে ১০১-এ গেলে মোটামুটি ৯৯% key নতুন সার্ভারে। অর্থাৎ পুরো cache কার্যত খালি, আর সব miss একসাথে origin-এ — ঠিক যে বিপদ এড়াতে CDN. Consistent hashing-এ সার্ভার আর key দুটোই একটি বৃত্তে বসে, আর একটি সার্ভার যোগ বা বাদে শুধু প্রায় ১/n ভাগ key সরে — ১০১টি সার্ভারে প্রায় ১%. ধারণাটি পরে Dynamo, Cassandra আর বহু distributed cache-এর ভিত্তি হয়েছে; ring, virtual node আর rendezvous hashing-এর পুরো আলোচনা পর্ব ১৬-এ।
গবেষণাপত্রের দ্বিতীয় অংশ, random trees, ধরে হট-স্পটের প্রশ্ন: খুব জনপ্রিয় জিনিসের অনুরোধ একটি গাছের মতো কয়েক স্তরে ছড়ানো, যাতে কোনো এক সার্ভার সব চাপ না পায় — আজকের tiered caching-এর সাথে মিলটি লক্ষ করার মতো।
মূল উপলব্ধি: CDN শুধু “অনেক জায়গায় অনেক কপি” নয়; সার্ভার যোগ-বিয়োগে cache যাতে হঠাৎ খালি না হয়, সেই গাণিতিক নকশাই একে টেকসই করেছে। স্কেলের প্রশ্নে প্রায়ই উত্তর আসে একটি ভালো hash-এর নকশা থেকে।
প্র ০২লগইন-করা ব্যবহারকারীর পাতা সাধারণত CDN-এ cache করা হয় না কেন? তবু ব্যক্তিগত পাতা দ্রুত করার উপায় কী?
দুটি কারণ — একটি নিরাপত্তার, একটি অঙ্কের।
নিরাপত্তা: shared cache একজনের উত্তর আরেকজনকে দেয়, এটাই তার কাজ। ব্যক্তিগত উত্তর সেখানে ঢুকলে পরের জন অন্যের নাম, ঠিকানা বা অর্ডারের তালিকা দেখবেন। এটি তাত্ত্বিক ভয় নয়। ২০১৫ সালের বড়দিনে একটি জনপ্রিয় game-store, Steam, জানায় যে একটি DDoS সামলাতে cache-এর config বদলানোর সময় ভুলবশত কিছু ব্যবহারকারীর অ্যাকাউন্ট-পাতা অন্যদের কাছে দেখা গেছে; প্রতিষ্ঠানটির পরে প্রকাশিত বক্তব্য অনুযায়ী প্রায় ৩৪ হাজার ব্যবহারকারী প্রভাবিত হতে পারতেন। লক্ষ করুন ক্রমটি: ভিড়ের চাপে তাড়াহুড়ো করে caching বাড়ানো — ঠিক যখন ভুলের সম্ভাবনা সবচেয়ে বেশি।
অঙ্ক: নিরাপদে রাখলেও key-তে ব্যবহারকারী লাগে (Vary: Cookie বা token), তাই এক লাখ ব্যবহারকারী মানে এক লাখ কপি, প্রতিটি দু-একবার চাওয়া — hit প্রায় শূন্য, আর জনপ্রিয় সাধারণ জিনিস জায়গা হারায়।
তাহলে উপায়?
- পাতা ভাগ করা: সবার জন্য এক কাঠামো CDN থেকে, ব্যক্তিগত টুকরো আলাদা ছোট
privateAPI-তে। একই চিন্তার পুরোনো রূপ Edge Side Includes (ESI) — ২০০১ সালে W3C-তে জমা দেওয়া একটি প্রস্তাব, যেখানে edge পাতার cache-যোগ্য অংশের সাথে ছোট ব্যক্তিগত অংশ জোড়া দেয়। - দল ধরে রূপ: ব্যক্তির বদলে দেশ, ভাষা, mobile-desktop বা A/B দল ধরে কয়েকটি কপি।
- Cache ছাড়াই CDN-এর লাভ: TLS edge-এ শেষ, গরম connection-এ origin-এ যাওয়া — অংশ ৮-এর হিসাবে প্রথম অনুরোধে কয়েকশো ms কম।
প্রতিরক্ষার অভ্যাস: লগইন-করা path-এ ডিফল্ট private, no-store, CDN-এর নিয়মে এসব path cache-বহির্ভূত, আর প্রতিটি config বদলের আগে দুটি ভিন্ন test-অ্যাকাউন্ট দিয়ে যাচাই যে একজনের তথ্য অন্যজনের কাছে যাচ্ছে না।
মূল উপলব্ধি: cache-এর প্রথম প্রশ্ন “কতটা দ্রুত?” নয়, “এই উত্তর কি সবার জন্য এক?” এর উত্তর “না” হলে বাকি সব প্রশ্ন অর্থহীন।
প্র ০৩“Computer science-এ কঠিন কাজ দুটি: cache invalidation আর নাম দেওয়া” — কথাটি এত জনপ্রিয় কেন? CDN-এর purge-এ ঠিক কোথায় কোথায় কঠিন?
উক্তিটি প্রচলিতভাবে Netscape-এর প্রকৌশলী Phil Karlton-এর বলে ধরা হয়; প্রমাণিত উৎস নিয়ে নিশ্চিত হওয়া কঠিন, কিন্তু প্রকৌশলীরা কথাটি ভালোবাসেন কারণ এটি সত্য। CDN-এ কঠিন জায়গাগুলো:
- কোন কোন কপি? একটি তথ্য বহু উত্তরে ছড়ানো — খবরের পাতা, হোমপেজ, বিভাগ, API, RSS. URL ধরে purge করতে হলে এই নির্ভরতার তালিকা অ্যাপকে জানতে হয়; tag বা surrogate key সেই তালিকা উত্তরের সাথেই লিখে রাখে (অংশ ৭)।
- ক্রমের দৌড়: লেখা primary-তে, পড়া replica থেকে; purge আগে পৌঁছালে পুরোনো তথ্য আবার cache-এ বসে, এবার পুরো মেয়াদের জন্য। এটি distributed system-এর একটি সাধারণ সত্য — দুটি আলাদা পথের (ডেটাবেসের replication আর CDN-এর purge) বার্তা কোন ক্রমে পৌঁছাবে, তার নিশ্চয়তা নেই (পর্ব ৩৮)।
- ছড়ানোর সময়: সেই কয়েক সেকেন্ডে কোনো edge নতুন, কোনোটি পুরোনো দেখায়; network বদলে অন্য edge-এ গেলে কেউ নতুন দেখে আবার পুরোনো দেখতে পারেন — monotonic read ভাঙা (পর্ব ৪০)।
- নাগালের বাইরের কপি: ব্রাউজার, অ্যাপের নিজস্ব cache, প্রতিষ্ঠানের proxy.
- Purge-এর ঢল: বড় purge মানে একসাথে বহু miss — origin-এর জন্য নিজের হাতে বানানো stampede.
এজন্যই অভিজ্ঞ দলগুলো invalidation যতটা পারে এড়ায়। সবচেয়ে শক্তিশালী কৌশল অপরিবর্তনীয় নাম: বিষয়বস্তু বদলালে নামই বদলায় (fingerprinted asset, সংস্করণ-যুক্ত API key), তাই পুরোনো কপি ভুল হয় না, শুধু অব্যবহৃত হয়। যেখানে নাম বদলানো যায় না (খবরের URL), সেখানে ঘটনা ধরে tag-purge, লেখা দেখা যাওয়ার পরে পাঠানো, দরকারে কয়েক সেকেন্ড পরে দ্বিতীয়বার; সাথে নিরাপত্তা-জাল হিসেবে সীমিত TTL, আর ব্রাউজারে ছোট মেয়াদ। Eviction, invalidation আর stampede-এর সাধারণ আলোচনা পর্ব ২৯-এ।
মূল উপলব্ধি: “ভুলে যাওয়া”-কে নির্ভরযোগ্য করা কঠিন, “কখনো না বদলানো”-কে নির্ভরযোগ্য করা সহজ। যেখানে পারেন, invalidation-কে নামকরণের সমস্যায় বদলে দিন।
প্র ০৪Edge তো ব্যবহারকারীর কাছে — তাহলে পুরো অ্যাপটাই edge-এ চালালে কি সব দ্রুত হবে?
শুনতে যৌক্তিক, কিন্তু উত্তর নির্ভর করে ডেটা কোথায় তার ওপর। কোড সারা পৃথিবীতে ছড়ানো সহজ; ডেটা ছড়ানো কঠিন, কারণ লেখার পর সব কপি মেলাতে হয়।
একটি হিসাব করি। একটি পাতা বানাতে ডেটাবেসে পরপর ৫টি query লাগে — প্রতিটির ফল পরেরটির ইনপুট। ডেটাবেস একটি দূরের region-এ। ধরা যাক ব্যবহারকারী ↔ region RTT ২০০ ms, ব্যবহারকারী ↔ edge ১০ ms, edge ↔ ডেটাবেস ১৯০ ms, region-এর ভেতরে query-প্রতি ১ ms, আর হিসাবের কাজ ২০ ms; connection সব আগে থেকে খোলা।
- অ্যাপ region-এ: ২০০ + ৫ × ১ + ২০ = ২২৫ ms.
- অ্যাপ edge-এ, ডেটাবেস region-এ: ১০ + ৫ × ১৯০ + ২০ = ৯৮০ ms — চার গুণের বেশি ধীর! প্রতিটি query এখন একটি সমুদ্র-পারের round trip.
- Query এক করে একটি endpoint: ১০ + ১৯০ + ২০ = ২২০ ms — region-এর সমান, তাহলে edge-এ রাখার লাভও নেই।
- পড়ার ডেটা edge-এর store-এ কপি: ১০ + ৫ × ~১ + ২০ ≈ ৩৫ ms — দারুণ দ্রুত, কিন্তু কপি পুরোনো হতে পারে (eventual consistency), আর লেখা তবু দূরের primary-তে।
তার ওপর CPU-সময় আর memory-র কড়া সীমা (সেবাভেদে আলাদা), আর হাজার জায়গায় ছড়ানো কোডের debug আর log কঠিন।
তাই প্রচলিত বিভাজন: edge-এ সেসব কাজ, যার তথ্য অনুরোধেই আছে বা যা পুরোনো হলেও চলে — redirect, token-এর স্বাক্ষর যাচাই, A/B দল, cache key, ছবির আকার বদল, অল্প-বদলানো config পড়া। Region-এ সেসব কাজ, যা ডেটাবেসের সাথে ঘন ঘন কথা বলে বা যেখানে সঠিকতা জরুরি — অর্ডার, পেমেন্ট, stock. ব্যবহারকারী কয়েক মহাদেশে ছড়ানো হলে উত্তর edge নয়, ডেটাসহ কয়েকটি region (পর্ব ৫১)।
মূল উপলব্ধি: latency-র হিসাবে গুনুন কতবার দূরের ডেটার কাছে যেতে হয়। কোড কাছে আনলে লাভ শুধু তখনই, যখন ডেটাও কাছে, বা দরকারই নেই।
প্র ০৫CDN নিজেই যদি বসে যায়? “তখন origin সরাসরি সামলাবে” — পরিকল্পনাটি কতটা বাস্তব, আর multi-CDN কি সমাধান?
বড় CDN-ও বিকল হয়, আর হলে তার পেছনের হাজারো সাইট একসাথে। প্রকাশিত post-mortem অনুযায়ী দুটি পরিচিত ঘটনা: ২০১৯ সালের ২ জুলাই Cloudflare-এর WAF-এ একটি নতুন নিয়মের regular expression সারা বিশ্বের সার্ভারের CPU প্রায় পুরো দখল করে নেয়, আর প্রায় আধা ঘণ্টা বহু সাইট error দেখায়। ২০২১ সালের ৮ জুন Fastly-র একটি গ্রাহকের বৈধ config বদল একটি লুকানো বাগ জাগিয়ে তোলে; প্রতিষ্ঠানটির বিবরণে network-এর প্রায় ৮৫% error ফেরায়, আর এক ঘণ্টার কম সময়ে বেশিরভাগ ঠিক হয়। দুই ঘটনাতেই মূল শিক্ষা একই: একটি বদল একসাথে সব জায়গায় ছড়িয়েছিল — পরে ধাপে ধাপে ছড়ানোর ওপর জোর দেওয়া হয় (canary, পর্ব ৫৩)।
“Origin সরাসরি সামলাবে” — অঙ্ক কী বলে? R = ১,০০,০০০, hit ৯৯% — origin দেখে সেকেন্ডে ১,০০০. CDN সরিয়ে DNS সরাসরি origin-এ দিলে origin পায় পুরো ১,০০,০০০ — ১০০ গুণ, সাথে ৪০ Gbit/s-এর মতো egress. স্বাভাবিকের দুই-তিন গুণের জন্য বানানো origin প্রায় নিশ্চিত বসে যাবে, যদি না সামনে load shedding আর সরল সংস্করণ থাকে (পর্ব ১৮)।
বাস্তব বিকল্প: Multi-CDN — দাম দুটি আলাদা cache, দুই জায়গায় config আর purge, DNS TTL-এর দেরি; বিকল্পটি ঠান্ডা থাকলে সরানোর মুহূর্তে origin-এ ঢল, তাই দুটিতেই নিয়মিত কিছু traffic রাখা হয়। জরুরি static সংস্করণ — জরুরি পাতাগুলোর আগে বানানো কপি আলাদা সেবায় (পর্ব ৬-এর বিকল্প পথের মতো)। নিজের CDN-নিয়ম বদলও একটি deploy — ধাপে ধাপে, ফেরানোর পথ রেখে। কোনটি নেবেন, তা availability-র লক্ষ্য আর খরচের তুলনা (পর্ব ৫, পর্ব ৫১)।
মূল উপলব্ধি: যে স্তর ভারের ৯৯% সামলায়, সেটি সবচেয়ে বড় নির্ভরতাও। Hit ratio যত ভালো, CDN-বিভ্রাটে origin-এর ধাক্কা তত বড় — তাই failure-mode review-তে “CDN না থাকলে” প্রশ্নটি সবার আগে।
অনুশীলন
কাগজ-কলমে হিসাব করুন, তারপর উত্তর মেলান। শেষ প্রশ্নে আগে অনুমান, তারপর simulator চালিয়ে যাচাই।
-
Hit ratio-র লক্ষ্য আর origin-এর ভার: একটি কাল্পনিক ভিডিও-বিহীন সংবাদ-অ্যাপ: peak-এ CDN-এ সেকেন্ডে মোট ৮০,০০০ অনুরোধ, গড় উত্তর ৪০ KB (১ KB = ১,০০০ byte). (ক) Edge-এর hit ratio ৯২%, ৯৮% আর ৯৯.৫% হলে origin-এ সেকেন্ডে কত অনুরোধ আর কত Gbit/s? (shield নেই) (খ) Origin নিরাপদে সেকেন্ডে ১,৫০০ অনুরোধ সামলায়। ন্যূনতম কত hit ratio লাগবে? ভিড় দ্বিগুণ হলে? (গ) ৪০টি edge, shield নেই; ১০,০০০টি জনপ্রিয় জিনিস, প্রতিটি সব edge-এ চাওয়া হয়, TTL ৬০ সেকেন্ড। শুধু মেয়াদ ফুরানোর কারণে origin-এ সেকেন্ডে কত অনুরোধ? একটি shield বসালে?
ব্যবহারকারীরা পান ৮০,০০০ × ৪০ KB = ৩.২ GB/s = ২৫.৬ Gbit/s — এটি CDN থেকে।
- (ক) ৯২%: ৮০,০০০ × ০.০৮ = ৬,৪০০ অনুরোধ, × ৪০ KB = ২৫৬ MB/s = ২.০৪৮ Gbit/s. ৯৮%: ১,৬০০, ০.৫১২ Gbit/s. ৯৯.৫%: ৪০০, ০.১২৮ Gbit/s. Hit ৭.৫ বিন্দু বাড়ল, ভার কমল ১৬ গুণ (০.০৮ ÷ ০.০০৫)।
- (খ) ৮০,০০০ × (১ − h) ≤ ১,৫০০, অর্থাৎ ১ − h ≤ ০.০১৮৭৫: h ≥ ৯৮.১২৫%. ভিড় দ্বিগুণ হলে ১ − h ≤ ০.০০৯৩৭৫: h ≥ প্রায় ৯৯.০৬%. ভিড়ে hit কিছুটা বাড়লেও সেই ভরসায় না থেকে origin-এর সামনে load shedding রাখুন।
- (গ) Shield ছাড়া প্রতিটি জিনিস প্রতি মিনিটে প্রতিটি edge থেকে একবার: ১০,০০০ × ৪০ ÷ ৬০ ≈ সেকেন্ডে ৬,৬৬৭. Shield-সহ প্রতি মিনিটে একবার: ১০,০০০ ÷ ৬০ ≈ সেকেন্ডে ১৬৭ — ৪০ গুণ কম (p = ১-এর গুণক ১/E).
-
চার ধরনের content-এর key আর TTL নকশা করুন: একটি কাল্পনিক অনলাইন বাজার। প্রতিটির জন্য Cache-Control, cache key-তে কী থাকবে আর কী থাকবে না, আর বদলালে পুরোনো কপি কীভাবে সরবে: (ক)
/assets/app.3f9a2c.js(খ) পণ্যের ছবি/img/p/4821.jpg?w=400&utm_source=fb(গ) সবার জন্য এক দামের তালিকা/api/v1/prices?category=rice, যা প্রতি ৫ মিনিটে বদলায়, আর ব্যবসা বলছে এক মিনিটের বেশি পুরোনো দাম দেখানো চলবে না (ঘ) লগইন-করা ব্যবহারকারীর/cart.- (ক)
public, max-age=31536000, immutable. Key: host + path; query আর cookie বাদ, শুধু মানসম্মত encoding;Set-Cookieসরান। বদলে নতুন নাম — purge লাগে না। - (খ) Key: host + path + allow-list-এর
w(২০০, ৪০০, ৮০০, ১৬০০-তে গোল করা) + format-এর শ্রেণি (avif, webp বা jpeg —Acceptথেকে edge-এ)।utm_*,fbclidআর অচেনা সব param বাদ। CDN-এ দীর্ঘ মেয়াদ (কয়েক দিন থেকে এক মাস), ব্রাউজারে এক দিন। ছবি বদলালে নতুন নাম (4821-v2.jpg), না পারলে tagproduct-4821ধরে purge. - (গ) Key: host + path +
category(ছোট হাতের অক্ষরে মানসম্মত), বাকি param বাদ। দুই বিকল্প: শুধু TTL —max-age=10, s-maxage=30, stale-while-revalidate=15; সবচেয়ে খারাপ পিছিয়ে থাকা মোটামুটি CDN-এ ৩০ + ১৫ আর ব্রাউজারে ১০ = ৫৫ সেকেন্ড (সাথে fetch-এর সময়), সীমার ভেতরে; CDN-এ গড় পুরোনো ভাগ ≈ ৩০ ÷ ৬০০ = ৫%. ঘটনা ধরে purge — দাম বদলের job শেষে tagprices-ricepurge, আরs-maxage=300নিরাপত্তা-জাল হিসেবে; পিছিয়ে থাকা প্রায় purge ছড়ানোর সময় আর ব্রাউজারের ১০ সেকেন্ড, origin-এর ভারও কম। দ্বিতীয়টি ভালো, যদি purge-এর ক্রম (লেখা দেখা যাওয়ার পরে) নিশ্চিত করা যায়। ব্রাউজারেmax-ageছোট রাখা জরুরি, কারণ সেখানে purge পৌঁছায় না। আরstale-if-errorদেবেন কি না, তা ব্যবসার সিদ্ধান্ত: origin-এর বিভ্রাটে এক মিনিটের সীমা ভেঙে পুরোনো দাম দেখানো ভালো, না error? পুরোনো দেখালে উত্তরে আর পাতায় “হালনাগাদ: কখন” সময়টি রাখুন। - (ঘ)
private, no-store; CDN-এর নিয়মে/cartcache-বহির্ভূত, collapsing বন্ধ — তবু CDN দিয়ে যাবে TLS, WAF আর গরম connection-এর জন্য। পাতার JS, CSS, ছবি (ক) আর (খ)-এর মতো cache হবে।
- (ক)
-
এই API-র উত্তর কি cache-যোগ্য? (সিদ্ধান্ত আর সমালোচনা) একটি কাল্পনিক mobile banking আর কেনাকাটার সেবা। প্রতিটির জন্য বলুন CDN-এ cache হবে কি না, কেন, আর দরকারে নকশা কীভাবে বদলাবেন: (ক)
GET /api/v1/exchange-rates— সবার জন্য এক, প্রতি ১০ মিনিটে বদলায়। (খ)GET /api/v1/me/balance,Authorization: Bearer …সহ; একজন সহকর্মী বলছেন “গতি বাড়াতেpublic, s-maxage=30দিয়ে দিই”। (গ)GET /api/v1/products/4821?session=ab12…— পণ্যের তথ্য সবার জন্য এক, কিন্তু URL-এ session id. (ঘ)POST /api/v1/search, body-তে খোঁজার শব্দ; পণ্যের তালিকা সবার জন্য এক। (ঙ) flash sale চলাকালীনGET /api/v1/sale/4821/stock, প্রতি সেকেন্ডে বদলায়।- (ক) হ্যাঁ।
public, max-age=60, s-maxage=120, stale-while-revalidate=60, stale-if-error=3600, বা হার বদলের সাথে tag-purge. হাজারো poll edge-এই মেটে। - (খ) না — প্রস্তাবটি বিপজ্জনক। Authorization-যুক্ত উত্তর shared cache সাধারণত রাখে না, কিন্তু
publicবাs-maxageসেই সুরক্ষা তুলে দিতে পারে (RFC 9111); key-তে token না থাকলে একজনের ব্যালান্স আরেকজনের কাছে, থাকলেও hit প্রায় শূন্য। দিনprivate, no-store; গতি আসুক edge-এর TLS আর গরম connection থেকে। - (গ) এই রূপে কার্যত না। URL-এ session id মানে প্রতিজনের আলাদা key, hit প্রায় শূন্য, আর ব্যক্তিগত কিছু ঢুকলে ঝুঁকি। Session id সরান cookie বা header-এ (key-তে নয়), পণ্যের JSON ব্যবহারকারী-নিরপেক্ষ রাখুন, ব্যক্তিগত ছাড় আলাদা private call-এ; তারপর দীর্ঘ মেয়াদ আর tag-purge.
- (ঘ) এই রূপে না। CDN সাধারণত POST-এর উত্তর cache করে না। খোঁজা শুধু পড়া আর idempotent, তাই
GET /api/v1/search?q=…,qমানসম্মত (ছোট হাতের অক্ষর, ফাঁকা ছাঁটা), ছোট TTL; long tail-এর খোঁজায় miss মেনে নিন (পর্ব ২৭)। - (ঙ) সঠিকতার জন্য না, দেখানোর জন্য অল্প। “মাত্র ১২টি বাকি” দেখাতে ১–২ সেকেন্ডের TTL আর collapsing origin-কে flash crowd থেকে বাঁচায়। কিন্তু কেনার মুহূর্তে stock-এর সিদ্ধান্ত origin-এর transaction-এ (পর্ব ৬০)।
- (ক) হ্যাঁ।
-
আগে অনুমান, তারপর চালান: অংশ ৬-এর Zipf simulator-এ cache-এর আকারের তালিকায় (ক)
12000যোগ করলে LRU-র hit ratio কত হবে বলে মনে করেন? আদর্শ সীমা আন্দাজ করতে সূত্র (k০.২ − ১) ÷ (N০.২ − ১) ব্যবহার করুন। (খ) পুরো catalogue, অর্থাৎ20000দিলে কি ১০০% hit হবে?(ক) আদর্শ সীমা: ১২,০০০০.২ ≈ ৬.৫৪, ২০,০০০০.২ ≈ ৭.২৫; (৬.৫৪ − ১) ÷ (৭.২৫ − ১) ≈ ০.৮৯ — প্রায় ৮৯%. ৬,০০০-এ LRU সীমার প্রায় ৯ বিন্দু নিচে ছিল, আর cache বড় হলে ফাঁক কমে; তাই ভালো অনুমান প্রায় ৮৪–৮৫%. চালিয়ে পাওয়া সারি:
12000 60.0% 84.2% 88.9% 15.8%(খ) না। পুরো catalogue ধরলে কিছুই সরে না, কিন্তু যে জিনিস এই সময়ে প্রথমবার চাওয়া হলো, তা আগে থেকে cache-এ থাকতে পারে না — একে বলে compulsory বা cold miss. Long tail-এ এমন একবার-চাওয়া জিনিস অনেক:
20000 100.0% 94.3% 100.0% 5.7%পুরো catalogue ধরেও hit ৯৪.৩%; বাকি ৫.৭% শুধু প্রথম-চাওয়া — catalogue যত বড় আর লেজ যত লম্বা, এই ভাগ তত বেশি। এটি কমাতে জায়গা নয়, লাগে আগে ভরা (warming বা push), বা চাহিদা বড় shield-এ জড়ো করা — যাতে একজনের প্রথম miss অন্যদের hit হয়।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমOrigin shield-সহ একটি CDN-এ নতুন একটি ছবির প্রথম অনুরোধ থেকে পরের অনুরোধ পর্যন্ত ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 🧭 DNS বা anycast ব্যবহারকারীকে কাছের edge-এ আনে
- 🔎 Edge cache key দিয়ে খোঁজে — কপি নেই, miss
- 🏬 Edge জিজ্ঞেস করে আঞ্চলিক shield-কে — সেখানেও miss
- 🏭 Shield origin থেকে একবারই ছবিটি আনে
- 📦 Shield আর edge কপি রাখে, ব্যবহারকারী ছবি পান
- ⚡ একই শহরের পরের অনুরোধ edge-এই hit
একটি CDN-এ সেকেন্ডে মোট ৫০,০০০ অনুরোধ আসে, edge-এর hit ratio ৯৮%, origin shield নেই। Origin-এ সেকেন্ডে কতটি অনুরোধ যায়?
- ৯৮০
- ১,০০০
- ২,৫০০
- ৪৯,০০০
মোট অনুরোধ অপরিবর্তিত রেখে একটি CDN-এর edge hit ratio ৯০% থেকে বেড়ে ৯৯% হলো (shield নেই)। Origin-এর ভারে কী ঘটে?
- ভার ৯% কমে
- ভার প্রায় ১০% কমে
- ভার ১০ গুণ কমে
- ভার অর্ধেক হয়
নামে বিষয়বস্তুর hash-যুক্ত একটি JavaScript ফাইলের (যেমন app.3f9a2c.js) জন্য কোন Cache-Control সবচেয়ে উপযুক্ত?
no-store- no-cache
public, max-age=31536000, immutableprivate, max-age=60
একটি CDN-এর ৪০টি edge, origin shield নেই। একটি জনপ্রিয় ছবি সব edge-এ সারাক্ষণ চাওয়া হয়, TTL ৬০ সেকেন্ড, আর প্রতিটি edge-এ মেয়াদ ফুরালে আলাদাভাবে miss হয়। Origin প্রতি মিনিটে মোটামুটি কতবার ছবিটি দেয় — আর একটি origin shield বসালে কতবার?
- ১ আর ১
- ৪০ আর ১
- ৪০ আর ৪০
- ৬০ আর ৪০
একটি খবরের লিংক Facebook থেকে ?fbclid=… আর বিজ্ঞাপন থেকে ?utm_source=… নিয়ে আসে, আর CDN পুরো query string cache key-তে রাখে। সবচেয়ে সম্ভাব্য ফল আর প্রতিকার কোনটি?
- কোনো সমস্যা নেই; TTL বাড়ালেই হবে
- ব্যবহারকারীর cookie-ও key-তে যোগ করা
- প্রতি মিনিটে পুরো cache purge করা
- প্রতিটি লিংক আলাদা key হয়ে hit ratio পড়ে; key-তে শুধু উত্তর-বদলানো param রাখা (allow-list)
CDN-এর edge function-এ চালানোর জন্য নিচের কোন কাজটি সবচেয়ে কম উপযুক্ত?
- পুরোনো URL থেকে নতুন URL-এ redirect
- স্বাক্ষর-করা token-এর মেয়াদ আর স্বাক্ষর যাচাই
- দূরের একটি region-এর ডেটাবেসে পরপর ৮টি query চালিয়ে পাতা বানানো
- A/B পরীক্ষার দল বেছে cache key-তে রাখা
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- CDN-এর চার লাভ: latency, origin offload, bandwidth-এর খরচ, ভিড় আর DDoS শোষণ; প্রথম অনুরোধ নয়, লাভ আসে hit থেকে।
- Origin-এর ভার = R × (১ − h); প্রতিটি বাড়তি “নয়” ১০ গুণ কম। Shield-এর গুণক [১ − (১ − p)E] ÷ Ep — জনপ্রিয় জিনিসে প্রায় ১/E, long tail-এ প্রায় ১।
- নামে hash-ওয়ালা asset এক বছর আর
immutable, deploy-এ আগে asset, পরে HTML; API ছোট TTL আরstale-while-revalidate/stale-if-error; ব্যক্তিগত উত্তরprivate/no-store. - Cache key-তে উত্তর যা বদলায় শুধু তা-ই — tracking param, cookie, User-Agent নয়; TTL ব্যবসার staleness-সীমা থেকে, পুরোনো ভাগ ≈ T ÷ ২P; বদলের খবর জানা থাকলে tag ধরে purge, লেখা দেখা যাওয়ার পরে।
- Edge-এ TLS, WAF, rate limit আর ছোট function — কিন্তু ডেটাবেস দূরে থাকলে edge-logic ধীর; origin শুধু CDN-এর জন্য খোলা; আর CDN-ও বিকল হয় — ৯৯% hit-এ CDN না থাকলে origin-এ ১০০ গুণ.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৮ · Overload সামলানো — queueing, backpressure, load shedding আর graceful degradationপরবর্তী পর্বCache-এর পরেও ভিড় সামলানোর বাকি অস্ত্র।
- পর্ব ১৬ · Consistent hashing — ring, virtual node আর rendezvous hashingআগের পর্বEdge-এর শত সার্ভারে কোন URL কোথায় — সার্ভার বদলেও cache খালি না করে।
- সিস্টেম ডিজাইন · কোর্স হোম৬৬টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- Computer Networks · Web cache, proxy আর CDN — কাছে রাখলেই দ্রুতগভীরে যানCache-Control, ETag আর 304, Vary, CDN-এর DNS redirection আর LRU-LFU — protocol ধরে ধরে।
- Computer Networks · প্রচলিত আক্রমণ আর প্রতিরক্ষা — DDoS, spoofing, MITM আর IDSপাশের ট্র্যাকDDoS-এর ধরন আর প্রতিরক্ষার স্তর — রক্ষকের চোখে।
- Operating Systems · Page replacement — FIFO, Optimal, LRU আর Clockপাশের ট্র্যাকজায়গা ফুরালে কী সরবে — cache-এর একই প্রশ্ন OS-এর ভেতরে।