সরাসরি পর্বে যান
পর্ব ৩৮ · ৬২-এর মধ্যে · মডিউল ৬

Web cache, proxy আর CDN — কাছে রাখলেই দ্রুত

Web Caching, Proxies & CDNs — Institutional Caches, HTTP Caching (Cache-Control, ETag, 304, Vary), Cache Busting, DNS Redirection & Edge Delivery

Taught in Bangla, this lesson explains caching close to users: browser, forward proxy, reverse proxy and CDN edge caches, the classic institutional cache numerical on access link traffic intensity and hit rate, HTTP caching with Cache-Control, ETag, conditional requests and 304, Vary, cache busting, and how CDNs route users with DNS redirection.

এই পর্বে যা শিখবেন

  • Cache কেন দরকার, আর browser, forward proxy, reverse proxy ও CDN edge-এর cache কোথায় আলাদা
  • প্রতিষ্ঠানের access link-এর অঙ্ক: traffic intensity, link বাড়ানো বনাম cache বসানো — সংখ্যা দিয়ে পুরো সমাধান
  • HTTP caching-এর নিয়ম: freshness (Cache-Control, Expires, heuristic), validation (ETag, Last-Modified, 304), Vary আর cache key
  • Cache busting-এর বাস্তব নকশা, কী কখনো cache করা যাবে না, আর cache-সংক্রান্ত নিরাপত্তা-ঝুঁকি থেকে বাঁচার উপায়
  • CDN-এর গঠন, DNS আর anycast দিয়ে কাছের edge বাছাই, cache-এর স্তর, purge, edge computing, আর hit ratio, TTFB, LRU ও LFU

১ · কেন cache — দূরের জিনিস কাছে রাখা

পর্ব ৩৬-এ দেখেছেন, ব্রাউজার একটি HTTP অনুরোধ পাঠায় আর সার্ভার উত্তর দেয়। কিন্তু একটি জনপ্রিয় সংবাদ-সাইটের লোগো দিনে কয়েক লাখ মানুষ চায়, আর প্রতিবার সেই একই কয়েক কিলোবাইট। প্রতিটি অনুরোধ যদি সমুদ্রের ওপারের মূল সার্ভারে যায়, তাহলে একই জিনিস লাখ বার সাগর পাড়ি দেয়। Cache-এর ধারণা সরল: একবার আনা জিনিসের একটি কপি কাছে রেখে দিন, পরের বার সেখান থেকেই দিন।

মহল্লার মুদি দোকানের কথা ভাবুন। দোকানি প্রতিবার ক্রেতা এলে কারওয়ান বাজারের পাইকারি আড়তে ছোটেন না। যে জিনিস বেশি বিক্রি হয় — চাল, ডাল, তেল, বিস্কুট — সেগুলো তাকে সাজিয়ে রাখেন। ক্রেতা পান সঙ্গে সঙ্গে (latency কম), আড়তে ভিড় কম (origin-এর ভার কম), দোকানির যাতায়াত খরচ কম (bandwidth খরচ কম)। কিন্তু দুটি প্রশ্ন থাকেই: তাকে কতদিনের পুরোনো মাল রাখা চলে (freshness), আর তাক ভরে গেলে কোনটা সরাবেন (eviction). আর একটি নিয়ম: কারও নামে আলাদা করে রাখা ওষুধের প্যাকেট সবার তাকে রাখা যায় না — ব্যক্তিগত জিনিস shared cache-এ নয়। এই পর্বের পুরোটা এই তিন প্রশ্নের উত্তর।

চারটি লাভ

১. Latency কমে

দূরের সার্ভারের RTT হয়তো ২০০ ms, কাছের cache-এর ১০ ms. প্রতিটি নতুন সংযোগে কয়েকটি RTT লাগে (পর্ব ৩৩), তাই দূরত্ব কমানোই সবচেয়ে বড় লাভ।

দূরত্ব = দেরি

২. Origin-এর ভার কমে

Cache যত অনুরোধ নিজে সামলায়, origin server তত কম কাজ করে। হঠাৎ ভিড়ে (ভর্তি পরীক্ষার ফল, খেলার স্কোর) সাইট টিকে থাকে।

ভিড় সামলানো

৩. Bandwidth-এর খরচ কমে

ISP বা প্রতিষ্ঠানকে বিদেশমুখী (international) bandwidth-এর জন্য টাকা দিতে হয়। একই ফাইল দেশের ভেতর থেকে দিলে সেই খরচ বাঁচে।

টাকা বাঁচে

৪. Access link-এর জ্যাম কমে

একটি অফিস বা বিশ্ববিদ্যালয়ের বাইরে যাওয়ার access link একটিমাত্র সরু পথ। Cache-এ hit হওয়া অনুরোধ এই পথে যায়ই না (অংশ ৩)।

সরু রাস্তা

Cache কাজ করে কারণ ওয়েবের চাহিদা সমান ভাগে ছড়ানো নয়। অল্প কিছু জিনিস (জনপ্রিয় পাতা, লোগো, JavaScript লাইব্রেরি, ভাইরাল ভিডিও) বারবার চাওয়া হয়, আর বিপুল সংখ্যক জিনিস কদাচিৎ। গবেষণায় ওয়েবের জনপ্রিয়তাকে প্রায়ই Zipf-ধাঁচের বণ্টন দিয়ে আন্দাজ করা হয় — প্রথম কয়েকটি জিনিস মোট অনুরোধের বড় অংশ নেয়। তাই ছোট একটি cache-ও অনেক অনুরোধ সামলাতে পারে। একে বলে লোকালিটি (locality): যা এইমাত্র চাওয়া হয়েছে বা যা জনপ্রিয়, তা আবার চাওয়ার সম্ভাবনা বেশি — OS-এর page cache আর CPU cache-ও এই একই নীতিতে চলে।

মূল ধারণা

Cache-এর প্রতিটি সিদ্ধান্ত দুই প্রশ্নের দোলাচল: গতি (কাছের কপি দেওয়া) বনাম সঠিকতা (কপিটি কি এখনো চলতি?)। HTTP-র caching নিয়ম পুরোটা এই দোলাচল সামলানোর ভাষা — সার্ভার জানায় কতক্ষণ বিশ্বাস করা চলে, আর তারপর কীভাবে সস্তায় যাচাই করা যায়।

২ · Cache কোথায় কোথায় থাকে — ব্রাউজার থেকে CDN edge

একটি অনুরোধ ব্রাউজার থেকে origin পর্যন্ত যেতে পথে কয়েকটি cache পেরোতে পারে। কে কার পক্ষে কাজ করে, আর কে কার তথ্য দেখতে পায় — এটাই তাদের আলাদা করে।

Cacheকোথায়, কার পক্ষেধরনউদাহরণ
Browser cacheআপনার যন্ত্রে, শুধু আপনার জন্যprivateChrome, Firefox-এর disk/memory cache; মোবাইল অ্যাপের HTTP cache
Forward proxy / প্রতিষ্ঠানের cacheঅফিস, বিশ্ববিদ্যালয় বা ISP-র নেটওয়ার্কে, ক্লায়েন্টদের পক্ষেsharedSquid-এর মতো proxy server
Reverse proxy / gateway cacheসাইটের data center-এ, origin-এর ঠিক সামনে, সার্ভারের পক্ষেsharedVarnish, nginx-এর proxy cache
CDN edgeবহু শহরে, ISP-র ভেতরে বা IXP-তে; সাইটের মালিকের ভাড়া করাsharedবাণিজ্যিক CDN, বড় কনটেন্ট প্রতিষ্ঠানের নিজস্ব CDN

Forward proxy ক্লায়েন্টের দিকে বসে: প্রতিষ্ঠানের সব কম্পিউটার তার মাধ্যমে বাইরে যায়, আর সে জনপ্রিয় জিনিস জমিয়ে রাখে। ব্রাউজারে proxy-র ঠিকানা লেখা থাকতে পারে, বা নেটওয়ার্ক নিজেই ট্রাফিক ঘুরিয়ে proxy-তে পাঠাতে পারে (transparent proxy). Reverse proxy উল্টো দিকে: ক্লায়েন্ট ভাবে এটিই সার্ভার; এটি পেছনের সার্ভারগুলোর হয়ে উত্তর দেয়, cache রাখে, load ভাগ করে, কখনো TLS-ও সামলায়। CDN-কে বলা যায় বিশ্বজুড়ে ছড়ানো reverse proxy-র নেটওয়ার্ক।

HTTPS forward proxy-র cache প্রায় শেষ করে দিয়েছে। HTTPS-এ ব্রাউজার আর সার্ভারের মাঝে সব এনক্রিপ্টেড; পথের proxy URL বা বিষয়বস্তু দেখতে পায় না, তাই cache-ও করতে পারে না (সে শুধু CONNECT দিয়ে একটি সুড়ঙ্গ চালায়)। কিছু প্রতিষ্ঠান নিজস্ব root certificate বসিয়ে TLS খুলে দেখে (TLS inspection) — এটি গোপনীয়তা আর নিরাপত্তার বড় আপস, পর্ব ৪৮-এ আলোচনা। আজ shared caching-এর বড় অংশ তাই হয় সাইটের মালিকের নিয়ন্ত্রণে থাকা reverse proxy আর CDN-এ, যাদের কাছে সাইটের নিজের certificate থাকে।

একটি অনুরোধের যাত্রা — তিন স্তরের cache

রাজশাহীর এক পাঠক shop.example.com-এর লোগো চাইছেন। সাইটটি একটি CDN ব্যবহার করে, যার একটি edge server ঢাকায়; origin ইউরোপে। সংখ্যাগুলো উদাহরণ: ব্রাউজার থেকে edge-এর RTT ১০ ms, edge থেকে origin-এর RTT ১৮০ ms (বাস্তবে পথভেদে অনেক বদলায়)। সংযোগ আর TLS-এর সময় এখানে বাদ রাখা হলো।

  1. ব্রাউজার cacheprivateরাজশাহী
  2. CDN edgesharedঢাকা, RTT 10 ms
  3. Originমূল কপিইউরোপ, RTT 180 ms
প্রতিটি ধাপে কী ঘটছে
ঘটনা
ব্রাউজার cache
Edge cache
Origin
সময় (আনুমানিক)
  1. প্রথম স্তর: ব্রাউজার। পাঠক ১ সাইটে প্রথমবার এসেছেন, তাই তাঁর ব্রাউজারের private cache-এ লোগোর কোনো কপি নেই — MISS. অনুরোধ নেটওয়ার্কে যাবে।
  2. দ্বিতীয় স্তর: CDN edge. DNS-এর কৌশলে (অংশ ৭) ব্রাউজার পৌঁছেছে ঢাকার edge-এ। Edge তার cache key — method আর পুরো URL — দিয়ে খুঁজল; কপি নেই, MISS.
  3. তৃতীয় স্তর: origin. Edge নিজে origin-এর কাছে একই অনুরোধ পাঠাল। এই পথের RTT ১৮০ ms — সবচেয়ে দামি অংশ। Origin-এর কাছে এটি অনেক অনুরোধের মধ্যে একটি কাজ।
  4. উত্তর ফিরে আসার পথে দুই জায়গায় জমা। Origin পাঠাল 200 OK, সাথে Cache-Control: public, max-age=86400 (২৪ ঘণ্টা)। Edge কপি রাখল, ব্রাউজারও রাখল। পাঠক ১-এর মোট সময় প্রায় ১০ + ১৮০ = ১৯০ ms.
  5. পাঠক ২ — edge-এ HIT. খুলনার এক পাঠক একই লোগো চাইলেন। তাঁর ব্রাউজারে কপি নেই, কিন্তু edge-এ আছে — shared cache-এর সুবিধা। Edge নিজেই দিল: প্রায় ১০ ms, origin কিছুই জানল না।
  6. পাঠক ১ আবার — ব্রাউজারে HIT. ১০ মিনিট পরে একই পাতা। কপির বয়স ৬০০ সেকেন্ড, max-age ৮৬,৪০০ — fresh. ব্রাউজার নেটওয়ার্কে কোনো অনুরোধই পাঠাল না। সবচেয়ে দ্রুত অনুরোধ হলো যেটি পাঠাতেই হয় না।

৩ · প্রতিষ্ঠানের access link — link বাড়াবেন, না cache বসাবেন?

এটি বিশ্ববিদ্যালয়ের Computer Networks পরীক্ষা আর চাকরির লিখিত পরীক্ষায় বারবার আসা একটি ক্লাসিক অঙ্ক (Kurose-Ross-এর পাঠ্যবইয়ে এর একটি রূপ আছে; নিচের সংখ্যাগুলো আমাদের নিজের)। এটি পর্ব ৫-এর traffic intensity আর queuing delay-এর সরাসরি প্রয়োগ।

সমস্যা

একটি বিশ্ববিদ্যালয়ের ভেতরের নেটওয়ার্ক (LAN) 100 Mbps. বাইরে যাওয়ার access link মাত্র 10 Mbps. ধরে নিন:

  • প্রতিটি ওয়েব-বস্তুর (object) গড় আকার L = 0.8 Mbit (১০০ কিলোবাইট)।
  • পুরো ক্যাম্পাস থেকে গড়ে a = 12টি অনুরোধ প্রতি সেকেন্ডে, প্রতিটির উত্তর access link দিয়ে ভেতরে আসে।
  • Access link-এর বাইরের router থেকে origin পর্যন্ত গিয়ে উত্তর ফিরতে গড়ে ১ সেকেন্ড (“Internet delay”, দূরের সার্ভার আর পথের সব delay মিলিয়ে ধরা একটি মান)।
  • LAN-এর ভেতরের delay প্রায় ০.০১ সেকেন্ড (0.8 Mbit ÷ 100 Mbps = 8 ms transmission, সাথে সামান্য বাকি)।

Access link-এ queuing delay কত, তা বের করতে একটি মডেল লাগে। পর্ব ৫-এ দেখেছেন, এলোমেলো আগমনে গড় অপেক্ষা (1 − ρ) হর-এ রাখে। এখানে আমরা সবচেয়ে প্রচলিত সরল মডেল M/M/1 নেব: access link-এ একটি বস্তুর গড় মোট সময় (অপেক্ষা + নিজের transmission):

daccess = (L/R) ÷ (1 − ρ), যেখানে ρ = L·a / R

এটি একটি আনুমানিক মডেল — বাস্তবের আগমন আর বস্তুর আকার এত সরল নয়। পাঠ্যবই অনেক সময় মডেল ছাড়াই শুধু বলে “ρ ১-এর কাছে হলে delay বিশাল, ০.৬-এর মতো হলে কয়েক দশ মিলিসেকেন্ড” — পরীক্ষায় প্রশ্ন যেভাবে চায় সেভাবে লিখবেন, কিন্তু কোন মডেল ধরলেন তা স্পষ্ট লিখবেন।

  1. এখনকার অবস্থা10 Mbpsকোনো cache নেই
  2. বিকল্প A100 Mbpslink ১০ গুণ
  3. বিকল্প Bhit = 0.4ক্যাম্পাসে cache
হিসাব
কী হিসাব
সূত্র
মান
  1. Traffic intensity প্রায় ১. প্রতি সেকেন্ডে আসছে ১২ × 0.8 = 9.6 Mbit, link পারে 10 Mbit. ρ = ০.৯৬ — রাস্তায় গাড়ির সংখ্যা প্রায় রাস্তার ধারণক্ষমতার সমান। পর্ব ৫-এর বক্ররেখা মনে করুন: এই অঞ্চলে delay খাড়া ওপরে ওঠে।
  2. Delay ৩ সেকেন্ড ছাড়িয়ে। একটি বস্তুর transmission L/R = 0.8/10 = ০.০৮ সেকেন্ড। M/M/1 মডেলে access delay = ০.০৮ ÷ (১ − ০.৯৬) = ০.০৮ ÷ ০.০৪ = ২ সেকেন্ড. সাথে LAN ০.০১ আর Internet ১ — প্রতিটি বস্তু পেতে গড়ে ৩.০১ সেকেন্ড. আর অনুরোধ যদি একটু বাড়ে (১২.৫/সেকেন্ড), ρ = ১, আর তাত্ত্বিক delay অসীম।
  3. বিকল্প A: link ১০ গুণ। ρ = 9.6/100 = ০.০৯৬; L/R = ০.০০৮ সেকেন্ড; access = ০.০০৮ ÷ ০.৯০৪ ≈ ০.০০৯ সেকেন্ড — প্রায় শূন্য। মোট ≈ ০.০১ + ০.০০৯ + ১ = ১.০১৯ সেকেন্ড. দারুণ উন্নতি, কিন্তু খরচ বড়: প্রতি মাসে দশ গুণ bandwidth-এর বিল। আর Internet delay-এর ১ সেকেন্ড একটুও কমেনি।
  4. বিকল্প B: cache, hit rate ০.৪. ১০০টি অনুরোধের ৪০টি ক্যাম্পাসের cache থেকেই মেটে, link পেরোয় শুধু ৬০টি। তাই ρ = ০.৬ × ০.৯৬ = ০.৫৭৬. Access delay = ০.০৮ ÷ (১ − ০.৫৭৬) = ০.০৮ ÷ ০.৪২৪ ≈ ০.১৮৯ সেকেন্ড.
  5. বিকল্প B-র গড় delay. Hit হলে শুধু LAN: ০.০১. Miss হলে ০.০১ + ০.১৮৯ + ১ = ১.১৯৯. ভারযুক্ত গড়: ০.৪ × ০.০১ + ০.৬ × ১.১৯৯ = ০.০০৪ + ০.৭১৯ ≈ ০.৭২৩ সেকেন্ড. পাঠ্যবইয়ের সংক্ষিপ্ত রূপে: “০.৪ × (LAN delay) + ০.৬ × (Internet delay + সামান্য access delay)”.
  6. সিদ্ধান্ত। Cache (≈ ০.৭২ সেকেন্ড) link বাড়ানোর (≈ ১.০২ সেকেন্ড) চেয়েও দ্রুত — কারণ hit-গুলো Internet delay-এর পুরো ১ সেকেন্ডই এড়ায় — আর খরচ এককালীন একটি সার্ভার। শর্ত একটাই: hit rate সত্যিই ০.৪-এর কাছাকাছি হতে হবে, যা HTTPS-এর যুগে প্রতিষ্ঠানের forward proxy-তে কঠিন (অংশ ২)। আজ এই হিসাবটাই ISP আর CDN-এর edge cache-এর অর্থনীতি।

নিচের ক্যালকুলেটর একই মডেলে কয়েকটি পরিস্থিতি একসাথে হিসাব করে — hit rate ০, ০.৪, ০.৬, আর অনুরোধ সামান্য বাড়লে কী হয়।

// Institutional cache problem. Access link R bit/s, objects of L bits, a requests/s.
// Model (an approximation, M/M/1): access-link delay = (L/R) / (1 - rho), rho = L*a/R.
// Only cache MISSES cross the access link, so rho is multiplied by (1 - hit).
const L = 0.8e6;            // object size: 0.8 Mbit (100 KB)
const lanDelay = 0.01;      // s, inside the 100 Mbps LAN
const internetDelay = 1.0;  // s, outside router -> origin -> back (assumed)

function scenario(name, R, a, hit) {
  const rho = (1 - hit) * L * a / R;
  const access = rho < 1 ? (L / R) / (1 - rho) : Infinity;
  const avg = hit * lanDelay + (1 - hit) * (lanDelay + access + internetDelay);
  const f = (x) => isFinite(x) ? x.toFixed(3) + " s" : "infinite";
  console.log(name.padEnd(27) + String(hit).padEnd(5) + rho.toFixed(3).padEnd(8) +
              f(access).padEnd(11) + f(avg));
}
console.log("scenario                   hit  rho     access     avg delay");
scenario("10 Mbps, no cache", 10e6, 12, 0);
scenario("10 Mbps, no cache, 12.5/s", 10e6, 12.5, 0);
scenario("A: upgrade to 100 Mbps", 100e6, 12, 0);
scenario("B: cache, hit 0.4", 10e6, 12, 0.4);
scenario("B: cache, hit 0.6", 10e6, 12, 0.6);

আউটপুট:

scenario                   hit  rho     access     avg delay
10 Mbps, no cache          0    0.960   2.000 s    3.010 s
10 Mbps, no cache, 12.5/s  0    1.000   infinite   infinite
A: upgrade to 100 Mbps     0    0.096   0.009 s    1.019 s
B: cache, hit 0.4          0.4  0.576   0.189 s    0.723 s
B: cache, hit 0.6          0.6  0.384   0.130 s    0.462 s

প্রথম সারি stepper-এর ৩.০১ সেকেন্ড মেলায়। দ্বিতীয় সারিটি সতর্কবাণী: অনুরোধ মাত্র ৪% বাড়লে (১২ থেকে ১২.৫) ρ = ১, আর সরল মডেলে delay অসীম — বাস্তবে buffer ভরে packet loss আর TCP-র পিছু হটা (পর্ব ৩২) শুরু হয়। বিকল্প A দেয় ১.০১৯, বিকল্প B ০.৭২৩ সেকেন্ড; hit rate ০.৬ হলে আরও কমে ০.৪৬২. লক্ষ করুন, B-তে access delay (০.১৮৯) A-র চেয়ে বেশি, তবু গড় কম — কারণ ৪০% অনুরোধ ১ সেকেন্ডের Internet delay পুরোপুরি এড়ায়। internetDelay বদলে ০.২ করে দেখুন: তখন A আর B-র পার্থক্য কেমন দাঁড়ায়?

৪ · HTTP caching-এর নিয়ম ১ — freshness: কতক্ষণ বিশ্বাস করা যায়

HTTP-র caching নিয়ম এখন লেখা আছে RFC 9111-এ (জুন ২০২২, আগের RFC 7234-এর জায়গায়), আর শর্তযুক্ত অনুরোধ ও ETag-এর অর্থ RFC 9110-এ। মূল কাঠামো দুই ভাগে: freshness — কোনো প্রশ্ন না করে কতক্ষণ কপি ব্যবহার করা যায়; আর validation — মেয়াদ ফুরালে সস্তায় কীভাবে জিজ্ঞেস করা যায় “বদলেছে কি?”। একটি কপি fresh যতক্ষণ তার বয়স (age) freshness lifetime-এর কম; তারপর সে stale. Stale মানে বাতিল নয় — মানে ব্যবহারের আগে সাধারণত যাচাই লাগবে।

Cache-Control — সার্ভারের নির্দেশনা

সার্ভার response-এর header-এ Cache-Control দিয়ে নির্দেশনা (directive) দেয়। সবচেয়ে দরকারিগুলো:

Directiveঅর্থকোথায় ব্যবহার
max-age=NN সেকেন্ড পর্যন্ত fresh; সব cache-এর জন্যপ্রায় সব cache-যোগ্য response
s-maxage=Nশুধু shared cache (proxy, CDN)-এর জন্য freshness; সেখানে max-age-এর জায়গা নেয়“ব্রাউজারে ১ মিনিট, CDN-এ ১ ঘণ্টা” ধরনের নীতি
no-cacheজমা রাখা যাবে, কিন্তু প্রতিবার ব্যবহারের আগে origin-এর কাছে যাচাইHTML পাতা, যা সবসময় চলতি হওয়া দরকার কিন্তু 304-এর সুবিধা চাই
no-storeকোনো cache জমাই রাখবে নাব্যাংকের বিবরণী, ব্যক্তিগত তথ্য, এককালীন টোকেন
privateশুধু private cache (ব্রাউজার) রাখতে পারবে; shared cache নয়লগইন-করা ব্যবহারকারীর ড্যাশবোর্ড, যা তাঁর ব্রাউজারে রাখা চলে
publicShared cache-ও রাখতে পারবে, এমনকি যেখানে সাধারণত রাখা হতো না (যেমন Authorization-সহ অনুরোধ)প্রকাশ্য স্থির ফাইল; সতর্কভাবে
must-revalidateStale হওয়ার পর যাচাই ছাড়া কখনো ব্যবহার নয়, origin-এ পৌঁছানো না গেলেও নয় (তখন ত্রুটি)যেখানে পুরোনো তথ্য দেখানো ক্ষতিকর
immutableFresh থাকার সময়ে বিষয়বস্তু বদলাবে না — reload-এও যাচাই লাগবে না (RFC 8246)নামে hash-যুক্ত ফাইল (অংশ ৬)
stale-while-revalidate=NStale হওয়ার পর আরও N সেকেন্ড পুরোনো কপি দেওয়া যায়, পেছনে নতুন কপি যাচাই হয় (RFC 5861)খবরের তালিকা, যেখানে কয়েক সেকেন্ড পুরোনো চলে কিন্তু দেরি চলে না

সবচেয়ে প্রচলিত বিভ্রান্তি: no-cache মানে “cache নিষেধ” নয়। এর মানে: জমা রাখা চলবে, কিন্তু প্রতিবার ব্যবহারের আগে যাচাই করে নিতে হবে। জমা রাখা একেবারে নিষেধ করতে চাইলে no-store. ব্যক্তিগত তথ্যের পাতায় ভুল করে শুধু no-cache দিলে কপিটি disk-এ থেকে যেতে পারে — যেমন একটি শেয়ার করা কম্পিউটারে। আর পুরোনো HTTP/1.0-এর Pragma: no-cache আজ শুধু পেছনের সামঞ্জস্যের জন্য; নির্ভর করার জিনিস নয়।

Expires, Age আর heuristic freshness

Expires হলো HTTP/1.0 যুগের header: একটি নির্দিষ্ট তারিখ-সময়, যেমন Expires: Thu, 01 Jan 2026 00:00:00 GMT. এর সমস্যা — ক্লায়েন্ট আর সার্ভারের ঘড়ি না মিললে হিসাব ভুল হয়। তাই max-age থাকলে নিয়ম অনুযায়ী সেটিই প্রাধান্য পায়, Expires উপেক্ষিত হয়। Shared cache উত্তরের সাথে Age header যোগ করে, যাতে জানা যায় কপিটি সেখানে কত সেকেন্ড ধরে আছে: CDN থেকে max-age=600 আর Age: 100 এলে ব্রাউজারের কাছে কপিটি আর ৫০০ সেকেন্ড fresh.

সার্ভার কোনো মেয়াদ না দিলে? RFC 9111 cache-কে নিজে আন্দাজ করার (heuristic freshness) অনুমতি দেয়, যদি response-টি এমনিতে cache-যোগ্য হয় (যেমন 200, 301, 404). একটি প্রচলিত কৌশল RFC-তেই উদাহরণ হিসেবে আছে: Last-Modified-এর পর থেকে এখন পর্যন্ত যত সময় গেছে তার একটি ভগ্নাংশ — সাধারণত প্রায় ১০% — freshness ধরা। ফাইলটি ১০ দিন আগে শেষ বদলালে প্রায় ১ দিন। ব্রাউজারভেদে বাস্তব আচরণ আলাদা হতে পারে, তাই নিজের সাইটে কখনো এই আন্দাজের ওপর ভরসা করবেন না; স্পষ্ট Cache-Control দিন।

৫ · নিয়ম ২ — validation: ETag, Last-Modified আর 304

Stale কপি ফেলে দিয়ে আবার পুরো ফাইল আনা অপচয় — বেশিরভাগ সময় ফাইলটি বদলায়নি। তাই cache একটি conditional request পাঠায়: “আমার কাছে এই সংস্করণ আছে; বদলালে নতুনটি দিন, না বদলালে শুধু জানান।” দুই রকম চিহ্ন দিয়ে এটি হয়:

  • ETag → If-None-Match: সার্ভার প্রতিটি সংস্করণে একটি চিহ্ন দেয়, যেমন ETag: "33a7" (প্রায়ই বিষয়বস্তুর hash বা সংস্করণ নম্বর)। Cache পরে পাঠায় If-None-Match: "33a7". চিহ্ন মিললে উত্তর 304 Not Modified, body ছাড়া।
  • Last-Modified → If-Modified-Since: সময় দিয়ে তুলনা। সেকেন্ডের চেয়ে সূক্ষ্ম পার্থক্য ধরতে পারে না, আর একই ফাইল কয়েকটি সার্ভারে ভিন্ন সময়ে লেখা হলে ভুল করতে পারে। দুটো একসাথে এলে সার্ভার If-None-Match-কে প্রাধান্য দেয়।

ETag দুই রকম: strong ("33a7") — বাইট বাইট একই; weak (W/"33a7") — অর্থে একই, বাইটে সামান্য আলাদা হতে পারে (যেমন ভিন্ন compression). আর একটি সতর্কতা: অনেক সার্ভারের পুরোনো সংস্করণে ETag-এ inode বা সার্ভার-নির্দিষ্ট তথ্য থাকত, ফলে load balancer-এর পেছনের দুই সার্ভার একই ফাইলের দুই আলাদা ETag দিত আর 304 প্রায় কখনো আসত না। ETag বিষয়বস্তু থেকে বানানোই নিরাপদ।

  1. ব্রাউজার cachestyle.css24 KB
  2. If-None-Match"33a7"বদলেছে কি?
  3. সার্ভার304 / 200তুলনা করে উত্তর
সময়রেখা (max-age = 300 সেকেন্ড)
সময়
অনুরোধ
উত্তর
Cache-এর অবস্থা
  1. প্রথম আনা। সার্ভার পাঠাল পুরো ২৪ KB, সাথে ETag: "33a7" আর Cache-Control: max-age=300. ব্রাউজার কপি রাখল, সাথে এই দুই তথ্য আর আনার সময়।
  2. ২ মিনিট পরে — fresh. কপির বয়স ১২০ সেকেন্ড, ৩০০-এর কম। ব্রাউজার কোনো প্রশ্ন না করে কপি ব্যবহার করল; নেটওয়ার্কে একটি বাইটও যায়নি।
  3. ৪০০ সেকেন্ডে — stale. বয়স ৪০০ > ৩০০. কপিটি ফেলে না দিয়ে ব্রাউজার শর্তযুক্ত অনুরোধ পাঠাল: If-None-Match: "33a7".
  4. সার্ভারের তুলনা — মিলেছে। চলতি সংস্করণের ETag-ও "33a7". সার্ভার পাঠাল 304 Not Modified — body নেই, শুধু কয়েকশো বাইটের header, সাথে নতুন max-age=300. ২৪ KB-এর জায়গায় প্রায় ২০০ বাইট — তবে এক RTT লেগেছেই।
  5. Cache নবায়ন। ব্রাউজার জমানো কপির header হালনাগাদ করল, বয়স আবার শূন্য — t = ৭০০ পর্যন্ত fresh — আর পাতায় সেই পুরোনো body-ই দেখাল।
  6. ৮০০ সেকেন্ডে — এবার বদলেছে। ইতিমধ্যে ডেভেলপার CSS বদলেছেন; চলতি ETag "9c1e". চিহ্ন মেলে না, তাই সার্ভার পূর্ণ 200 OK পাঠাল নতুন ২৫ KB-সহ। Validation কখনো পুরোনো জিনিস দেখায় না; শুধু অপ্রয়োজনীয় পুনঃপ্রেরণ বাঁচায়।

এবার একটি ছোট cache simulator. এতে তিন রকম URL: একটি লোগো (max-age=60, origin-এ ১৩০ সেকেন্ডে বদলায়), একটি নামে-hash-যুক্ত JavaScript ফাইল (এক বছর, immutable), আর একটি ব্যক্তিগত অ্যাকাউন্ট-পাতা (no-store).

// A tiny HTTP cache (like a browser or proxy) in front of an origin server.
// Origin: what the server would send for each URL; logo.png changes at t = 130 s.
function origin(url, t) {
  if (url === "/account") return { etag: '"u7"', cc: "no-store" };
  if (url === "/app.3f9a2c.js") return { etag: '"a1"', cc: "max-age=31536000, immutable" };
  return { etag: t < 130 ? '"v1"' : '"v2"', cc: "max-age=60" };
}
const cache = new Map();                    // url -> { etag, maxAge, storedAt }
function get(url, t) {
  const e = cache.get(url);
  if (e && t - e.storedAt < e.maxAge)
    return `HIT fresh (age ${t - e.storedAt}s < max-age ${e.maxAge}s), 0 bytes from origin`;
  const r = origin(url, t);
  if (r.cc.includes("no-store")) return "BYPASS: no-store -> 200 from origin, not stored";
  const maxAge = Number(r.cc.match(/max-age=(\d+)/)[1]);
  if (e) {                                  // stale: ask "has it changed?"
    const msg = `STALE (age ${t - e.storedAt}s) -> If-None-Match: ${e.etag} -> `;
    if (r.etag === e.etag) { e.storedAt = t; return msg + "304 Not Modified, reuse body"; }
    cache.set(url, { etag: r.etag, maxAge, storedAt: t });
    return msg + `200 OK, new body ETag ${r.etag}`;
  }
  cache.set(url, { etag: r.etag, maxAge, storedAt: t });
  return `MISS -> 200 OK, stored ETag ${r.etag}, ${r.cc}`;
}
const trace = [[0, "/logo.png"], [5, "/app.3f9a2c.js"], [30, "/logo.png"],
  [90, "/logo.png"], [120, "/logo.png"], [200, "/logo.png"], [210, "/account"],
  [215, "/app.3f9a2c.js"], [220, "/account"]];
for (const [t, url] of trace)
  console.log(`t=${String(t).padStart(3)}s GET ${url.padEnd(15)} ${get(url, t)}`);

আউটপুট:

t=  0s GET /logo.png       MISS -> 200 OK, stored ETag "v1", max-age=60
t=  5s GET /app.3f9a2c.js  MISS -> 200 OK, stored ETag "a1", max-age=31536000, immutable
t= 30s GET /logo.png       HIT fresh (age 30s < max-age 60s), 0 bytes from origin
t= 90s GET /logo.png       STALE (age 90s) -> If-None-Match: "v1" -> 304 Not Modified, reuse body
t=120s GET /logo.png       HIT fresh (age 30s < max-age 60s), 0 bytes from origin
t=200s GET /logo.png       STALE (age 110s) -> If-None-Match: "v1" -> 200 OK, new body ETag "v2"
t=210s GET /account        BYPASS: no-store -> 200 from origin, not stored
t=215s GET /app.3f9a2c.js  HIT fresh (age 210s < max-age 31536000s), 0 bytes from origin
t=220s GET /account        BYPASS: no-store -> 200 from origin, not stored

লোগোর জীবনচক্র পুরোটা দেখা যাচ্ছে: ০-তে MISS, ৩০-এ fresh HIT, ৯০-এ stale হয়ে 304 (কপি নবায়ন, তাই storedAt = ৯০), ১২০-এ আবার HIT — বয়স মাত্র ৩০, কারণ ৯০-এ ঘড়ি নতুন করে শুরু হয়েছে। ১৩০-এ origin-এ লোগো বদলেছে; ২০০-তে যাচাইয়ে চিহ্ন মেলেনি, তাই নতুন body "v2". /account দুবারই origin থেকে, কখনো জমা হয়নি। আর hash-যুক্ত JS ফাইল ২১০ সেকেন্ড পরেও কোনো প্রশ্ন ছাড়াই HIT. একটি সূক্ষ্ম কথা: ১৩০ থেকে ২০০-র মাঝে কেউ লোগো চাইলে, যদি তখন কপি fresh থাকত, পুরোনো "v1"-ই পেত — max-age মানে এতটুকু পুরোনো দেখানোর অনুমতি। সত্যিকারের ব্রাউজার আরও কিছু করে (Age header, heuristic, reload-এর আচরণ), simulator শুধু মূল নিয়মটুকু দেখায়।

একই কাজ সত্যিকারের socket-এ — C-তে origin আর caching proxy

এবার Linux-এ দুটি প্রক্রিয়া: fork করা একটি ছোট origin সার্ভার (127.0.0.1-এর একটি খালি port-এ), আর parent প্রক্রিয়া একটি caching proxy-র ভূমিকায়। প্রথম অনুরোধে proxy origin থেকে আনে আর জমায়; দ্বিতীয়টি memory থেকে দেয়; তৃতীয়টিতে ধরে নেওয়া হয়েছে max-age পেরিয়েছে, তাই If-None-Match পাঠায়। Socket-এর খুঁটিনাটি পর্ব ৪১-এ।

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/wait.h>
/* An origin server (child) and a caching proxy (parent) on 127.0.0.1 */
static struct sockaddr_in a = { .sin_family = AF_INET };
static void fetch(const char *req, char *resp) {      /* one request, one connection */
    int s = socket(AF_INET, SOCK_STREAM, 0), n = -1;
    if (connect(s, (struct sockaddr *)&a, sizeof a) == 0 && write(s, req, strlen(req)) > 0)
        n = read(s, resp, 511);
    close(s); resp[n > 0 ? n : 0] = 0;
}
int main(void) {
    char buf[512], body[64], etag[16], req[128]; socklen_t len = sizeof a;
    a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); int ls = socket(AF_INET, SOCK_STREAM, 0);
    bind(ls, (struct sockaddr *)&a, sizeof a); listen(ls, 4); fflush(stdout);
    getsockname(ls, (struct sockaddr *)&a, &len);       /* kernel chose a free port */
    if (fork() == 0) {                                   /* ORIGIN: 2 connections */
        for (int i = 0; i < 2; i++) {
            int c = accept(ls, NULL, NULL), n = read(c, buf, 511); buf[n > 0 ? n : 0] = 0;
            const char *r = strstr(buf, "If-None-Match: \"v1\"") ? "HTTP/1.1 304 Not Modified\r\n\r\n"
                : "HTTP/1.1 200 OK\r\nETag: \"v1\"\r\nCache-Control: max-age=60\r\n\r\nHello from origin";
            n = (int)write(c, r, strlen(r)); close(c); if (n < 0) return 1;
        } return 0;
    }
    close(ls); fetch("GET /hello HTTP/1.1\r\nHost: localhost\r\n\r\n", buf);
    sscanf(strstr(buf, "ETag: ") + 6, "%15s", etag); strcpy(body, strstr(buf, "\r\n\r\n") + 4);
    printf("req 1: MISS  -> origin: %.15s, stored ETag %s, body \"%s\"\n", buf, etag, body);
    printf("req 2: HIT   -> \"%s\" served from memory, origin not contacted\n", body);
    snprintf(req, sizeof req, "GET /hello HTTP/1.1\r\nHost: localhost\r\nIf-None-Match: %s\r\n\r\n", etag);
    fetch(req, buf);                                     /* req 3: assume max-age passed */
    printf("req 3: STALE -> If-None-Match %s, origin: %.25s, served stored \"%s\"\n", etag, buf, body);
    wait(NULL); return 0;
}

আউটপুট:

req 1: MISS  -> origin: HTTP/1.1 200 OK, stored ETag "v1", body "Hello from origin"
req 2: HIT   -> "Hello from origin" served from memory, origin not contacted
req 3: STALE -> If-None-Match "v1", origin: HTTP/1.1 304 Not Modified, served stored "Hello from origin"

তিনটি অনুরোধ, কিন্তু origin সংযোগ পেয়েছে মাত্র দুটি — child প্রক্রিয়ার for লুপ দুবার accept করেই শেষ, আর তাতেই সব কাজ হয়েছে। প্রথম লাইনে proxy origin-এর 200 OK থেকে ETag "v1" আর body আলাদা করে memory-তে রেখেছে। দ্বিতীয় লাইনে কোনো socket খোলাই হয়নি — এটি fresh HIT. তৃতীয় লাইনে proxy শর্তযুক্ত অনুরোধ পাঠিয়েছে; origin অনুরোধে If-None-Match: "v1" দেখে body ছাড়া শুধু 304 Not Modified ফেরত দিয়েছে, আর proxy নিজের জমানো body-ই দিয়েছে। Port-এর নম্বর ছাপা হয়নি, কারণ bind-এ port 0 দেওয়ায় kernel প্রতিবার খালি একটি port নিজে বেছে দেয়। সত্যিকারের proxy (Squid, Varnish, nginx) এর ওপর যোগ করে সময় আর max-age-এর হিসাব, অনেক URL, Vary, একসাথে অনেক ক্লায়েন্ট (পর্ব ৪৩–৪৪) আর ত্রুটি সামলানো।

৬ · Cache key, Vary, cache busting — আর যা কখনো cache করা যাবে না

Cache key আর Vary

Cache জমানো কপি খোঁজে একটি key দিয়ে — সাধারণত method (প্রায় সবসময় GET) আর পুরো URL, query string-সহ। কিন্তু একই URL-এর উত্তর কখনো request header-এর ওপর নির্ভর করে: যে ব্রাউজার Accept-Encoding: br পাঠায় সে Brotli-তে চাপা ফাইল পায়, যে পাঠায় না সে চাপহীন ফাইল। সার্ভার তখন response-এ লেখে Vary: Accept-Encoding — মানে “এই header-ও key-র অংশ”। না লিখলে একটি cache চাপা ফাইল জমিয়ে এমন ক্লায়েন্টকে দিতে পারে যে খুলতেই পারে না।

Vary-এর ফাঁদ। Accept-Encoding-এর মান ব্রাউজারভেদে নানা রকম (gzip, deflate, br, br, gzip, ফাঁকা স্থানসহ বা ছাড়া)। Cache যদি মানগুলো হুবহু key-তে ধরে, একই ফাইলের ডজনখানেক কপি হয় আর hit ratio পড়ে যায়; তাই ভালো CDN আর proxy এই header-কে কয়েকটি মানে normalize করে। Vary: User-Agent আরও খারাপ — হাজার রকম User-Agent, প্রায় প্রতিটি অনুরোধ আলাদা key. আর Vary: * কার্যত পুনর্ব্যবহার বন্ধ করে দেয়। Vary-তে যত কম header, তত ভালো।

Cache busting — দীর্ঘ cache, তবু তাৎক্ষণিক হালনাগাদ

দ্বন্দ্বটি এরকম: CSS আর JavaScript এক বছর cache করলে সাইট দ্রুত, কিন্তু নতুন সংস্করণ ছাড়লে পুরোনো কপি এক বছর ধরে চলবে। সমাধান: ফাইলের বিষয়বস্তু বদলালে URL-ই বদলে দিন — একে বলে cache busting. সবচেয়ে প্রচলিত রূপ fingerprinting: build-এর সময় বিষয়বস্তুর hash নামে বসানো।

index.html            Cache-Control: no-cache                  (প্রতিবার যাচাই, 304 সস্তা)
  └─ <script src="/assets/app.3f9a2c.js">
/assets/app.3f9a2c.js Cache-Control: public, max-age=31536000, immutable

নতুন সংস্করণ ছাড়লে:
index.html            (নতুন ETag → 200, ছোট ফাইল)
  └─ <script src="/assets/app.b71e04.js">   ← নতুন নাম, তাই নতুন cache key
/assets/app.3f9a2c.js আর কেউ চায় না — নিজে থেকে একসময় eviction-এ সরে যায়

এই নকশার যুক্তি: HTML ছোট আর সবসময় যাচাই হয়, তাই হালনাগাদ তাৎক্ষণিক; বড় ফাইলগুলো এক বছর বিনা প্রশ্নে চলে, কারণ একটি নামের বিষয়বস্তু কখনো বদলায় না। সরল বিকল্প হলো query দিয়ে সংস্করণ — style.css?v=20261028. এতে ফাইলের নাম বদলাতে হয় না, শুধু রেফারেন্স; এই সাইটের নিজের CSS-ও এভাবে চলে। দুর্বলতা: কেউ সংস্করণ বাড়াতে ভুলে গেলে পুরোনো কপি চলে, আর কিছু পুরোনো proxy একসময় query-যুক্ত URL cache করত না বা CDN-এ query বাদ দিয়ে key বানানোর সেটিং থাকতে পারে — নিজের CDN-এর নীতি যাচাই করে নিন।

কী কখনো shared cache-এ যাবে না

লগইন-করা ব্যবহারকারীর পাতা — প্রোফাইল, ব্যাংক-বিবরণী, অর্ডারের তালিকা, যেকোনো পাতা যাতে নাম, ফোন নম্বর বা টোকেন আছে — shared cache-এ জমলে পরের ব্যবহারকারী আগের জনের তথ্য দেখবেন। এমন response-এ Cache-Control: private (ব্রাউজারে রাখা চললে) বা no-store (কোথাও না); আর Set-Cookie-যুক্ত response-কে CDN-এ cache-যোগ্য না রাখা নিরাপদ অভ্যাস।

Cache-সংক্রান্ত দুটি দুর্বলতা — ধারণা আর প্রতিরক্ষা।

  • Web cache deception: CDN-এর নিয়ম হয়তো বলে: .css বা .jpg-এ শেষ হওয়া URL স্থির ফাইল, তাই cache হবে। কিন্তু অ্যাপটি /account/anything.css-এর মতো পথেও ব্যবহারকারীর অ্যাকাউন্ট-পাতা দেখায়। ফলে কাউকে এমন লিংকে পাঠানো গেলে তাঁর ব্যক্তিগত পাতা shared cache-এ জমে যেতে পারে। প্রতিরক্ষা: cache-এর সিদ্ধান্ত নিন origin-এর Cache-Control আর Content-Type দেখে, শুধু URL-এর শেষাংশ দেখে নয়; অজানা পথে অ্যাপ 404 দিক; ব্যক্তিগত পাতায় সবসময় no-store বা private.
  • Web cache poisoning: কোনো request header (যেমন একটি অপ্রচলিত host-জাতীয় header) response বদলায়, কিন্তু সেই header cache key-তে নেই। তাহলে একজনের অনুরোধে তৈরি ক্ষতিকর উত্তর জমে সবাইকে দেওয়া হতে পারে। প্রতিরক্ষা: যে header উত্তর বদলায় তা হয় key-তে (বা Vary-তে) রাখুন, নয়তো অ্যাপে উপেক্ষা করুন; অপ্রয়োজনীয় header edge-এ মুছে দিন; CDN-এর key-নীতি নিয়মিত পরীক্ষা করুন — শুধু নিজের সাইটে, নিজের অনুমতিতে।

৭ · CDN — বিশ্বজুড়ে ছড়ানো cache, আর কাছের edge খুঁজে পাওয়া

একটি CDN (Content Delivery Network) হলো বহু শহরে, বহু নেটওয়ার্কের ভেতরে বসানো হাজারো cache সার্ভারের একটি পরিকল্পিত নেটওয়ার্ক, যা সাইটের মালিকের পক্ষে কাজ করে। লাভ তিন দিকে: (১) দূরত্ব কমে, তাই RTT কমে — আর প্রতিটি নতুন TCP/TLS বা QUIC সংযোগে কয়েকটি RTT লাগে বলে এর প্রভাব কয়েক গুণ; (২) নেটওয়ার্কের সীমানা কম পেরোতে হয় — edge যদি ISP-র ভেতরে বা IXP-তে থাকে, ট্রাফিক বিদেশি transit link-এ ওঠেই না (পর্ব ২৫-এর peering); (৩) ভার আর আক্রমণ ছড়িয়ে যায় — হঠাৎ ভিড় বা DDoS হাজার সার্ভারে ভাগ হয়।

দুই দর্শন: enter deep আর bring home

পাঠ্যবইয়ে (Kurose-Ross) CDN-এর সার্ভার বসানোর দুই দর্শনের নাম দেওয়া হয়েছে। Enter deep: সারা বিশ্বের অসংখ্য access ISP-র ভেতরে ছোট ছোট সার্ভার-গুচ্ছ বসানো, যাতে ব্যবহারকারীর একদম কাছে থাকা যায় — Akamai এই পথের পরিচিত উদাহরণ। দাম: হাজারো জায়গায় যন্ত্র রক্ষণাবেক্ষণ আর হালনাগাদ। Bring home: কম সংখ্যক জায়গায় (প্রায়ই বড় IXP-র কাছে) বড় বড় cluster — পরিচালনা সহজ, কিন্তু ব্যবহারকারী থেকে একটু দূরে; পাঠ্যবই এখানে Limelight-এর নাম নেয়। বাস্তবে আজকের বড় CDN-গুলো দুটোর মিশ্রণ, আর নামগুলো পাঠ্যবইয়ের শ্রেণিবিভাগ, শিল্পের কোনো মান নয়।

ব্যবহারকারী কীভাবে কাছের edge-এ পৌঁছান — DNS redirection

সবচেয়ে প্রচলিত কৌশল DNS দিয়ে। সাইটের মালিক নিজের নামটিকে একটি CNAME দিয়ে CDN-এর নামে পাঠান। CDN-এর নিজস্ব authoritative DNS তারপর প্রশ্নকর্তা কোথায় তা দেখে কাছের edge-এর IP দেয়। সমস্যা একটাই: CDN-এর DNS ব্যবহারকারীকে দেখে না, দেখে তাঁর recursive resolver-কে (পর্ব ৩৫)। Resolver ব্যবহারকারীর কাছে হলে ঠিক আছে; দূরে হলে ভুল edge. এর প্রতিকার EDNS Client Subnet (ECS, RFC 7871, ২০১৬): resolver ক্লায়েন্টের ঠিকানার একটি ছাঁটা অংশ (যেমন /24) জানিয়ে দেয়। সব resolver এটি পাঠায় না — গোপনীয়তার কারণে কিছু public resolver ইচ্ছা করেই পাঠায় না বা সীমিত করে।

নিচের উদাহরণে সব নাম আর ঠিকানা কাল্পনিক (documentation-এর জন্য সংরক্ষিত IP পরিসর)। চট্টগ্রামের একজন ব্যবহারকারী www.shop.example.com খুলছেন; তাঁর ISP-র resolver ঢাকায়।

  1. ব্যবহারকারী203.0.113.45চট্টগ্রাম
  2. ISP resolver192.0.2.53ঢাকা
  3. সাইটের DNSCNAMEshop.example.com
  4. CDN-এর DNSedge বাছাইcdn.example.net
প্রশ্ন আর উত্তর
কে কাকে
প্রশ্ন / উত্তর
CDN কী দেখল
Cache-এ কী থাকল
  1. প্রশ্ন resolver-এর কাছে। ব্যবহারকারীর যন্ত্র নিজে পুরো খোঁজ করে না; ISP-র recursive resolver-কে জিজ্ঞেস করে www.shop.example.com-এর ঠিকানা কী।
  2. সাইটের DNS দেয় ডাকনাম। Resolver (root আর .com পেরিয়ে, পর্ব ৩৫) সাইটের authoritative DNS-এ পৌঁছায়। উত্তর IP নয়, একটি CNAME: “এই নামটি আসলে www.shop.example.com.cdn.example.net”. এর DNS TTL ৩৬০০ সেকেন্ড — এই সম্পর্ক সহজে বদলায় না।
  3. CDN-এর DNS বাছাই করে। Resolver এবার CDN-এর DNS-কে জিজ্ঞেস করে। CDN দেখে প্রশ্নকর্তার ঠিকানা 192.0.2.53 — তার মানচিত্রে এটি ঢাকার একটি ISP; ECS থাকলে ক্লায়েন্টের 203.0.113.0/24 অংশও দেখে। নিজের মাপজোখ (কোন edge কাছে, কোনটি ব্যস্ত নয়) মিলিয়ে দেয় ঢাকার edge 198.51.100.20, TTL মাত্র ৬০ সেকেন্ড — যাতে প্রয়োজনে দ্রুত অন্য edge-এ সরানো যায়।
  4. উত্তর ব্যবহারকারীর কাছে। Resolver পুরো শিকল (CNAME + A) ফেরত দেয় আর নিজে cache করে — CNAME এক ঘণ্টা, A এক মিনিট। ওই এক মিনিটে একই resolver-এর অন্য ব্যবহারকারীরাও একই edge পাবেন।
  5. সংযোগ যায় কাছের edge-এ। ব্রাউজার ইউরোপের origin-এ নয়, ঢাকার edge-এ সংযোগ করে — RTT প্রায় ৮ ms (উদাহরণ)। Edge-এর কাছে সাইটের certificate আছে, তাই TLS-ও এখানেই শেষ হয় (অংশ ৮)। এরপর অংশ ২-এর প্রথম stepper-এর ঘটনা।
  6. দুর্বলতা: দূরের resolver. ব্যবহারকারী যদি অন্য দেশে বসানো কোনো public resolver ব্যবহার করেন আর সেটি ECS না পাঠায়, CDN ভাবে ব্যবহারকারী সেই resolver-এর কাছে — ফলে ভুল, দূরের edge. এজন্যই ECS এসেছে, আর এজন্যই কিছু CDN DNS-এর বদলে anycast-কে প্রাধান্য দেয়।

বিকল্প: anycast

অন্য কৌশলে CDN সব edge থেকে একই IP prefix ঘোষণা করে (anycast, পর্ব ২৫)। BGP নিজেই প্রতিটি ব্যবহারকারীর packet-কে “সবচেয়ে কাছের” — আসলে BGP-র নীতিতে সবচেয়ে পছন্দের — edge-এ পাঠায়। সুবিধা: resolver-এর অবস্থানের সমস্যা নেই, আর একটি edge বন্ধ হলে route সরে যায়। অসুবিধা: BGP RTT মাপে না, তাই “কাছের” সবসময় দ্রুততম নয়; আর সংযোগের মাঝে route বদলালে একটি TCP সংযোগ অন্য edge-এ গিয়ে ভেঙে যেতে পারে (ছোট HTTP অনুরোধে এটি কদাচিৎ সমস্যা)। অনেক CDN দুটো মেলায়: DNS দিয়ে অঞ্চল, anycast দিয়ে অঞ্চলের ভেতরে। পর্ব ২৬-এ দেখেছেন, multicast-এর বদলে ইন্টারনেটে বড় পরিসরে একই কনটেন্ট বিতরণের ব্যবহারিক উত্তর হয়ে উঠেছে এই CDN-ই।

৮ · CDN-এর ভেতরে — স্তর, purge, TLS, edge computing আর মাপজোখ

Cache-এর স্তর: edge → shield → origin

বিশ্বজুড়ে কয়েকশো edge থাকলে, প্রতিটি edge-এর প্রথম miss origin-এ গেলে একটি নতুন ফাইলের জন্য origin কয়েকশো অনুরোধ পায়। তাই CDN মাঝখানে একটি স্তর রাখে — regional cache বা origin shield: সব edge-এর miss আগে সেখানে যায়, আর শুধু shield-এর miss origin-এ। ফলে origin একটি ফাইলের জন্য আদর্শভাবে একবারই জিজ্ঞাসিত হয়। দাম: miss-এর পথে একটি বাড়তি hop.

Request collapsing — একসাথে একশো miss, origin-এ একটি

একটি খেলার স্কোর-পাতার মেয়াদ ফুরানোর ঠিক মুহূর্তে একটি edge-এ একসাথে ৫০০ অনুরোধ এল। সরল cache হলে ৫০০টি অনুরোধই origin-এ যেত (“thundering herd” বা cache stampede). Request collapsing-এ edge প্রথম অনুরোধটি origin-এ পাঠায়, বাকি ৪৯৯টি সেই উত্তরের অপেক্ষায় দাঁড় করিয়ে রাখে, তারপর একই উত্তর সবাইকে দেয়। stale-while-revalidate এর সাথে মিলে আরও ভালো: অপেক্ষাও করতে হয় না, পুরোনো কপি যায় আর পেছনে একটিমাত্র যাচাই চলে। ব্যক্তিগত বা অ-cache-যোগ্য উত্তরে collapsing বিপজ্জনক — একজনের উত্তর অন্যের কাছে চলে যেতে পারে — তাই CDN সেখানে এটি বন্ধ রাখে।

Purge — জোর করে ভুলিয়ে দেওয়া

ভুল দাম বা ভুল ছবি প্রকাশ হয়ে গেছে, আর max-age এক দিন। তখন CDN-এর API দিয়ে purge (invalidation): নির্দিষ্ট URL, URL-এর অংশ, বা ট্যাগ (surrogate key) ধরে কপি মুছে বা stale ঘোষণা করা। আধুনিক CDN সেকেন্ডের মধ্যে purge ছড়ানোর দাবি করে, কিন্তু মনে রাখবেন: purge শুধু CDN-এর কপি মোছে, ব্যবহারকারীর ব্রাউজারের কপি নয়। তাই ব্রাউজারের জন্য ছোট max-age আর CDN-এর জন্য বড় s-maxage — এটি একটি প্রচলিত কৌশল; আর fingerprinting থাকলে purge-এর দরকারই কম।

Edge-এ TLS

HTTPS সাইটের সামনে CDN থাকলে ব্রাউজারের TLS সংযোগ শেষ হয় edge-এ (TLS termination) — edge-এর কাছে সাইটের নামের certificate আর private key (বা key ব্যবহারের ব্যবস্থা) থাকে। এতে handshake-এর RTT ছোট হয় আর edge বিষয়বস্তু দেখে cache করতে পারে। Edge থেকে origin-এ আলাদা একটি সংযোগ যায় — এটিও এনক্রিপ্টেড রাখা জরুরি, নইলে ব্যবহারকারী তালা দেখলেও পথের অর্ধেক খোলা। দাম হলো বিশ্বাস: CDN আপনার ব্যবহারকারীদের ট্রাফিক খোলা অবস্থায় দেখতে পায়। TLS আর certificate-এর বিস্তারিত পর্ব ৪৮-এ।

বাংলাদেশের প্রেক্ষাপট

বাংলাদেশের বিদেশমুখী ইন্টারনেট মূলত সাবমেরিন কেবল (SEA-ME-WE সিরিজ) আর স্থলপথের কিছু সংযোগ দিয়ে যায়, আর এই international bandwidth তুলনামূলক দামি। বড় কনটেন্ট প্রতিষ্ঠানগুলোর অনেকে বিশ্বজুড়ে ISP-দের নিজেদের নেটওয়ার্কের ভেতরে cache সার্ভার বসানোর কর্মসূচি চালায় — যেমন Google Global Cache বা Netflix Open Connect নামে পরিচিত কর্মসূচিগুলো — আর দেশের অনেক ISP এমন cache রাখে বা BDIX-এর মতো দেশীয় exchange-এর মাধ্যমে দেশের ভেতরের cache থেকে কনটেন্ট পায় বলে প্রচলিত ধারণা। কোন ISP-তে ঠিক কী আছে, তা প্রতিষ্ঠানভেদে আলাদা আর সময়ের সাথে বদলায়; তাই নির্দিষ্ট দাবির আগে নিজে মেপে দেখাই ভালো — যেমন জনপ্রিয় ভিডিও-সাইটে traceroute দিলে শেষ hop-গুলো দেশের ভেতরেই থাকে কি না (পর্ব ২০)। মূল কথাটি সার্বজনীন: জনপ্রিয় কনটেন্ট যত কাছে, ব্যবহারকারীর গতি তত বেশি আর ISP-র বিদেশি bandwidth-এর খরচ তত কম।

Edge computing

CDN-এর edge এখন শুধু ফাইল জমায় না, ছোট প্রোগ্রামও চালায় — edge computing বা “edge function”: ভাষা বা দেশ দেখে redirect, header যোগ, ছবির আকার বদল, সরল A/B পরীক্ষা, bot ছাঁকা, এমনকি ছোট API. সুবিধা ব্যবহারকারীর কাছে হিসাব; সীমাবদ্ধতা — কম memory আর CPU-সময়, আর ডেটাবেস সাধারণত দূরে থেকে যায়, তাই ডেটা-নির্ভর কাজে লাভ সীমিত।

মাপজোখ: hit ratio, byte hit ratio, TTFB

  • Cache hit ratio = hit ÷ মোট অনুরোধ। ১০,০০০ অনুরোধে ৯,২০০ hit হলে ০.৯২.
  • Byte hit ratio = cache থেকে দেওয়া বাইট ÷ মোট বাইট। ছোট আইকনে hit বেশি কিন্তু বড় ভিডিওতে miss হলে অনুরোধের হিসাবে ভালো দেখায়, বাইটের হিসাবে খারাপ। Bandwidth-খরচ বোঝে এই সংখ্যা।
  • TTFB (time to first byte): অনুরোধ শুরু থেকে উত্তরের প্রথম বাইট পর্যন্ত। এতে DNS, সংযোগ, TLS আর সার্ভারের ভাবনার সময় মেশে — তাই edge-এ hit হলে TTFB কম, origin পর্যন্ত miss গেলে বেশি। ব্রাউজারের developer tools-এর Network ট্যাবে প্রতিটি অনুরোধের এই ভাগগুলো দেখা যায়।

জায়গা ফুরালে কী সরবে — LRU আর LFU

Edge-এর disk আর memory সীমিত, তাই নতুন জিনিস রাখতে পুরোনো কিছু সরাতে হয় — eviction. দুই ক্লাসিক নিয়ম: LRU — যেটি সবচেয়ে বেশি সময় আগে শেষবার ব্যবহার হয়েছে; LFU — যেটি সবচেয়ে কম বার ব্যবহার হয়েছে। OS-এর page replacement-এও ঠিক এই প্রশ্ন আসে (অপারেটিং সিস্টেম কোর্সের page replacement পর্ব); LRU-কে O(1)-এ চালাতে hash table আর doubly linked list একসাথে লাগে। নিচের simulator একই ৩-জায়গার cache-এ দুটি ভিন্ন ধরনের অনুরোধ-ধারা চালায়।

// Edge cache with room for N = 3 objects: LRU vs LFU on two request traces.
// LRU evicts the object used longest ago; LFU evicts the one used least often
// (count kept only while cached; ties broken by older last use).
const N = 3;
function run(trace, policy) {
  const c = new Map();                      // obj -> { freq, last }
  let hits = 0; const ev = [];
  trace.forEach((o, t) => {
    if (c.has(o)) { hits++; c.get(o).freq++; c.get(o).last = t; return; }
    if (c.size === N) {
      let victim = null;
      for (const [k, v] of c) {
        const w = victim && c.get(victim);
        if (!w || (policy === "LRU" ? v.last < w.last
                   : v.freq < w.freq || (v.freq === w.freq && v.last < w.last))) victim = k;
      }
      c.delete(victim); ev.push(victim);
    }
    c.set(o, { freq: 1, last: t });
  });
  console.log(`  ${policy}: hits ${hits}/${trace.length} = ${(hits / trace.length).toFixed(3)}` +
              `  evictions ${ev.length}: ${ev.join(" ")}`);
}
const traces = {
  "1) popular A, B + one-time objects": "A B A B A C D A E B F A G B",
  "2) popularity shifts from A, B to C, D, E": "A B A B A C D C D C E D C E D",
};
for (const [name, s] of Object.entries(traces)) {
  console.log(name + ":  " + s);
  run(s.split(" "), "LRU");
  run(s.split(" "), "LFU");
}

আউটপুট:

1) popular A, B + one-time objects:  A B A B A C D A E B F A G B
  LRU: hits 4/14 = 0.286  evictions 7: B C D A E B F
  LFU: hits 7/14 = 0.500  evictions 4: C D E F
2) popularity shifts from A, B to C, D, E:  A B A B A C D C D C E D C E D
  LRU: hits 10/15 = 0.667  evictions 2: B A
  LFU: hits 3/15 = 0.200  evictions 9: C D C D C E D C E

প্রথম ধারায় A আর B জনপ্রিয়, মাঝে মাঝে একবার-আসা জিনিস (C, D, E, F, G) — যেমন কেউ একটি পুরোনো খবর একবার খুলল। LRU এই একবারের জিনিসগুলোকে “সাম্প্রতিক” ভেবে রাখে আর জনপ্রিয় B, এমনকি A-কেও সরিয়ে দেয় (৭টি eviction, hit ০.২৮৬)। LFU জনপ্রিয়দের গণনা দেখে রাখে, শুধু একবারের জিনিসগুলো সরায় (hit ০.৫০০)। দ্বিতীয় ধারায় উল্টো: শুরুতে A (৩ বার) আর B (২ বার) জনপ্রিয়, তারপর চাহিদা C, D, E-তে সরে গেছে। LFU পুরোনো বড় গণনার কারণে A আর B আঁকড়ে থাকে আর নতুন জনপ্রিয়দের বারবার সরায় (৯টি eviction, hit ০.২০০); LRU দ্রুত মানিয়ে নেয় (০.৬৬৭)। বাস্তবের cache তাই প্রায়ই মিশ্র নিয়ম চালায় — যেমন সময়ের সাথে গণনা কমিয়ে আনা (aging), বা নতুন জিনিসকে প্রথমে একটি ছোট “পরীক্ষামূলক” অংশে রাখা।

আরও গভীরে: বড় CDN-এ একটি শহরের ভেতরেও কয়েকশো সার্ভার; কোন URL কোন সার্ভারে থাকবে তা ঠিক করতে প্রায়ই consistent hashing ব্যবহার হয় — Akamai-র প্রতিষ্ঠাতাদের ১৯৯৭ সালের গবেষণাপত্র এই ধারণার পরিচিত উৎস — যাতে একটি সার্ভার যোগ বা বাদ দিলে শুধু অল্প কিছু key সরে। Disk-ভিত্তিক cache-এ “একবার-আসা” জিনিস যাতে disk নষ্ট না করে, সেজন্য অনেকে দ্বিতীয়বার চাওয়ার পরই জমায় (“cache on second hit”). আর eviction-এর গবেষণা LRU আর LFU-কে ছাড়িয়ে গেছে: ARC, TinyLFU-র মতো নিয়ম সাম্প্রতিকতা আর পুনরাবৃত্তি দুটোই মাপে।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • Web cache কীভাবে access link-এর delay কমায়? — hit-গুলো link পেরোয় না, তাই traffic intensity (১ − hit rate) গুণ কমে আর queuing delay অনেক কমে; hit-এ Internet delay-ও এড়ায়।
  • no-cache আর no-store-এর পার্থক্য? — no-cache: রাখা যায়, প্রতিবার যাচাই; no-store: রাখাই যায় না।
  • 304 Not Modified কখন আসে? — conditional request (If-None-Match / If-Modified-Since)-এ চলতি সংস্করণ না বদলালে; body থাকে না।
  • CDN ব্যবহারকারীকে কাছের সার্ভারে পাঠায় কীভাবে? — DNS redirection (CNAME, resolver-এর অবস্থান, ECS) বা anycast (BGP).
  • CSS এক বছর cache করেও হালনাগাদ তাৎক্ষণিক রাখবেন কীভাবে? — fingerprinted নাম (app.3f9a2c.js) + immutable, আর HTML-এ no-cache.

৯ · AI-এর যুগে caching — header লেখা, মাপা আর যাচাই

“আমার সাইট দ্রুত করার একটি nginx config চাই” — AI মুহূর্তে লম্বা config দেয়। কিন্তু caching-এর ভুল নিঃশব্দ: সাইট চলে, শুধু একজনের তথ্য আরেকজন দেখে, বা হালনাগাদ এক সপ্তাহ কারও কাছে পৌঁছায় না। চেনা ভুলগুলো:

AI-এর ভুলআসল কথাকীভাবে যাচাই করবেন
“cache বন্ধ করতে no-cache দিন”জমা বন্ধ করতে no-store; no-cache শুধু প্রতিবার যাচাইRFC 9111-এর directive অংশ পড়ুন
সব কিছুতে public, max-age=31536000নাম না বদলানো ফাইল আর লগইন-পাতায় বিপজ্জনকনিজের সাইটে লগআউট/লগইন করে দুই ব্রাউজারে মিলিয়ে দেখুন
Access link-এর অঙ্কে hit rate দিয়ে link-এর delay গুণ করা, বা ρ ≥ ১-এ সসীম delay দেখানোশুধু miss link পেরোয়: ρnew = (১ − h) ρ; ρ ≥ ১-এ queue অসীম বাড়েঅংশ ৩-এর ক্যালকুলেটরে নিজের সংখ্যা বসান
“CDN সবসময় anycast দিয়ে কাজ করে” বা “DNS TTL 0 দিলে failover তাৎক্ষণিক”DNS আর anycast দুটোই চলে; অনেক resolver খুব ছোট TTL মানে নাdig দিয়ে নিজের ডোমেইনের CNAME আর TTL দেখুন
বানানো header বা ভুল RFC নম্বর (যেমন HTTP caching-কে RFC 2616 বলা)বর্তমানে RFC 9111 (caching), RFC 9110 (semantics); 2616 বহু আগে বাতিলrfc-editor.org-এ নম্বর আর অবস্থা মেলান

AI-কে caching নীতি লিখতে বললে প্রতিটি URL-এর ধরন আর ঝুঁকি স্পষ্ট করে দিন:

তুমি একজন অভিজ্ঞ web performance আর নিরাপত্তা প্রকৌশলীর মতো উত্তর দাও। RFC 9111 অনুযায়ী আমার সাইটের Cache-Control নীতি বানাও। ১. URL-গুলো: index.html, /assets/app.[hash].js, /img/logo.png (নাম বদলায় না), /api/prices (প্রতি মিনিটে বদলায়), /account (লগইন লাগে)। প্রতিটির জন্য browser আর CDN-এর আলাদা নীতি দাও, আর কেন তা এক লাইনে বলো। ২. no-cache, no-store, private, s-maxage, immutable আর stale-while-revalidate-এর মধ্যে কোনটা কোথায় আর কোথায় কখনো নয়, স্পষ্ট বলো। ৩. প্রতিটি নীতি curl দিয়ে কীভাবে যাচাই করব (কোন header দেখব) তা দাও — শুধু আমার নিজের ডোমেইনে। ৪. লগইন-পাতা যেন কোনো shared cache-এ না জমে, তার পরীক্ষার ধাপ দাও। ৫. নিশ্চিত না হলে বা CDN-ভেদে আচরণ আলাদা হলে স্পষ্ট লেখো।

AI-এর caching পরামর্শ চালু করার আগে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১প্রোগ্রামারদের একটি পুরোনো রসিকতা: “কম্পিউটার বিজ্ঞানে কঠিন কাজ দুটি — cache invalidation আর নাম দেওয়া।” Cache invalidation আসলে এত কঠিন কেন, আর ওয়েব এই সমস্যা কীভাবে পাশ কাটায়?

উক্তিটি প্রায়ই Netscape-এর প্রকৌশলী Phil Karlton-এর নামে উদ্ধৃত হয়। কঠিনতার মূল কারণ বিতরণ: একটি তথ্যের কপি ছড়িয়ে আছে হাজারো জায়গায় — কোটি ব্রাউজারে, অজানা সংখ্যক proxy-তে, CDN-এর শত শত edge-এ। মূল তথ্য বদলালে সবাইকে জানাতে হবে, অথচ origin জানেই না কার কাছে কপি আছে। ব্রাউজারের কপি দূর থেকে মুছে দেওয়ার কোনো উপায় HTTP-তে নেই; ব্রাউজার নিজে জিজ্ঞেস না করা পর্যন্ত সার্ভার তার কাছে পৌঁছাতে পারে না। আর বার্তা পৌঁছালেও সময়ের প্রশ্ন থাকে: purge কয়েক সেকেন্ডে ছড়ায়, কিন্তু সেই কয়েক সেকেন্ডে কেউ পুরোনো কপি পেয়ে নিজের কাছে আবার জমাতে পারে।

HTTP তাই “জানানো”-র বদলে “মেয়াদ”-এর ভাষা বেছে নিয়েছে। সার্ভার আগেই বলে দেয় কতক্ষণ কপি বিশ্বাস করা যাবে (max-age), তারপর cache নিজে জিজ্ঞেস করবে (validation). এতে নিশ্চয়তা আসে, কিন্তু একটি চুক্তি মেনে: max-age-এর সময়টুকু পুরোনো দেখানো গ্রহণযোগ্য। দাম আর গতির মাঝে সার্ভার নিজেই এই জানালার আকার ঠিক করে।

দ্বিতীয় কৌশল আরও চতুর — invalidation-কেই অপ্রয়োজনীয় করে দেওয়া। Fingerprinting-এ একটি URL-এর বিষয়বস্তু কখনো বদলায় না; বদলায় শুধু কোন URL ব্যবহার হবে। অর্থাৎ পরিবর্তনশীল অংশ ছোট একটি HTML-এ সীমাবদ্ধ, যা সবসময় যাচাই হয়, আর বাকি সব অপরিবর্তনীয়। ডেটাবেস আর বিতরণ-ব্যবস্থার জগতেও এই নীতি পরিচিত: immutable ডেটা আর নতুন সংস্করণের পরিচয় দিয়ে কাজ করলে সামঞ্জস্যের অনেক সমস্যা উধাও হয়। Git-এর commit hash বা Docker image-এর digest একই ধারণা।

যেখানে এই কৌশল খাটে না — যেমন একটি দামের তালিকা বা খবরের প্রথম পাতা — সেখানে প্রকৌশলীরা ছোট TTL, stale-while-revalidate আর purge মিলিয়ে চলেন, আর স্পষ্ট করে নেন কতটা পুরোনো ব্যবসার জন্য গ্রহণযোগ্য।

মূল উপলব্ধি: বিতরণ-ব্যবস্থায় “সবাইকে ভুলিয়ে দেওয়া” প্রায় অসম্ভব; তাই ভালো নকশা হয় মেয়াদ দিয়ে সীমা টানে, নয়তো বদলানো জিনিসকে নতুন নাম দিয়ে পুরোনোকে আপনা থেকেই অপ্রাসঙ্গিক করে।

প্র ০২CDN শিল্পের জন্ম কীভাবে হলো, আর আজ বড় কনটেন্ট প্রতিষ্ঠানগুলো নিজেদের CDN ও নেটওয়ার্ক বানানোয় ইন্টারনেটের গঠন কীভাবে বদলেছে?

১৯৯০-এর দশকের শেষে ওয়েব জনপ্রিয় হতেই একটি সমস্যা প্রকট হলো: কোনো সাইট হঠাৎ জনপ্রিয় হলে (একে বলা হতো “flash crowd”, পরে “Slashdot effect”) সার্ভার আর তার link ভেঙে পড়ত, ঠিক যখন সবচেয়ে বেশি মানুষ দেখতে চায়। MIT-র গণিতবিদ Tom Leighton আর তাঁর ছাত্র Danny Lewin এই সমস্যার গাণিতিক সমাধান খুঁজছিলেন — অনুরোধ কীভাবে অনেক সার্ভারে ভাগ করা যায় যাতে একটি সার্ভার যোগ বা বাদ দিলে সব এলোমেলো না হয়; এ থেকেই consistent hashing-এর গবেষণা। ১৯৯৮ সালে তাঁরা Akamai প্রতিষ্ঠা করেন। Danny Lewin ২০০১ সালের ১১ সেপ্টেম্বর ছিনতাই হওয়া প্রথম বিমানে নিহত হন — শিল্পের ইতিহাসে একটি বেদনাদায়ক অধ্যায়।

প্রথম যুগের CDN ছিল তৃতীয় পক্ষের সেবা: সাইটের মালিক টাকা দিতেন, CDN নিজের সার্ভার ISP-দের ভেতরে বসাত। ২০১০-এর দশকে ভিডিও যখন ইন্টারনেট ট্রাফিকের বড় অংশ হয়ে উঠল, সবচেয়ে বড় কনটেন্ট প্রতিষ্ঠানগুলো নিজেরাই CDN বানাল — নিজস্ব সার্ভার ISP-র ভেতরে দেওয়ার কর্মসূচি (Netflix-এর Open Connect, Google-এর Global Cache), নিজস্ব সাবমেরিন কেবলে বিনিয়োগ, আর বড় IXP-গুলোতে সরাসরি peering.

গবেষকরা একে বলেন ইন্টারনেটের “flattening”: আগে ট্রাফিক স্তরে স্তরে Tier-1 transit নেটওয়ার্ক পেরিয়ে যেত (পর্ব ২৫); এখন বড় অংশ সরাসরি কনটেন্ট প্রতিষ্ঠানের নেটওয়ার্ক থেকে access ISP-তে যায়, মাঝের পরিবাহক এড়িয়ে। লাভ — দ্রুত গতি, কম খরচ। প্রশ্ন — ক্ষমতার কেন্দ্রীকরণ: অল্প কয়েকটি প্রতিষ্ঠানের নেটওয়ার্ক আর CDN-এ সমস্যা হলে একসাথে অনেক সাইট অচল হয়, যা কয়েকটি বড় CDN-বিভ্রাটে দেখা গেছে। আর ছোট দেশের ISP-রা দর-কষাকষিতে দুর্বল থাকে।

বাংলাদেশের মতো দেশের জন্য শিক্ষাটি ব্যবহারিক: দেশীয় IXP-তে peering আর ISP-র ভেতরের cache বিদেশি bandwidth বাঁচায় ও গতি বাড়ায়, কিন্তু নির্ভরতাও তৈরি করে।

মূল উপলব্ধি: CDN একটি প্রযুক্তিগত সমাধান হিসেবে জন্মেছিল (ভিড় সামলানো), কিন্তু আজ এটি ইন্টারনেটের অর্থনীতি আর গঠনেরই একটি স্তম্ভ — কে কত কাছে, কার কত পথ, তা-ই ঠিক করে কে কত দ্রুত।

প্র ০৩২০০০-এর দশকে অনেক ISP আর বিশ্ববিদ্যালয় transparent proxy cache দিয়ে প্রচুর bandwidth বাঁচাত। HTTPS প্রায় সর্বজনীন হওয়ায় সেই যুগ শেষ। এটি ভালো হলো না খারাপ?

সেই যুগে Squid-এর মতো proxy ছিল ISP আর বিশ্ববিদ্যালয়ের প্রিয় হাতিয়ার। বিশেষ করে যেসব দেশে international bandwidth দামি — বাংলাদেশসহ দক্ষিণ এশিয়ার অনেক দেশ — সেখানে একটি ভালো proxy উল্লেখযোগ্য ট্রাফিক দেশের ভেতরেই মেটাত। অনেক সময় ব্যবহারকারী জানতেনও না; নেটওয়ার্ক নিজেই port 80-এর ট্রাফিক proxy-তে ঘুরিয়ে দিত।

কিন্তু এর অন্ধকার দিকও ছিল। Transparent proxy ভুল কনফিগারেশনে পুরোনো বা অন্যের পাতা দেখাত; কিছু ISP পাতায় নিজেদের বিজ্ঞাপন ঢুকিয়ে দিত; ব্যবহারকারীর প্রতিটি URL লগ হতো। ২০১৩-র পর নজরদারি নিয়ে বিশ্বব্যাপী আলোচনার পর, আর Let's Encrypt (২০১৪ সালে ঘোষিত, ২০১৫–১৬ থেকে চালু) বিনামূল্যে certificate সহজ করায়, HTTPS দ্রুত সর্বজনীন হলো। ব্রাউজারও HTTP সাইটকে “Not secure” দেখাতে শুরু করল। ফলে পথের proxy আর URL বা বিষয়বস্তু দেখতে পায় না — cache-ও করতে পারে না।

লাভ স্পষ্ট: গোপনীয়তা, বিষয়বস্তুর অখণ্ডতা (কেউ বিজ্ঞাপন বা ক্ষতিকর কোড ঢোকাতে পারে না), আর নিশ্চয়তা যে আপনি সত্যিকারের সার্ভারের সাথেই কথা বলছেন। দাম: ISP-র নিজস্ব cache-এর ক্ষমতা গেল, সাশ্রয়ের নিয়ন্ত্রণ চলে গেল কনটেন্ট প্রতিষ্ঠানের হাতে। এখন ISP সাশ্রয় পায় তখনই, যখন বড় প্রতিষ্ঠান নিজের cache তার নেটওয়ার্কে বসাতে রাজি হয় — যা বড় ISP-র জন্য সহজ, ছোটদের জন্য কঠিন। যে দীর্ঘ লেজের (long tail) ছোট সাইট এমন কর্মসূচিতে নেই, তাদের ট্রাফিক এখন প্রতিবার বিদেশ থেকে আসে।

কিছু প্রতিষ্ঠান TLS inspection দিয়ে আবার দেখার চেষ্টা করে, কিন্তু তাতে প্রতিটি যন্ত্রে নিজস্ব root certificate বসাতে হয় আর ভুল বাস্তবায়নে নিরাপত্তা দুর্বল হয় — সাধারণ ISP-র জন্য এটি গ্রহণযোগ্য পথ নয়।

মূল উপলব্ধি: নিরাপত্তা আর caching-এর মধ্যে আপসে ওয়েব নিরাপত্তা বেছে নিয়েছে; caching হারায়নি, শুধু তার নিয়ন্ত্রণ পথের মাঝখান থেকে কনটেন্টের মালিকের কাছে সরে গেছে।

প্র ০৪Web cache deception আর web cache poisoning দুটোই “cache ভুল জিনিস জমাল” ধরনের সমস্যা। এদের মূল কারণ কী, আর একটি সাইট নকশার শুরু থেকেই কীভাবে এগুলো এড়াতে পারে?

দুই সমস্যার মূলে একটি মিল: cache আর অ্যাপ্লিকেশন একই অনুরোধকে ভিন্ন চোখে দেখে। Cache একটি সরল নিয়মে চলে — key (method + URL + Vary-র header) মিললে একই উত্তর। অ্যাপ্লিকেশন আরও অনেক কিছু দেখে — cookie, অপ্রচলিত header, URL-এর অতিরিক্ত অংশ উপেক্ষা করা ইত্যাদি। এই দুই দৃষ্টির ফাঁকেই দুর্বলতা।

Cache deception-এ ফাঁকটি “কোনটি স্থির ফাইল” নিয়ে। CDN-এর নিয়ম বলে .css-এ শেষ হলে cache হবে, অথচ অ্যাপ /account/x.css-কে /account হিসেবে দেখে আর ব্যক্তিগত পাতা দেয়। ২০১৭ সালে নিরাপত্তা-গবেষক Omer Gil এই শ্রেণিটি প্রকাশ্যে আলোচনায় আনেন বলে পরিচিত। Cache poisoning-এ ফাঁকটি “কোন header উত্তর বদলায়” নিয়ে: অ্যাপ এমন একটি header ব্যবহার করে যা key-তে নেই, তাই একজনের প্রভাবিত উত্তর সবাই পায়। PortSwigger-এর গবেষক James Kettle-এর ২০১৮ সালের কাজ এই শ্রেণিকে ব্যাপক মনোযোগে আনে।

নকশার শুরু থেকে প্রতিরক্ষা কয়েক স্তরে: (১) Origin ঠিক করবে কী cache হবে — CDN-এ origin-এর Cache-Control মেনে চলার নীতি, শুধু extension দেখে নয়। (২) ব্যক্তিগত সবকিছুতে স্পষ্ট no-store বা private, আর Set-Cookie-যুক্ত উত্তর shared cache-এ না রাখা। (৩) কঠোর routing — অ্যাপ অজানা পথে 404 দিক, পথের শেষে অতিরিক্ত অংশ নীরবে মেনে না নিক। (৪) Key আর input-এর মিল — যে header বা query উত্তর বদলায়, তা হয় key-তে থাকবে, নয়তো edge-এ মুছে ফেলা হবে। (৫) স্থির ফাইল আলাদা domain বা path-এ, যেখানে কোনো ব্যক্তিগত তথ্যই নেই। (৬) নিয়মিত নিজের সাইটে, নিজের অনুমতিতে, পরীক্ষা।

মূল উপলব্ধি: দুটি উপাদান একই জিনিসকে ভিন্নভাবে ব্যাখ্যা করলেই নিরাপত্তার ফাঁক জন্মায়। Caching নিরাপদ রাখার মূলমন্ত্র — cache-এর key আর অ্যাপের আচরণ যেন সবসময় একই কথা বলে।

প্র ০৫একটি নতুন CDN বানাতে হলে আপনি ব্যবহারকারীকে কাছের edge-এ পাঠাতে DNS-ভিত্তিক redirection নেবেন, না anycast? দুই পথের সূক্ষ্ম সমস্যাগুলো কী?

DNS-ভিত্তিক redirection-এর বড় শক্তি নিয়ন্ত্রণ। CDN প্রতিটি প্রশ্নে নিজের মাপজোখ — edge-গুলোর ভার, পথের RTT, কোন ISP-র সাথে কোন edge-এর peering — মিলিয়ে নির্দিষ্ট IP দিতে পারে, এমনকি ভিন্ন ব্যবহারকারীকে ভিন্ন edge-এ পাঠিয়ে ভার ভাগ করতে পারে। দুর্বলতা তিনটি। প্রথমত, সে ব্যবহারকারীকে নয়, resolver-কে দেখে; দূরের public resolver ব্যবহার করলে ভুল edge. ECS এটি সারায়, কিন্তু ক্লায়েন্টের ঠিকানার অংশ authoritative সার্ভারে পৌঁছায় — গোপনীয়তার আপস — আর resolver-এর cache-কে subnet-ভেদে ভাগ করে দক্ষতা কমায়। দ্বিতীয়ত, TTL: দ্রুত বদলাতে TTL ছোট রাখতে হয়, ফলে DNS প্রশ্ন বাড়ে; আর কিছু resolver ও অ্যাপ TTL পুরো মানে না, পুরোনো IP-তে আটকে থাকে। তৃতীয়ত, ব্যর্থতায় সাড়া TTL-এর গতিতেই সীমিত।

Anycast সরল আর মজবুত: সব edge একই prefix ঘোষণা করে, BGP বাকিটা করে। Resolver-এর অবস্থান অপ্রাসঙ্গিক, আর একটি edge বন্ধ হলে route প্রত্যাহার করলেই ট্রাফিক অন্যত্র যায়; DDoS-এর ট্রাফিকও স্বাভাবিকভাবে বহু edge-এ ছড়িয়ে পড়ে। কিন্তু BGP latency বা ভার জানে না, শুধু নীতি আর AS-path; তাই কোনো ISP-র নীতির কারণে ঢাকার ব্যবহারকারী সিঙ্গাপুরের edge-এ যেতে পারেন। আর ভার বাড়লে একটি edge-কে “একটু কম” ট্রাফিক দেওয়া কঠিন — হয় route ঘোষণা, নয় প্রত্যাহার। দীর্ঘ TCP সংযোগের মাঝে route বদলে অন্য edge-এ গেলে সংযোগ ভাঙার ঝুঁকিও আছে, যদিও বাস্তবে এটি বিরল বলে পরিমাপগুলো ইঙ্গিত দেয়।

তাই বাস্তবের বড় CDN-গুলো প্রায়ই মিশ্র পথে চলে: DNS অঞ্চল বা নেটওয়ার্ক ঠিক করে, anycast অঞ্চলের ভেতরে সহনশীলতা দেয়; আবার কেউ কেউ প্রায় পুরোপুরি anycast-নির্ভর। আরও সূক্ষ্ম নিয়ন্ত্রণের জন্য HTTP redirect বা অ্যাপের ভেতরে সার্ভার-তালিকা (যেমন ভিডিও প্লেয়ার, পর্ব ৪০) ব্যবহার হয়।

মূল উপলব্ধি: DNS redirection দেয় সূক্ষ্ম নিয়ন্ত্রণ কিন্তু ভুল তথ্যের (resolver-এর অবস্থান, TTL) ঝুঁকি; anycast দেয় সরলতা আর মজবুতি কিন্তু BGP-র অন্ধত্ব। ভালো নকশা দুটির শক্তি মেলায়।

অনুশীলন

খাতা-কলম নিন — ρ, সেকেন্ড আর header ধরে ধরে হিসাব করুন।

  1. হিসাব (access link আর cache): একটি অফিসের access link 20 Mbps, LAN-এর delay ০.০১ সেকেন্ড, প্রতিটি বস্তু 1 Mbit, গড়ে ১৮টি অনুরোধ প্রতি সেকেন্ডে, Internet delay ১.৫ সেকেন্ড। M/M/1 মডেলে (access delay = (L/R) ÷ (১ − ρ)) বের করুন: (ক) cache ছাড়া traffic intensity আর গড় delay; (খ) hit rate ০.৫-এর cache বসালে নতুন ρ আর গড় delay.

    (ক) ρ = L·a/R = ১ × ১৮ ÷ ২০ = ০.৯. L/R = ১ ÷ ২০ = ০.০৫ সেকেন্ড। Access delay = ০.০৫ ÷ (১ − ০.৯) = ০.৫ সেকেন্ড. গড় delay = ০.০১ + ০.৫ + ১.৫ = ২.০১ সেকেন্ড.

    (খ) শুধু অর্ধেক অনুরোধ link পেরোয়: ρ = ০.৫ × ০.৯ = ০.৪৫. Access delay = ০.০৫ ÷ ০.৫৫ ≈ ০.০৯০৯ সেকেন্ড। Miss-এর delay = ০.০১ + ০.০৯০৯ + ১.৫ = ১.৬০০৯. গড় = ০.৫ × ০.০১ + ০.৫ × ১.৬০০৯ = ০.০০৫ + ০.৮০০৫ ≈ ০.৮০৫৫ সেকেন্ড — প্রায় আড়াই গুণ দ্রুত। লক্ষ করুন, এখানে Internet delay বড় (১.৫ সেকেন্ড), তাই hit-এর লাভও বড়।

  2. হাতে trace (freshness): একটি CDN থেকে ব্রাউজার t = ০-তে এই header-সহ উত্তর পেল: Cache-Control: public, max-age=600, s-maxage=3600, Age: 100, ETag: "k9". (ক) ব্রাউজারের কপি কখন stale হবে? (খ) CDN-এর কাছে কপিটি (CDN-এর নিজের হিসাবে) আর কত সেকেন্ড fresh? (গ) t = ৭০০-তে একই ফাইল চাইলে ব্রাউজার কী পাঠাবে, আর ফাইল না বদলালে কী উত্তর পাবে?

    (ক) ব্রাউজার private cache, তাই max-age=600 মানে; কপির বয়স আসার মুহূর্তেই ১০০ (Age header). বাকি ৬০০ − ১০০ = ৫০০, তাই t = ৫০০ সেকেন্ডে stale.

    (খ) CDN shared cache, তার জন্য s-maxage=3600 প্রযোজ্য। কপির বয়স ১০০, তাই আর ৩৫০০ সেকেন্ড fresh.

    (গ) t = ৭০০-তে ব্রাউজারের কপি stale, তাই শর্তযুক্ত অনুরোধ: GET + If-None-Match: "k9". এটি যাবে CDN-এ; CDN-এর কপি এখনো fresh আর ETag মেলে, তাই CDN নিজেই 304 Not Modified দেবে — origin পর্যন্ত যেতে হবে না।

  3. আউটপুট অনুমান করুন: অংশ ৫-এর HTTP cache simulator-এ শুধু লোগোর max-age=60 বদলে max-age=100 করা হলো। t = ৯০, ১২০ আর ২০০-এর লোগোর তিনটি লাইন কী ছাপবে? আর এতে কোন সমস্যা চোখে পড়ে?
    t= 90s GET /logo.png       HIT fresh (age 90s < max-age 100s), 0 bytes from origin
    t=120s GET /logo.png       STALE (age 120s) -> If-None-Match: "v1" -> 304 Not Modified, reuse body
    t=200s GET /logo.png       HIT fresh (age 80s < max-age 100s), 0 bytes from origin

    ৯০-এ বয়স ৯০ < ১০০, তাই HIT. ১২০-এ stale; তখনো origin-এ "v1" (বদল ১৩০-এ), তাই 304, আর ঘড়ি নতুন করে ১২০ থেকে। ২০০-তে বয়স ৮০ < ১০০ — fresh HIT, অথচ origin-এ লোগো ১৩০ সেকেন্ডেই বদলে গেছে। ব্যবহারকারী ৭০ সেকেন্ড ধরে পুরোনো লোগো দেখছেন। বড় max-age মানে বেশি hit, কিন্তু বেশি পুরোনো দেখানোর সম্ভাবনা — এজন্যই বদলাতে পারে এমন ফাইলে fingerprinting.

  4. Header আর command লিখুন: একটি দোকানের সাইটে চার ধরনের URL: /index.html, /assets/app.3f9a2c.js, /api/prices (প্রতি মিনিটে বদলায়, সবার জন্য একই), /account/orders (লগইন লাগে)। প্রতিটির জন্য একটি Cache-Control লিখুন। তারপর নিজের সাইটের header আর conditional request যাচাইয়ের দুটি curl command লিখুন।
    /index.html              Cache-Control: no-cache
    /assets/app.3f9a2c.js    Cache-Control: public, max-age=31536000, immutable
    /api/prices              Cache-Control: public, max-age=30, s-maxage=60, stale-while-revalidate=30
    /account/orders          Cache-Control: no-store

    HTML প্রতিবার যাচাই হয় (ETag থাকলে প্রায় সবসময় সস্তা 304); hash-যুক্ত JS এক বছর বিনা প্রশ্নে; দামের API ব্রাউজারে ৩০ সেকেন্ড, CDN-এ ৬০ সেকেন্ড, তার পরেও ৩০ সেকেন্ড পুরোনো দিয়ে পেছনে নবায়ন — মান কয়েক সেকেন্ড পুরোনো হওয়া চলে কি না, তা ব্যবসার সিদ্ধান্ত; অর্ডারের তালিকা কোথাও জমা নয় (private-ও চলত, কিন্তু শেয়ার করা কম্পিউটারের কথা ভেবে no-store নিরাপদ)।

    curl -sI https://your-site.example/index.html | grep -i -E 'cache-control|etag|age|vary'
    curl -sI -H 'If-None-Match: "PUT-ETAG-HERE"' https://your-site.example/index.html | head -1

    প্রথমটি শুধু header দেখায় (-I মানে HEAD অনুরোধ)। দ্বিতীয়টিতে প্রথম command-এ পাওয়া ETag বসালে প্রথম লাইনে HTTP/2 304 বা HTTP/1.1 304 Not Modified আসা উচিত। Windows-এর PowerShell-এ curl.exe লিখুন, কারণ পুরোনো PowerShell-এ curl নামটি Invoke-WebRequest-এর ডাকনাম। শুধু নিজের বা অনুমতিপ্রাপ্ত সাইটে চালান।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমCDN ব্যবহার করা একটি সাইটের ছবি প্রথমবার চাওয়া হলো — ঘটনাগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ব্রাউজার নিজের cache-এ খোঁজে, কপি পায় না
  2. DNS-এ সাইটের নাম CNAME দিয়ে CDN-এর নামে যায়, CDN কাছের edge-এর IP দেয়
  3. Edge cache key দিয়ে খোঁজে, miss হয়
  4. Edge origin থেকে ছবিটি আনে, Cache-Control দেখে নিজের কাছে জমায়
  5. ব্রাউজার ছবি পায় আর max-age অনুযায়ী নিজের cache-এও রাখে

একটি HTTP response-এ Cache-Control: no-cache আছে। একটি সঠিক ব্রাউজার এর সাথে কী করবে?

  • একেবারেই জমা রাখবে না
  • জমা রাখবে, কিন্তু প্রতিবার ব্যবহারের আগে সার্ভারের কাছে যাচাই করবে
  • জমা রেখে যাচাই ছাড়াই যতদিন খুশি ব্যবহার করবে
  • শুধু CDN-এ জমা রাখবে, ব্রাউজারে নয়

একটি প্রতিষ্ঠানের access link 20 Mbps, প্রতিটি বস্তু 0.5 Mbit, গড়ে ৩০টি অনুরোধ প্রতি সেকেন্ডে। hit rate ০.৫-এর একটি cache বসানোর পর access link-এর traffic intensity কত?

  • ০.৭৫
  • ০.২৫
  • ০.৩৭৫
  • ১.৫

ব্রাউজার If-None-Match: "33a7" পাঠাল, আর সার্ভারে ফাইলের চলতি ETag-ও "33a7". সার্ভারের সঠিক উত্তর কী?

  • 304 Not Modified, body ছাড়া
  • 200 OK, পুরো ফাইলসহ
  • 412 Precondition Failed
  • 404 Not Found

একটি সাইট /assets/app.3f9a2c.js ফাইলে Cache-Control: public, max-age=31536000, immutable দেয়। নতুন সংস্করণ ছাড়ার পর ব্যবহারকারীরা পুরোনো কোড পাবেন না কেন?

  • immutable থাকায় ব্রাউজার প্রতিবার সার্ভারের কাছে যাচাই করে
  • এক দিন পর max-age শেষ হয়ে যায়
  • CDN purge ব্রাউজারের কপিও মুছে দেয়
  • নতুন সংস্করণের নাম (hash) আলাদা, আর HTML নতুন নামটি দেখায়

একটি CDN DNS দিয়ে কাছের edge বাছে। ECS (EDNS Client Subnet) ব্যবহার না হলে CDN-এর authoritative DNS সাধারণত কার ঠিকানা দেখে edge বাছাই করে?

  • ব্যবহারকারীর যন্ত্রের IP ঠিকানা
  • ব্যবহারকারীর recursive resolver-এর IP ঠিকানা
  • Origin সার্ভারের IP ঠিকানা
  • Root DNS সার্ভারের ঠিকানা

২টি বস্তু রাখার মতো একটি LRU cache শুরুতে খালি। অনুরোধ আসে এই ক্রমে: A, B, A, C. C রাখার সময় কোনটি সরানো হবে, আর শেষে cache-এ কী থাকবে?

  • A সরবে; শেষে B, C
  • C রাখাই হবে না; শেষে A, B
  • B সরবে; শেষে A, C
  • A আর B দুটোই সরবে; শেষে শুধু C

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Cache-এর চার লাভ: কম latency, origin-এর কম ভার, কম bandwidth খরচ, access link-এর কম জ্যাম। Browser cache private; forward proxy, reverse proxy আর CDN edge shared.
  2. Access link-এর অঙ্ক: ρ = La/R; cache থাকলে শুধু miss link পেরোয়, তাই ρnew = (১ − h)·ρ. গড় delay = h·dLAN + (১ − h)·(dLAN + daccess + dInternet); কোন queuing মডেল ধরলেন তা লিখুন।
  3. Freshness: max-age (সবার), s-maxage (shared), Expires (পুরোনো, max-age-এর কাছে হারে), heuristic (Last-Modified-এর প্রায় ১০%). no-cache = রাখা যায়, প্রতিবার যাচাই; no-store = রাখাই যায় না।
  4. Validation: ETag → If-None-Match, Last-Modified → If-Modified-Since; না বদলালে body-ছাড়া 304. Vary-তে কম header; বদলানো ফাইলে fingerprinted নাম + immutable; ব্যক্তিগত পাতা কখনো shared cache-এ নয়।
  5. CDN: কাছের edge-এ পৌঁছানো DNS (CNAME, resolver, ECS) বা anycast দিয়ে; edge → shield → origin, request collapsing, purge, edge-এ TLS, edge computing; মাপ hit ratio, byte hit ratio, TTFB; eviction LRU বা LFU — কোনটি ভালো তা অনুরোধের ধরনের ওপর নির্ভর করে।

পরের পর্বে: ওয়েবের পাশে ইন্টারনেটের আরেক পুরোনো স্তম্ভ — ইমেইল। SMTP কীভাবে চিঠি সার্ভার থেকে সার্ভারে পৌঁছায়, IMAP দিয়ে কীভাবে পড়েন, আর SPF, DKIM, DMARC কীভাবে জাল প্রেরক ঠেকায়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান