এই পর্বে যা পাবেন
- Byte থেকে ms: bundle, compression, code splitting আর LCP-এর বাজেটের হিসাব
- React-এ কতবার render হলো, তা memo আর state-এর ঠিকানা দেখে আগেই গোনা
- Offset বনাম keyset-এর খরচ, N+1-এর SQL সংখ্যা আর round trip-এর সময়
- Cache hit rate থেকে ডেটাবেসের ভার, TTL jitter, stampede আর Little-এর সূত্রে pool
- Flash sale-এর queue, backoff, rate limit, বাংলা SMS, পয়সা, JWT, idempotency, CLS, INP, error budget, Docker আর খরচ
১ · পাতার নিয়ম, ভাগের মানচিত্র আর খাতার কৌশল
মডিউল ১৪-এ স্বাগতম। মুদি দোকানের মালিক “ঈদে ভিড় হবে” বলে থামেন না; গুদামে কত বস্তা চাল, দিনে কত ক্রেতা, কাউন্টারে কতজন লোক লাগবে, সব গুনে দেখেন। ওয়েব অ্যাপের সিদ্ধান্তও এমন গোনার ওপর দাঁড়িয়ে: ছবিটি কত বড় হলে পাতা ধীর হবে, কতটি connection লাগবে, cache কত শতাংশ ধরলে ডেটাবেস বাঁচবে। এ ধরনের হিসাব বিশ্ববিদ্যালয়ের Web Engineering কোর্সে, চাকরির লিখিত পরীক্ষায় আর developer ইন্টারভিউতে প্রায়ই আসে। এই পাতা সেগুলো এক জায়গায় অনুশীলন করায়।
কোন ভাগে কী, কোন পর্ব ঝালাবেন
| ভাগ | কী আছে | মূল পর্ব |
|---|---|---|
| ২ | Download সময়, gzip, Brotli, code splitting, content hash, LCP-এর বাজেট | ২৩, ৭৯, ৮৪ |
| ৩ | State-এর ঠিকানা, memo, অস্থির prop, useDeferredValue-এ render গোনা | ১৪, ১৫, ১৬, ২৩ |
| ৪ | পাতার সংখ্যা, offset বনাম keyset, N+1-এর SQL আর round trip | ৩৪, ৪২ |
| ৫ | Hit rate আর ডেটাবেসের ভার, গড় latency, TTL jitter, stampede | ৪৪, ৮৭ |
| ৬ | DAU থেকে req/s, storage, Little-এর সূত্র, pool আর max_connections | ৩৮, ৮৭ |
| ৭ | Flash sale-এর queue, exponential backoff, jitter, rate limit-এর window | ৪৯, ৬২, ৮৭ |
| ৮ | বাংলা SMS-এর ভাগ, পয়সার rounding, JWT-এর ঘড়ি, idempotency | ৩২, ৩৩, ৪৬, ৬৩, ৭৩ |
| ৯ | CLS-এর session window, INP আর p75, error budget, Docker layer, খরচ | ৮০, ৮২, ৮৩, ৮৪ |
এই পাতার নিয়ম — প্রশ্নে অন্য কিছু না বললে
| বিষয় | নিয়ম |
|---|---|
| Byte আর bit | ফাইলের আকার byte-এ, নেটওয়ার্কের গতি bit-এ। সময় = byte × ৮ ÷ (bit/s) |
| kB আর KB | Build tool-এর মতো 1 kB = 1,000 byte. Storage-এর হিসাবে পর্ব ৮৭-এর মতো 1 GB = 1,024 × 1,024 KB |
| ধীর নেটওয়ার্ক | পর্ব ৮৪-এর Lighthouse-এর ধরা মান: 1.6384 Mbps (সেকেন্ডে 16,38,400 bit), RTT 150 ms |
| দিন আর বছর | দিন = ৮৬,৪০০ সেকেন্ড; বছর = ৩৬৫ দিন; মাস = ৩০ দিন |
| Percentile | Nearest-rank: সাজানো তালিকায় ⌈q × n⌉ নম্বর মান, পর্ব ৮৪-এর মতো |
| টাকা | হিসাব পয়সায় (integer), দেখানোর সময় টাকায়। সব দাম কাল্পনিক, কোনো প্রতিষ্ঠানের আসল দাম নয় |
| মানুষ, connection, server | ক্ষমতার উত্তর সবসময় ওপরের পূর্ণসংখ্যায় (⌈ ⌉); ৮.৩টি connection হয় না |
| মাপা সংখ্যা | পর্বগুলোর local run থেকে আসা সংখ্যা একটি কম্পিউটারের একটি run; অনুপাত শিক্ষণীয়, মান সাধারণ নিয়ম নয় |
১. দেওয়া তথ্য আর একক এক লাইনে। “৭২,০০০ byte, ১.৬৩৮৪ Mbps” লিখলেই ৮ দিয়ে গুণের কথা মনে পড়ে।
২. ধরে নেওয়া শর্ত লিখুন। “CPU আর TLS ধরা হয়নি, cache-এর পড়া সব একই রকম।” প্রশ্নে স্পষ্ট না থাকলে এটিই আপনার রক্ষাকবচ।
৩. সূত্র আগে, মান পরে। গণনায় ভুল হলেও সূত্র দেখে পরীক্ষক যুক্তি বুঝতে পারেন।
৪. উল্টো দিক থেকে মেলান। ৩,৫৮,৪০০ byte পেলে আবার ×৮ ÷ ১৬,৩৮,৪০০ করে দেখুন ১.৭৫ সেকেন্ড আসে কি না। এক লাইনের এই যাচাই অর্ধেক ভুল ধরে।
৫. উত্তরের অর্থ এক লাইনে বলুন। “Hit rate ৭৫%” লিখেই থামবেন না। বলুন, এর কম হলে ঈদের রাতে ডেটাবেস budget ছাড়াবে।
২ · Bundle, compression আর ধীর নেটওয়ার্ক — byte থেকে মিলিসেকেন্ড
ঝালিয়ে নিন: পর্ব ২৩ · React performance (code splitting, ধীর ফোনের বাজেট), পর্ব ৭৯ · Build আর bundle (chunk, content hash, gzip-এর আকার), পর্ব ৮৪ · Web performance (LCP, Brotli, cache header). নেটওয়ার্কের গভীরে যেতে Computer Networks.
নামার সময় = byte × ৮ ÷ (bit/s). একটি অনুরোধ ≈ RTT + নামার সময় (TCP slow start, TLS আর CPU এই মডেলে নেই)। সাশ্রয় = (আগে − পরে) ÷ আগে। LCP-এর বাজেট = ২,৫০০ ms − TTFB − RTT − অন্যান্য দেরি।
২.১(সহজ) বাজারঘরের প্রথম পাতার JavaScript bundle কাঁচা অবস্থায় ২,৪০,০০০ byte, gzip-এ ৭২,০০০ আর Brotli-তে ৬৬,০০০ byte. নেটওয়ার্ক ১.৬৩৮৪ Mbps, RTT ১৫০ ms. (ক) তিন রূপে শুধু নামতে কত সময়? (খ) gzip রূপে একটি অনুরোধের RTT যোগ করলে? (গ) Brotli gzip-এর চেয়ে কত শতাংশ ছোট, কত ms বাঁচায়? (ঝালিয়ে নিন: পর্ব ৭৯, ৮৪)
speed = 1.6384 Mbps = 1,638,400 bit/s
(a) raw : 240,000 x 8 = 1,920,000 bit / 1,638,400 = 1.1719 s = 1171.9 ms
gzip : 72,000 x 8 = 576,000 bit / 1,638,400 = 0.3516 s = 351.6 ms
brotli: 66,000 x 8 = 528,000 bit / 1,638,400 = 0.3223 s = 322.3 ms
(b) 351.6 + 150 = 501.6 ms
(c) (72,000 - 66,000) / 72,000 = 8.3% smaller
351.6 - 322.3 = 29.3 ms
check: 1,638,400 / 8 = 204,800 B/s, so 72,000 B is about 0.35 s
সবচেয়ে বড় লাভ কাঁচা থেকে gzip-এ (৭০% ছোট); gzip থেকে Brotli-তে লাভ ছোট কিন্তু বিনা খরচের। ফাঁদ: ৮ দিয়ে গুণ ভুলে যাওয়া, তাহলে উত্তর আট গুণ ছোট আসে। ১১৭১.৯, ৩৫১.৬ আর ৩২২.৩ ms; ৫০১.৬ ms; ৮.৩%, ২৯.৩ ms. বাস্তবে TCP slow start আর TLS handshake আরও কয়েকটি RTT যোগ করে, তাই এটি নিচের সীমা।
২.২(মাঝারি) বাজারঘরের chunk-গুলো gzip-এ: vendor ৬৮,০০০, main ১,২০,০০০, admin ১,৫০,০০০ আর cart ৬২,০০০ byte. নেটওয়ার্ক ১.৬৩৮৪ Mbps, RTT ১৫০ ms. (ক) সব এক bundle-এ হলে প্রথম পাতায় নামতে কত সময়? (খ) Code splitting-এ প্রথম পাতায় শুধু vendor আর main; সময় কত, কত শতাংশ কম? (গ) পরে কার্ট খুললে cart chunk আনতে (RTT-সহ) কত? (ঘ) একজন ক্রেতা সপ্তাহে ১০ বার আসেন; ৭৪,৪৬৩ byte-এর একটি chunk-এর নামে content hash আর immutable থাকলে, আর max-age=0 হলে (304-ও নেই) সপ্তাহে কত byte? (ঝালিয়ে নিন: পর্ব ২৩, ৭৯, ৮৪)
(a) all = 68,000 + 120,000 + 150,000 + 62,000 = 400,000 B
time = 400,000 x 8 / 1,638,400 = 1.953 s = 1953 ms
(b) first = 68,000 + 120,000 = 188,000 B -> 918 ms
saved = (1953 - 918) / 1953 = 53%
(c) cart = 62,000 x 8 / 1,638,400 = 303 ms, + 150 RTT = 453 ms
(d) immutable : 1 x 74,463 = 74,463 B
max-age=0 : 10 x 74,463 = 744,630 B
যাচাই: প্রথম পাতার byte মোটের ৪৭%, সময়ও তাই (৯১৮ ÷ ১,৯৫৩ = ০.৪৭)। Admin chunk সাধারণ ক্রেতা কখনো নামানই না, আর কার্টের ৪৫৩ ms অপেক্ষা আসে পরে, যখন ক্রেতা পাতাটি চান। ফাঁদ: chunk খুব ছোট করে ভাঙলে প্রতিটি নিজে নিজে সংকুচিত হয়, তাই মোট বাড়ে; পর্ব ৭৯-এর একটি run-এ ২০টি ছোট chunk-এ gzip-এর যোগফল এক ফাইলের ১২৬%. ১,৯৫৩ ms; ৯১৮ ms, ৫৩% কম; ৪৫৩ ms; ৭৪,৪৬৩ বনাম ৭,৪৪,৬৩০ byte.
২.৩(কঠিন) একটি পণ্যের পাতার TTFB ৬০০ ms. HTML আসার সাথে সাথে hero ছবির অনুরোধ যায় (একটি RTT, ১৫০ ms), নেটওয়ার্ক ১.৬৩৮৪ Mbps; render-blocking আর CPU-র সময় ধরবেন না। (ক) LCP ≤ ২.৫ s রাখতে ছবি সর্বোচ্চ কত byte? (খ) মাসে ৫ লাখ page view, প্রতিটিতে HTML ৯,০৪৮ byte; gzip-এ ৭৮১ আর Brotli-তে ৪৫৯ byte (পর্ব ৮৪-এর একটি run-এর মাপ)। Compression ছাড়ার তুলনায় মাসে কত MB বাঁচে (1 MB = 106 byte), আর Brotli gzip-এর ওপর আরও কত? (গ) বাংলা অক্ষর UTF-8-এ ৩ byte হলে ১,০০০ অক্ষরের বাংলা বিবরণ কাঁচা অবস্থায় কত byte? (ঝালিয়ে নিন: পর্ব ৮৪)
(a) budget = 2500 - 600 - 150 = 1750 ms
bytes = 1.75 s x 1,638,400 / 8 = 358,400 B (about 350 KiB)
check : 358,400 x 8 / 1,638,400 = 1.75 s
(b) gzip : (9,048 - 781) x 500,000 = 4,133,500,000 B = 4133.5 MB
brotli: (9,048 - 459) x 500,000 = 4294.5 MB
extra : (781 - 459) x 500,000 = 161.0 MB
(c) 1,000 x 3 = 3,000 B (English text: about 1,000 B)
উল্টো যাচাই: পর্ব ৮৪-এর অনুশীলনে ৫,০০,০০০ byte-এর ছবিতে LCP প্রায় ৩.২ s এসেছিল; সেটি ৩,৫৮,৪০০-এর বেশি, তাই ২.৫ s পেরোনোই প্রত্যাশিত। (খ)-তে বড় লাভ compression চালু করায়, Brotli সামান্য বাড়তি। (গ) বলে, বাংলা পাতায় compression না দিলে ক্ষতি ইংরেজির চেয়ে বেশি। ৩,৫৮,৪০০ byte; ৪,১৩৩.৫ MB আর ৪,২৯৪.৫ MB, Brotli আরও ১৬১ MB; ৩,০০০ byte.
// ধরে নেওয়া ধীর নেটওয়ার্ক: 1.6384 Mbps, RTT 150 ms
const BPS = 1638400, RTT = 150;
const ms = (bytes) => (bytes * 8) / BPS * 1000;
const show = (label, bytes) =>
console.log(label.padEnd(7), String(bytes).padStart(6), 'B', ms(bytes).toFixed(1).padStart(7), 'ms');
// ২.১: একটি JS bundle, তিন রূপে
show('raw', 240000); show('gzip', 72000); show('brotli', 66000);
console.log('gzip + 1 RTT =', (ms(72000) + RTT).toFixed(1), 'ms');
// ২.২: code splitting, সব আকার gzip-এ
const c = { vendor: 68000, main: 120000, admin: 150000, cart: 62000 };
const all = c.vendor + c.main + c.admin + c.cart, first = c.vendor + c.main;
console.log('one bundle', all, 'B', ms(all).toFixed(0), 'ms | first page', first, 'B', ms(first).toFixed(0), 'ms');
console.log('cart later', ms(c.cart).toFixed(0), 'ms + RTT =', (ms(c.cart) + RTT).toFixed(0), 'ms');
console.log('10 visits: immutable', 74463, 'B | max-age=0', 10 * 74463, 'B');
// ২.৩: LCP বাজেট থেকে ছবির সর্বোচ্চ আকার (TTFB 600 ms)
const budget = 2500 - 600 - RTT;
console.log('image budget', budget, 'ms -> max', Math.floor((budget / 1000) * BPS / 8), 'B');
const views = 500000, raw = 9048, gz = 781, br = 459;
const mb = (b) => ((b * views) / 1e6).toFixed(1) + ' MB';
console.log('per month saved: gzip', mb(raw - gz), '| brotli', mb(raw - br), '| br over gzip', mb(gz - br));gzip 72000 B 351.6 ms আর gzip + 1 RTT = 501.6 ms ২.১-এর সাথে মেলে। one bundle 400000 B 1953 ms | first page 188000 B 918 ms আর cart later 303 ms + RTT = 453 ms ২.২-এর। image budget 1750 ms -> max 358400 B ২.৩(ক), আর শেষ লাইন per month saved: gzip 4133.5 MB | brotli 4294.5 MB | br over gzip 161.0 MB ২.৩(খ)। BPS বদলে নিজের নেটওয়ার্কের গতি বসান; যেমন 10 Mbps-এ একই gzip bundle প্রায় ৫৮ ms.
৩ · React-এ render গোনা — memo, অস্থির prop আর state-এর ঠিকানা
ঝালিয়ে নিন: পর্ব ১৪ · Hook-এর নিয়ম আর custom hook (useMemo, useCallback), পর্ব ১৫ · Virtual DOM আর reconciliation, পর্ব ১৬ · State কোথায় রাখবেন (colocation), পর্ব ২৩ · React performance (memo, useDeferredValue).
State যে component-এ, তার update-এ সে আর তার নিচের সব component আবার render হয় (re-render). memo ছাড়া n টি child, m বার update: n × (১ + m). memo-সহ: n + (অস্থির prop-ওয়ালা child) × m. State নিচে নামালে (colocation) তালিকাটি শুধু প্রথম render-এ, n বার। এখানে StrictMode-এর দ্বিগুণ render গোনা হয়নি; সেটি শুধু development-এ।
৩.১(সহজ) একটি পাতায় ৪০টি পণ্যের Card আর পাশে কুপনের input. Card-এ memo নেই। কুপনের state দোকানের ওপরের component-এ থাকলে প্রতি অক্ষরে পুরো দোকান আবার render হয়। ক্রেতা “EID2026” (৭ অক্ষর) লিখলেন। প্রথম render-সহ Card মোট কতবার render হলো? State শুধু কুপনের component-এ নামালে কতবার? কত শতাংশ কাজ বাঁচল? (ঝালিয়ে নিন: পর্ব ১৬)
state high: 40 (first render) + 7 x 40 = 320 Card renders
state low : 40 (first render only) = 40 Card renders
saved : (320 - 40) / 320 = 87.5%
check (Part 16 run): 3 Cards, "EID" = 3 + 3 x 3 = 12, measured 12
পর্ব ১৬-এর Edge run-এ ঠিক এই সূত্রই মিলেছিল: ওপরে state রাখলে Card renders: 12, নিচে রাখলে 3. এখানে কোনো memo বা নতুন library লাগেনি, শুধু state-এর ঠিকানা বদলেছে। ৩২০ বনাম ৪০ বার; ৮৭.৫% কাজ কম।
৩.২(মাঝারি) একটি তালিকায় ২০টি memo করা Card. তার ৩টিতে inline style object যায়, যা প্রতি render-এ নতুন reference; বাকি ১৭টির সব prop স্থির। Parent প্রথম render-এর পরে ১০ বার update হলো। Card মোট কতবার render হবে: (ক) memo ছাড়া, (খ) এই অবস্থায়, (গ) তিনটির style বাইরে স্থির object করলে, (ঘ) সব ২০টিতেই অস্থির prop থাকলে? (ঝালিয়ে নিন: পর্ব ১৪, ২৩)
(a) no memo : 20 x (1 + 10) = 220
(b) 3 unstable : 20 + 3 x 10 = 50
(c) all stable : 20 + 0 x 10 = 20
(d) all unstable: 20 + 20 x 10 = 220 (memo compares, then renders anyway)
memo শুধু তখনই বাঁচায়, যখন সব prop আগের reference-এর সাথে Object.is-এ মেলে। (ঘ)-তে memo কিছুই বাঁচায় না, উল্টো প্রতিবার তুলনার সামান্য খরচ যোগ করে। যাচাইয়ের জন্য নিচের live React পরীক্ষায় ছোট সংখ্যা: ৪টি memo Card, তার ১টি অস্থির, ৫ বার update. সূত্রে memo-তে ৪ + ১ × ৫ = ৯, memo ছাড়া ৪ × ৬ = ২৪. ২২০; ৫০; ২০; ২২০.
<div id="root"></div>
<script src="../course-kit/vendor/react-lab.js"></script>
<script type="text/jsx">
let memoRenders = 0, plainRenders = 0, bump;
const MemoCard = memo(function MemoCard({ name, style }) {
memoRenders++;
return <li style={style}>{name}</li>;
});
function PlainCard({ name }) { plainRenders++; return <li>{name}</li>; }
const fixed = { padding: 4 }; // বাইরে: সবসময় একই object
const names = ['চাল', 'ডাল', 'তেল', 'চিনি'];
function Shop() {
const [n, setN] = useState(0);
bump = setN;
return <div><p>ঝুড়িতে {n}টি</p>
<ul>{names.map((x, i) => <MemoCard key={x} name={x}
style={i === 0 ? { padding: 4 } : fixed} />)}</ul>
<ul>{names.map((x) => <PlainCard key={x} name={x} />)}</ul></div>;
}
createRoot(document.getElementById('root')).render(<Shop />);
setTimeout(() => {
for (let v = 1; v <= 5; v++) ReactDOM.flushSync(() => bump(v));
console.log('updates 5 | memo cards', memoRenders, '| plain cards', plainRenders);
}, 50);
</script>আউটপুট updates 5 | memo cards 9 | plain cards 24: সূত্রের সাথে হুবহু মিলেছে। প্রথম memo Card-এর { padding: 4 } প্রতিটি parent render-এ নতুন object, তাই সে ৬ বার render হয়েছে; বাকি তিনটি ১ বার করে, মোট ৯. এটি production React (react-lab.js), তাই StrictMode-এর দ্বিগুণ render নেই। { padding: 4 }-কে fixed করে আবার চালান: memo-র সংখ্যা ৪ হবে। flushSync এখানে শুধু প্রতিটি update আলাদা দেখানোর জন্য।
৩.৩(মাঝারি) ৪০০ পণ্যের তালিকা, প্রতিটি Row render-এ ০.৫ ms লাগে (ধরে নেওয়া), তালিকাটি memo করা। ক্রেতা এক টানে “c”, “ch”, “chi”, “chil” লিখলেন; প্রতিটিতে যথাক্রমে ২০০, ১২০, ৩০ আর ৬টি পণ্য মেলে। (ক) সাধারণ পথে মোট কতটি Row render, কত ms? (খ) useDeferredValue-এ আদর্শ ক্ষেত্রে, যখন শুধু শেষ মানের তালিকা render হয়? (গ) প্রথম অক্ষরের render একাই কত ms, আর INP-এর “ভালো” সীমা ২০০ ms-এর সাথে তুলনা কী বলে? (ঝালিয়ে নিন: পর্ব ২৩)
(a) normal : 200 + 120 + 30 + 6 = 356 rows x 0.5 = 178 ms
(b) deferred: 6 rows x 0.5 = 3 ms (urgent renders skip the memo list)
(c) first key alone: 200 x 0.5 = 100 ms of rendering
check (Part 23 run): 50 + 50 + 25 = 125 rows vs 25 rows
(গ)-র ১০০ ms শুধু render-এর কাজ; input delay আর পর্দায় আঁকার সময় যোগ হলে চারটি অক্ষর এক টানে ১৭৮ ms ধরে main thread আটকে রাখে, যা ২০০ ms-এর খুব কাছে, আর ধীর ফোনে তার বেশি। পর্ব ২৩-এর run-এ একই ধরনে সাধারণ Row render: 125 আর deferred Row render: 25 এসেছিল। আসল টাইপিংয়ে ফাঁকে ফাঁকে কিছু মাঝের মানও render হতে পারে, তাই (খ) আদর্শ সীমা। ৩৫৬টি, ১৭৮ ms; ৬টি, ৩ ms; ১০০ ms.
// ৩.১: state-এর ঠিকানা: n টি Card, k বার টাইপ
const placement = (n, k) => ({ stateHigh: n + k * n, stateLow: n });
console.log('40 cards, 7 keys:', JSON.stringify(placement(40, 7)));
// ৩.২: memo — cards টি Card, তার u টিতে অস্থির prop, parent m বার update
const memoCount = (cards, u, m) => ({ noMemo: cards * (1 + m), memo: cards + u * m });
console.log('20 cards, 3 unstable, 10 updates:', JSON.stringify(memoCount(20, 3, 10)));
console.log('20 cards, 0 unstable, 10 updates:', JSON.stringify(memoCount(20, 0, 10)));
console.log('20 cards, 20 unstable, 10 updates:', JSON.stringify(memoCount(20, 20, 10)));
// ৩.৩: ভারী তালিকা — প্রতিটি অক্ষরে কতটি সারি মেলে, প্রতি Row 0.5 ms
const matches = [200, 120, 30, 6], COST = 0.5;
const normal = matches.reduce((a, b) => a + b, 0);
console.log('normal rows', normal, '->', normal * COST, 'ms');
console.log('deferred rows', matches.at(-1), '->', matches.at(-1) * COST, 'ms (best case)');
console.log('worst single keystroke', matches[0] * COST, 'ms');40 cards, 7 keys: {"stateHigh":320,"stateLow":40} ৩.১-এর। ৩.২-এর চারটি অবস্থা তিন লাইনে: {"noMemo":220,"memo":50}, সব স্থির হলে "memo":20, আর সব অস্থির হলে "memo":220, অর্থাৎ memo ছাড়ার সমান। শেষ তিন লাইন ৩.৩: normal rows 356 -> 178 ms, deferred rows 6 -> 3 ms (best case) আর worst single keystroke 100 ms.
৪ · Pagination আর N+1 — কতটি সারি, কতটি SQL, কত round trip
ঝালিয়ে নিন: পর্ব ৩৪ · Pagination, filter আর sort (offset, cursor, limit + 1), পর্ব ৪২ · N+1 সমস্যা (include, select, query budget). Index-এর তত্ত্ব DBMS · Indexing-এ।
পাতা = ⌈N ÷ L⌉; শেষ পাতা = N − (পাতা − ১) × L. Offset: পাতা p-তে OFFSET = (p − ১) × L, ডেটাবেস ছোঁয় p × L সারি; সব পাতা ঘুরলে L × P(P + ১) ÷ ২. Keyset: প্রতি পাতায় L + ১. N+1: ১ + (সম্পর্ক) × N টি SQL; সময় ≈ trip × RTT + SQL × কাজ।
৪.১(সহজ) বাজারঘরে ৪৮,৩১৭টি পণ্য, প্রতি পাতায় ২০টি। (ক) মোট কয়টি পাতা, শেষ পাতায় কয়টি পণ্য? (খ) page = ১০০০-এ OFFSET কত, আর ডেটাবেস কতটি সারি ছোঁয়? (গ) Keyset-এ একই গভীরতায় কতটি সারি পড়ে (limit + 1 নিয়মে)? (ঘ) API-তে page ≤ ১০০০ আর limit ≤ ১০০ সীমা থাকলে সবচেয়ে গভীর OFFSET কত? (ঝালিয়ে নিন: পর্ব ৩৪)
(a) pages = ceil(48,317 / 20) = ceil(2415.85) = 2416
last = 48,317 - 2415 x 20 = 48,317 - 48,300 = 17
(b) OFFSET = (1000 - 1) x 20 = 19,980
rows touched = 19,980 + 20 = 20,000
(c) keyset: WHERE (created_at, id) < (?, ?) LIMIT 21 -> 21 rows
(d) (1000 - 1) x 100 = 99,900
যাচাই: ২,৪১৫ পূর্ণ পাতা × ২০ + ১৭ = ৪৮,৩১৭. ফাঁদ: ⌊ ⌋ নিয়ে ২,৪১৫ বলা, তখন শেষ ১৭টি পণ্য কোনো পাতায় থাকে না। Limit + 1-এর বাড়তি সারিটি শুধু “আরও আছে কি না” জানায়, COUNT ছাড়াই। ২,৪১৬টি পাতা, শেষটিতে ১৭; ১৯,৯৮০, ছোঁয় ২০,০০০ সারি; ২১টি; ৯৯,৯০০.
৪.২(মাঝারি) একটি export job ১০,০০০টি সারি পাতা ধরে পড়ে, প্রতি পাতায় ১০০টি। Offset-এ পাতা p পড়তে p × ১০০টি সারি ছুঁতে হয়; keyset-এ প্রতি পাতায় limit + 1 (শেষ পাতায় বাড়তি সারি থাকে না)। (ক) পুরো export-এ offset-এ মোট কতটি সারি ছোঁয়া হয়? (খ) Keyset-এ? (গ) প্রতি পাতায় ২০টি করে পড়লে offset-এ? (ঘ) পড়ার মাঝখানে তালিকার ওপরে একটি নতুন পণ্য ঢুকলে offset-এ কী ঘটে? (ঝালিয়ে নিন: পর্ব ৩৪)
(a) P = 10,000 / 100 = 100 pages
rows = 100 x (1 + 2 + ... + 100) = 100 x 100 x 101 / 2 = 505,000
ratio = 505,000 / 10,000 = 50.5 times the data
(b) keyset = 100 x 101 - 1 = 10,099
(c) P = 500: 20 x 500 x 501 / 2 = 2,505,000 (250.5 times)
(d) every later page shifts by one row: one row is read twice
শিক্ষা: offset-এর মোট খরচ পাতার সংখ্যার বর্গের সাথে বাড়ে, তাই ছোট পাতায় ভাগ করলে উল্টো খারাপ হয়, ৫০.৫ থেকে ২৫০.৫ গুণ। Keyset প্রায় ঠিক ডেটার আকার পড়ে। (ঘ)-র duplicate পর্ব ৩৪-এর drift.js-এ দেখা গেছে: twice [7]; মুছে গেলে একটি সারি হারায়। ৫,০৫,০০০; ১০,০৯৯; ২৫,০৫,০০০; একটি সারি দুবার।
৪.৩(কঠিন) “আমার অর্ডার” পাতায় N = ২০টি অর্ডার। লুপে প্রতিটি অর্ডারের customer আর items আলাদা SQL-এ আসে। ধরে নিন RTT ১০ ms, প্রতি SQL-এ ডেটাবেসের কাজ ০.৩ ms, আর একসাথে পাঠানো SQL এক round trip-এ যায়। (ক) লুপে কতটি SQL, কত ms? (খ) include-এর query strategy-র মতো: আগে অর্ডার, তারপর customers আর items দুটি IN-query একসাথে? (গ) একটিই join SQL? (ঘ) N = ১০০ হলে তিনটির কী হয়? (ঙ) ২৫টি অর্ডার, প্রতিটিতে ৪টি item; লুপে customer, items, প্রতিটি item-এর product আর সেই product-এর category আলাদা আনলে কতটি SQL? প্রতিটি সম্পর্কের স্তরে একটি করে SQL হলে কতটি, আর query budget ৫-এ কোনটি পাস করে? (ঝালিয়ে নিন: পর্ব ৪২)
(a) SQL = 1 + 2 x 20 = 41, trips = 41
time = 41 x 10 + 41 x 0.3 = 410 + 12.3 = 422.3 ms
(b) trips = 2, SQL = 3: 2 x 10 + 3 x 0.3 = 20.9 ms
(c) trips = 1, SQL = 1: 10 + 0.3 = 10.3 ms
(d) N = 100: loop 201 SQL = 2070.3 ms; IN and join unchanged
(e) loop = 1 + 25 + 25 + 25 x 4 + 25 x 4 = 251 SQL
level = orders, customers, items, products, categories = 5 SQL
budget 5: only the per-level version passes
যাচাই: পর্ব ৪২-এর মাপা run-এ লুপ ৪৪০.৩ ms এসেছিল, মডেলের ৪২২.৩-এর কাছে। লুপের সময় N-এর সাথে বাড়ে, IN আর join-এর বাড়ে না; এই মডেল বড় ফল টেনে আনার খরচ ধরে না। ফাঁদ: “এক SQL সবসময় দ্রুত” ভাবা; index ছাড়া join-ও ধীর হতে পারে, তাই মাপুন। ৪১টি, ৪২২.৩ ms; ২০.৯ ms; ১০.৩ ms; ২০১টি, ২,০৭০.৩ ms; ২৫১ বনাম ৫, শুধু দ্বিতীয়টি পাস।
// ৪.১: পাতার সংখ্যা, শেষ পাতা আর offset
const N = 48317, L = 20;
const pages = Math.ceil(N / L), last = N - (pages - 1) * L, offset = (1000 - 1) * L;
console.log('pages', pages, '| last page', last, '| page 1000 offset', offset,
'| rows read', offset + L, '| keyset reads', L + 1);
console.log('deepest offset (page<=1000, limit<=100):', (1000 - 1) * 100);
// ৪.২: সব পাতা ঘোরা — offset-এ পাতা p পড়ে p × L সারি, keyset-এ L + 1
function walk(n, l) {
const P = Math.ceil(n / l);
let off = 0;
for (let q = 1; q <= P; q++) off += Math.min(q * l, n);
return { pages: P, offsetRows: off, keysetRows: n + (P - 1), ratio: +(off / n).toFixed(1) };
}
console.log('10000 rows, 100/page:', JSON.stringify(walk(10000, 100)));
console.log('10000 rows, 20/page:', JSON.stringify(walk(10000, 20)));
// ৪.৩: N+1 — RTT 10 ms, প্রতি SQL-এ ডেটাবেসের কাজ 0.3 ms
const RTT = 10, WORK = 0.3;
const t = (trips, sql) => (trips * RTT + sql * WORK).toFixed(1);
for (const n of [20, 100]) {
const loop = 1 + 2 * n;
console.log(`N=${n}: loop ${loop} SQL ${t(loop, loop)} ms | IN 3 SQL ${t(2, 3)} ms | join 1 SQL ${t(1, 1)} ms`);
}
// ৪.৪: ২৫টি অর্ডার, প্রতিটিতে ৪টি item; লুপে customer, items, product, category
const o = 25, k = 4;
console.log('nested loop SQL:', 1 + o + o + o * k + o * k, '| one SQL per level:', 1 + 4);প্রথম লাইন ৪.১: pages 2416 | last page 17 | page 1000 offset 19980 | rows read 20000 | keyset reads 21. ৪.২-এর দুই লাইনে "offsetRows":505000,"keysetRows":10099,"ratio":50.5 আর ২০ করে পড়লে "ratio":250.5. ৪.৩-এ N=20: loop 41 SQL 422.3 ms | IN 3 SQL 20.9 ms | join 1 SQL 10.3 ms, N=100: loop 201 SQL 2070.3 ms, আর শেষে nested loop SQL: 251 | one SQL per level: 5. RTT-কে ৪০ করে চালালে লুপের সময় কয়েক গুণ বাড়ে, যা আলাদা server-এ ডেটাবেস থাকলে ঘটে।
৫ · Cache — hit rate, ডেটাবেসের ভার, TTL jitter আর stampede
ঝালিয়ে নিন: পর্ব ৪৪ · Cache — Redis, cache-aside আর invalidation, পর্ব ৮৭ · একটি অ্যাপ বড় করা (hit rate কত হলে database বাঁচে)। কৌশলের তত্ত্ব System Design · Caching strategies আর eviction ও invalidation-এ।
ডেটাবেসের ভার = peak × (১ − h). দরকারি hit rate = ১ − budget ÷ peak. গড় latency = h × hit-এর সময় + (১ − h) × miss-এর সময়। TTL jitter ±j: মেয়াদ ছড়ায় ২ × TTL × j সেকেন্ডে। Stampede: পাহারা ছাড়া ডেটাবেসে যায় req/s × নতুন মান বানানোর সময়।
৫.১(সহজ) ঈদের রাতে peak ১,৮০০ req/s, প্রতিটি অনুরোধে ২টি এমন SQL যা cache করা যায়। ডেটাবেস আরামে ৯০০ SQL/s নিতে পারে (load test থেকে ধরা)। (ক) Cache ছাড়া কত SQL/s, budget-এর কত গুণ? (খ) কমপক্ষে কত hit rate লাগবে? (গ) ৯০, ৯৫ আর ৯৯%-এ ডেটাবেসে কত SQL/s যায়? (ঘ) ৯৫% থেকে ৯৯%-এ গেলে ডেটাবেসের পড়া কত ভাগ হয়? (ঝালিয়ে নিন: পর্ব ৪৪, ৮৭)
(a) 1,800 x 2 = 3,600 SQL/s = 4 x budget
(b) h = 1 - 900 / 3,600 = 1 - 0.25 = 75%
(c) 90%: 3,600 x 0.10 = 360
95%: 3,600 x 0.05 = 180
99%: 3,600 x 0.01 = 36
(d) miss 0.05 -> 0.01: 0.01 / 0.05 = 1/5
check: 3,600 x (1 - 0.75) = 900 = budget
ডেটাবেস দেখে শুধু miss, তাই hit rate-এর শেষ কয়েক শতাংশ ভারে বিশাল পার্থক্য আনে। উল্টো দিকটিও লিখুন: Redis restart বা deploy-এ key-এর নাম বদলালে cache খালি, আর সেই মুহূর্তে ডেটাবেস পুরো ৩,৬০০ SQL/s দেখবে। ৩,৬০০, চার গুণ; ৭৫%; ৩৬০, ১৮০, ৩৬; পাঁচ ভাগের এক ভাগ।
একই ভিড়, ভিন্ন hit rate — ডেটাবেসে কত SQL/s পৌঁছায়
DB load at peak 3,600 SQL/s — numbers for problem ৫.১
৫.২(মাঝারি) একটি cache-aside পথে Redis-এ hit হলে ১ ms. Miss হলে Redis দেখা ১ ms + ডেটাবেস ২০ ms + Redis-এ লেখা ১ ms. Cache একদম না থাকলে সরাসরি ডেটাবেসে ২০ ms. (ক) Hit rate ০%, ৮০% আর ৯৫%-এ গড় সময় কত? (খ) কত hit rate-এর ওপরে cache গড়ে লাভ দেয়? (গ) একটি load test-এ ১,২৩,৬৮৯টি অনুরোধের ২০,৬৮৩টি ডেটাবেসে গেল; hit rate কত? (ঝালিয়ে নিন: পর্ব ৪৪, ৮৭)
miss = 1 + 20 + 1 = 22 ms
(a) avg = h x 1 + (1 - h) x 22
h = 0 : 22.00 ms (worse than no cache!)
h = 0.80: 0.8 + 4.4 = 5.20 ms
h = 0.95: 0.95 + 1.1 = 2.05 ms
(b) 22 - 21h < 20 -> h > 2/21 = 9.5%
(c) h = 1 - 20,683 / 123,689 = 83.3%
(গ)-র সংখ্যা পর্ব ৮৭-এর একটি আসল load test-এর, যেখানে index থাকার পর cache ডেটাবেসের query ৮০% কমিয়েছিল। ফাঁদ: গড় দেখে খুশি হওয়া। প্রতি miss এখনো ২২ ms, তাই নতুন ক্রেতা আর মেয়াদ শেষ হওয়া key-এর p99 বদলায় না; ধীর query-তে cache সমস্যা লুকায়, সারায় না। ২২.০০, ৫.২০ আর ২.০৫ ms; ৯.৫%-এর ওপরে; ৮৩.৩%.
৫.৩(কঠিন) (ক) Deploy-এর পরে ১,০০০টি key একই মুহূর্তে লেখা হলো, TTL ৩০০ s. মেয়াদ শেষে প্রতিটি key ডেটাবেসে একবার যায়। ১০ সেকেন্ডের ঘর ধরে, jitter ছাড়া চূড়ায় সেকেন্ডে কতটি reload? ±১০% আর ±২০% jitter-এ মেয়াদ সমানভাবে ছড়ালে গড়ে কত? (খ) একটি জনপ্রিয় ক্যাটাগরির key-তে ৩০০ req/s আসে; মেয়াদ শেষে নতুন মান বানাতে ০.৮ s লাগে। ৩টি server-এ ২টি করে process. কোনো পাহারা না থাকলে ঐ ০.৮ s-এ ডেটাবেসে কতবার একই query যায়? প্রতি process-এ single-flight দিলে? Redis-এ ভাগ করা lock দিলে? Lock-এ কতজন অপেক্ষা করেন, সর্বোচ্চ কতক্ষণ? (ঝালিয়ে নিন: পর্ব ৪৪)
(a) no jitter: 1,000 keys in one 10 s bin = 100 reloads/s
+-10%: spread = 2 x 300 x 0.10 = 60 s -> 6 bins
1,000 / 6 = 166.7 per bin = 16.7 reloads/s
+-20%: spread = 120 s -> 12 bins -> 83.3 per bin = 8.3 reloads/s
(b) no guard : 300 x 0.8 = 240 DB reads
single-flight/process: 3 x 2 = 6 DB reads
shared Redis lock : 1 DB read; 239 requests wait up to 800 ms
যাচাই (ক): পর্ব ৪৪-এর seeded run-এ ±১০%-এ চূড়া ছিল ১৮০টি key, গড় ১৬৬.৭-এর একটু ওপরে, কারণ এলোমেলো মান হুবহু সমান ছড়ায় না। (খ)-তে single-flight একটি process-এর Map-এ থাকে, তাই process যত, ডেটাবেসে ঢেউ তত; পর্ব ৪৪-এর মাপে ২টি instance × ৪ = ৮ এসেছিল। Stale-while-revalidate-এ একজন পেছনে নতুন মান বানান, কেউ অপেক্ষা করেন না, কিন্তু সর্বোচ্চ ০.৮ s পুরোনো মান দেখেন। ১০০, ১৬.৭ আর ৮.৩ reload/s; ২৪০, ৬ আর ১; ২৩৯ জন, সর্বোচ্চ ৮০০ ms.
// ৫.১: ডেটাবেসের budget থেকে দরকারি hit rate
const peakSql = 1800 * 2, budget = 900;
console.log('peak SQL/s', peakSql, '| needed hit rate', ((1 - budget / peakSql) * 100).toFixed(1) + '%');
for (const h of [0.5, 0.75, 0.9, 0.95, 0.99])
console.log(` hit ${(h * 100).toFixed(0)}% -> DB ${(peakSql * (1 - h)).toFixed(0)} SQL/s`);
// ৫.২: গড় latency — hit 1 ms; miss 1 + 20 + 1 ms; cache ছাড়া 20 ms
const avg = (h) => h * 1 + (1 - h) * 22;
console.log('avg at hit 0%, 80%, 95%:', [0, 0.8, 0.95].map((h) => avg(h).toFixed(2)).join(', '), 'ms');
console.log('break-even hit rate vs 20 ms:', (2 / 21 * 100).toFixed(1) + '%');
console.log('lab run D hit rate:', ((1 - 20683 / 123689) * 100).toFixed(1) + '%');
// ৫.৩: ১,০০০টি key একসাথে লেখা, TTL 300 s, ১০ সেকেন্ডের ঘর
for (const j of [0, 0.1, 0.2]) {
const spread = 2 * 300 * j, bins = Math.max(1, spread / 10);
console.log(`jitter +-${j * 100}%: ${spread} s, ${bins} bins, ` +
`${(1000 / bins).toFixed(1)} keys/bin = ${(1000 / bins / 10).toFixed(1)} reloads/s`);
}
// ৫.৪: stampede — একটি key-তে 300 req/s, নতুন করে বানাতে 0.8 s
const rps = 300, rebuild = 0.8, processes = 6;
console.log('no guard', rps * rebuild, 'DB reads | single-flight per process', processes, '| shared lock', 1);
console.log('wait for lock holders:', rps * rebuild - 1, 'requests wait up to', rebuild * 1000, 'ms');peak SQL/s 3600 | needed hit rate 75.0% আর পাঁচটি সারি (hit 95% -> DB 180 SQL/s ইত্যাদি) ৫.১-এর, ছবি ৮৯.১-এর দণ্ডও এই সংখ্যায়। avg at hit 0%, 80%, 95%: 22.00, 5.20, 2.05 ms, break-even hit rate vs 20 ms: 9.5% আর lab run D hit rate: 83.3% ৫.২-এর। Jitter-এর তিন লাইন আর no guard 240 DB reads | single-flight per process 6 | shared lock 1 ৫.৩-এর। budget বা processes বদলে নিজের ব্যবস্থা মিলিয়ে নিন।
৬ · Capacity — দৈনিক ব্যবহারকারী থেকে req/s, GB আর connection
ঝালিয়ে নিন: পর্ব ৩৮ · ORM-এর ধারণা (connection pool, ⌈N ÷ max⌉ × s), পর্ব ৮৭ · একটি অ্যাপ বড় করা (capacity আর Little-এর সূত্র)। পদ্ধতির পুরো তত্ত্ব System Design · Estimation-এ।
গড় req/s = DAU × প্রতি জনের দৈনিক অনুরোধ ÷ ৮৬,৪০০. Peak = গড় × peak factor. Little-এর সূত্র: L = λ × W, অর্থাৎ ব্যস্ত connection = SQL/s × একটি SQL-এর সেকেন্ড। সীমা: server × process × pool ≤ max_connections − সংরক্ষিত। Pool-এ অপেক্ষা: N টি একই সময়ের query শেষ হয় ⌈N ÷ max⌉ × s-এ।
৬.১(সহজ) বাজারঘরের দৈনিক ব্যবহারকারী (DAU) ২ লাখ, প্রতি জন দিনে ২৫টি অনুরোধ করেন, peak factor ৮, প্রতি অনুরোধে ৩টি SQL. (ক) গড় আর peak req/s, peak SQL/s কত? (খ) ঈদের রাতে ভিড় গড়ের ২০ গুণ হলে? (গ) প্রতি জন দিনে গড়ে ০.০৪টি অর্ডার দেন, প্রতিটি অর্ডারের সারি, item আর index মিলিয়ে ৪ KB. বছরে কতটি অর্ডার আর কত GB (1 GB = 1,024 × 1,024 KB)? (ঝালিয়ে নিন: পর্ব ৮৭)
(a) avg = 200,000 x 25 / 86,400 = 5,000,000 / 86,400 = 57.87 req/s
peak = 57.87 x 8 = 463.0 req/s
SQL = 463.0 x 3 = 1,389 SQL/s
(b) Eid = 57.87 x 20 = 1,157 req/s
(c) orders = 200,000 x 0.04 x 365 = 2,920,000 per year
size = 2,920,000 x 4 KB = 11,680,000 KB / 1,048,576 = 11.14 GB
check: 463 / 8 x 86,400 = 5,000,000 requests a day
শিক্ষা: “২ লাখ ব্যবহারকারী” শুনতে বড়, কিন্তু peak-এ মাত্র ৪৬৩ req/s, যা ঠিকঠাক index-ওয়ালা একটি ছোট ব্যবস্থাই সামলাতে পারে। নকশা হয় গড়ের জন্য নয়, peak-এর জন্য, আর বাংলাদেশের দোকানে সবচেয়ে বড় peak আসে উৎসবে। ৫৭.৮৭ আর ৪৬৩.০ req/s, ১,৩৮৯ SQL/s; ১,১৫৭ req/s; বছরে ২৯.২ লাখ অর্ডার, ১১.১৪ GB.
৬.২(মাঝারি) ৬.১-এর peak ১,৩৮৯ SQL/s, প্রতিটি SQL connection ৬ ms ধরে রাখে। (ক) গড়ে কতটি connection ব্যস্ত? দুই গুণ headroom রেখে মোট pool কত, আর ৩টি server × ২টি process-এ প্রতিটির pool কত? (খ) Index হারিয়ে একই query ৬০ ms হলে? (গ) PostgreSQL-এর ডিফল্ট max_connections ১০০, তার ৩টি superuser-এর জন্য। ৩ × ২ × pool ১০, ৩ × ৪ × pool ১০, আর serverless-এ ২৫টি কপি × pool ৫: কোনগুলো চলবে? (ঘ) Pool ৫-এ একসাথে ২০টি ১০০ ms-এর query এলে শেষটি কখন শেষ হয়? Pool ২-এ? (ঝালিয়ে নিন: পর্ব ৩৮, ৮৭)
(a) L = 1,389 x 0.006 = 8.3 busy connections
pool = ceil(8.3 x 2) = 17 in total
per process = ceil(17 / 6) = 3
(b) L = 1,389 x 0.060 = 83.3 -> 2x = 167 (> 97, will not fit)
(c) limit = 100 - 3 = 97
3 x 2 x 10 = 60 -> OK
3 x 4 x 10 = 120 -> too many
25 x 1 x 5 = 125 -> too many ("sorry, too many clients already")
(d) pool 5: ceil(20 / 5) x 100 = 400 ms
pool 2: ceil(20 / 2) x 100 = 1000 ms
যাচাই: পর্ব ৮৭-এর load test-এ ৫০টি সংযোগে গড়ে ২৯১ req/s এসেছিল, তাই W = ৫০ ÷ ২৯১ = ১৭২ ms, মাপা p50 ১৪২ ms-এর কাছে। (খ)-র শিক্ষা: ব্যবহারকারী নয়, query-র সময়ই pool-এর আকার ঠিক করে; আগে index, তারপর pool. (গ)-তে আরও process এলে pooler (যেমন PgBouncer) লাগে। (ঘ)-তে পর্ব ৩৮-এর আসল run-এ মাপা সময় সূত্রের চেয়ে একটু বেশি, কারণ সংযোগ খুলতেও সময় লাগে। ৮.৩টি, মোট ১৭, প্রতিটিতে ৩; ৮৩.৩টি, জায়গা হবে না; শুধু প্রথমটি চলে; ৪০০ আর ১,০০০ ms.
// ৬.১: DAU থেকে peak req/s, SQL আর storage (সব সংখ্যা ধরে নেওয়া)
const DAU = 200000, REQ = 25, PEAK = 8, SQL = 3;
const avg = (DAU * REQ) / 86400, peak = avg * PEAK, sql = Math.round(peak * SQL);
console.log('avg', avg.toFixed(2), 'req/s | peak', peak.toFixed(1), 'req/s | peak SQL/s', sql);
console.log('Eid (20x avg):', (avg * 20).toFixed(0), 'req/s');
const orders = DAU * 0.04 * 365;
console.log('orders/yr', orders, '| GB/yr at 4 KB', ((orders * 4) / 1024 / 1024).toFixed(2));
// ৬.২: Little-এর সূত্র L = λ × W
const busy = (perSec, ms) => (perSec * ms) / 1000;
const L = busy(sql, 6), pool = Math.ceil(L * 2);
console.log('busy', L.toFixed(1), '| total pool 2x', pool, '| per process (3 x 2)', Math.ceil(pool / 6));
console.log('same load, 60 ms query: busy', busy(sql, 60).toFixed(1), '| 2x', Math.ceil(busy(sql, 60) * 2));
const LIMIT = 100 - 3;
for (const [s, p, size] of [[3, 2, 10], [3, 4, 10], [25, 1, 5]])
console.log(`${s} x ${p} x pool ${size} = ${s * p * size} -> ${s * p * size <= LIMIT ? 'OK' : 'too many'}`);
console.log('20 queries x 100 ms, pool 5:', Math.ceil(20 / 5) * 100, 'ms; pool 2:', Math.ceil(20 / 2) * 100, 'ms');
console.log('lab check W = L / lambda:', ((50 / 291) * 1000).toFixed(0), 'ms');avg 57.87 req/s | peak 463.0 req/s | peak SQL/s 1389, Eid (20x avg): 1157 req/s আর orders/yr 2920000 | GB/yr at 4 KB 11.14 ৬.১-এর। busy 8.3 | total pool 2x 17 | per process (3 x 2) 3 আর same load, 60 ms query: busy 83.3 | 2x 167 ৬.২(ক) আর (খ)। তিনটি সীমার লাইনে 60 -> OK, 120 -> too many, 125 -> too many, তারপর 400 ms; pool 2: 1000 ms, আর শেষে ল্যাবের যাচাই 172 ms.
৭ · Queue, retry আর rate limit — ঢেউ সামলানো
ঝালিয়ে নিন: পর্ব ৪৯ · Account-এর কাজ (sliding window, fixed window), পর্ব ৬২ · Background job (BullMQ, backoff, jitter), পর্ব ৮৭ (ঈদের সেলের queue). তত্ত্ব System Design · Message queues আর Rate limiting-এ।
Queue বাড়ে (আগমন − worker-এর গতি) হারে, কমে (worker − আগমন) হারে। নতুন জনের অপেক্ষা ≈ queue ÷ worker-এর গতি। Exponential backoff: n-তম অপেক্ষা = delay × ২n−১, মোট = delay × (২k − ১). Jitter j: প্রতি অপেক্ষা [max × (১ − j), max) পরিসরে। Fixed window-এ সীমানার দুই পাশে সবচেয়ে খারাপ ক্ষেত্রে ২ × সীমা; sliding window-এ সীমাই।
৭.১(কঠিন) ঈদের সেলে অর্ডার একটি queue-তে যায়, worker সেকেন্ডে ২৫০টি শেষ করে, queue-তে সর্বোচ্চ ৪,০০০টি জমতে পারে; ভরে গেলে নতুন অর্ডার 429 পায় (load shedding). স্বাভাবিক সময়ে সেকেন্ডে ১০০টি অর্ডার, সেল চলে t = ১০ থেকে ২১ সেকেন্ড (১২ সেকেন্ড), সেকেন্ডে ৭০০টি। প্রতি সেকেন্ডে আগে জায়গা অনুযায়ী অর্ডার নেওয়া হয়, তারপর worker ২৫০টি পর্যন্ত শেষ করে। (ক) t = ১৪-এর শেষে queue কত, নতুন ক্রেতার অপেক্ষা কত? (খ) প্রথম 429 কখন? (গ) মোট কতজন 429 পেলেন? (ঘ) Queue কখন খালি হয়? (ঙ) সীমা না থাকলে চূড়া কত, খালি হতে কত সেকেন্ড, সবচেয়ে খারাপ অপেক্ষা কত? (ঝালিয়ে নিন: পর্ব ৬২, ৮৭)
net during the sale = 700 - 250 = +450 per second
(a) t = 10..14 is 5 s: 5 x 450 = 2,250, wait = 2,250 / 250 = 9.0 s
(b) end of t = 17: 8 x 450 = 3,600
t = 18: room = 4,000 - 3,600 = 400, so 300 get 429
(c) t = 19, 20, 21: room = 250 each, 450 rejected each
total = 300 + 3 x 450 = 1,650
(d) from 3,750 at t = 21, drain 250 - 100 = 150/s
3,750 / 150 = 25 s -> empty at t = 46
(e) no cap: 12 x 450 = 5,400, drain 5,400 / 150 = 36 s,
worst wait 5,400 / 250 = 21.6 s
যাচাই: মোট আগমন ১০ × ১০০ + ১২ × ৭০০ + ৬৮ × ১০০ = ১৬,২০০ (৯০ সেকেন্ডের মডেলে), আর চেকারে done 14550 | rejected 1650, যোগফল ১৬,২০০. Database কোনো মুহূর্তে সেকেন্ডে ২৫০-এর বেশি অর্ডার দেখেনি। সীমা না থাকলে কেউ 429 পেতেন না, কিন্তু শেষের ক্রেতা ২১.৬ সেকেন্ড ঝুলে থাকতেন, আর queue-এর মেমরি বাড়ত। ২,২৫০, ৯.০ s; t = ১৮; ১,৬৫০ জন; t = ৪৬; ৫,৪০০, ৩৬ s, ২১.৬ s.
in = 100/s, worker = 250/s
queue = 0
wait = 0 s
in = 700/s, net = +450/s
queue = 5 x 450 = 2,250
wait = 2,250 / 250 = 9.0 s
2,700 -> 3,150 -> 3,600
rejected = 0
wait = 14.4 s
t=18: room 400, 300 rejected
t=19..21: room 250, 450 rejected each
rejected = 1,650
queue after work = 3,750
in = 100/s, net = -150/s
3,750 / 150 = 25 s
empty at t = 46
arrived = 16,200 (in 90 s)
done = 14,550
rejected = 1,650
DB never above 250 orders/s
- স্বাভাবিক সন্ধ্যা। সেকেন্ডে ১০০টি অর্ডার আসে, worker ২৫০টি পর্যন্ত পারে, তাই queue খালি আর কেউ অপেক্ষা করেন না।
- সেল শুরু। সেকেন্ডে ৭০০ আসে, ২৫০ শেষ হয়, প্রতি সেকেন্ডে ৪৫০ জমে। পাঁচ সেকেন্ডে queue ২,২৫০, আর নতুন ক্রেতা “গ্রহণ করা হয়েছে” পেয়ে প্রায় ৯ সেকেন্ডে নিশ্চিতকরণ পান।
- সীমার কাছে। আরও তিন সেকেন্ডে ২,৭০০, ৩,১৫০, ৩,৬০০. এখনো কেউ ফেরত যাননি, কিন্তু নতুন জনের অপেক্ষা ৩,৬০০ ÷ ২৫০ = ১৪.৪ সেকেন্ড।
- Queue সীমায়। t = ১৮-তে জায়গা মাত্র ৪০০, তাই ৩০০ জন 429 আর Retry-After পান। এরপর প্রতি সেকেন্ডে শুধু ২৫০ জনের জায়গা, বাকি ৪৫০ ফেরত। চার সেকেন্ডে মোট ১,৬৫০ জন। খারাপ শোনায়, কিন্তু বিকল্প ছিল সবার জন্য timeout.
- ঢেউ শেষ। আবার সেকেন্ডে ১০০. Worker পুরো গতিতে, তাই queue প্রতি সেকেন্ডে ১৫০ করে কমে; ৩,৭৫০ থেকে ২৫ সেকেন্ডে শূন্য, t = ৪৬-এ।
- মোট। ৯০ সেকেন্ডে ১৬,২০০টি অর্ডার এসেছে: ১৪,৫৫০টি শেষ, ১,৬৫০টি ভদ্রভাবে ফেরত। ডেটাবেস কোনো মুহূর্তে সেকেন্ডে ২৫০-এর বেশি অর্ডার দেখেনি; এই সীমাটিই worker-এর গতি, যা load test দিয়ে ঠিক করা হয়।
// ৭.১: ঈদের flash sale — worker 250/s, queue-র সীমা 4000
// ঢেউ t = 10 থেকে 21 (১২ সেকেন্ড) 700/s, বাকি সময় 100/s; কৃত্রিম ঘড়ি
const RATE = 250, CAP = 4000;
const arrivals = (t) => (t >= 10 && t < 22 ? 700 : 100);
let depth = 0, done = 0, rejected = 0, maxDepth = 0, emptyAt = null, firstFull = null;
for (let t = 0; t < 90; t++) {
const inc = arrivals(t), acc = Math.min(inc, CAP - depth);
if (acc < inc && firstFull === null) firstFull = t;
rejected += inc - acc;
depth += acc;
const served = Math.min(depth, RATE);
depth -= served; done += served;
maxDepth = Math.max(maxDepth, depth);
if (t >= 22 && depth === 0 && emptyAt === null) emptyAt = t;
if ([9, 14, 18, 21, 30, 40].includes(t))
console.log(`t=${t}s in=${inc} queue=${depth} wait~${(depth / RATE).toFixed(1)}s rejected=${rejected}`);
}
console.log('first 429 at t =', firstFull, '| max queue', maxDepth, '| empty at t =', emptyAt);
console.log('done', done, '| rejected', rejected, '| arrived', done + rejected);
// সীমা না থাকলে: ঢেউয়ে প্রতি সেকেন্ডে 450 জমে, পরে 150 করে কমে
const peakNoCap = (700 - RATE) * 12;
console.log('no cap: peak', peakNoCap, '| drain', peakNoCap / (RATE - 100), 's | worst wait', peakNoCap / RATE, 's');t=14s in=700 queue=2250 wait~9.0s rejected=0 ধাপ ২-এর, t=18s in=700 queue=3750 wait~15.0s rejected=300 ধাপ ৪-এর শুরু, আর t=21s ... rejected=1650 তার শেষ। first 429 at t = 18 | max queue 3750 | empty at t = 46 আর done 14550 | rejected 1650 | arrived 16200 ধাপ ৫ ও ৬. শেষ লাইন no cap: peak 5400 | drain 36 s | worst wait 21.6 s ৭.১(ঙ)। মডেলে প্রতি সেকেন্ডের কাজের পরে queue মাপা হয়, তাই চূড়া দেখায় ৩,৭৫০, সীমা ৪,০০০ নয়।
৭.২(সহজ) একটি email job-এ backoff delay ১,০০০ ms, attempts ৫ (প্রথম চেষ্টাসহ)। (ক) প্রতিটি অপেক্ষা কত, আর শেষ চেষ্টা প্রথমটির কত ms পরে? (খ) Attempts ৯ আর প্রতিটি অপেক্ষার ছাদ ৩০ s হলে মোট অপেক্ষা? (গ) Jitter ০.৫ হলে, অর্থাৎ প্রতিটি অপেক্ষা [max ÷ ২, max) পরিসরে, (ক)-র শেষ চেষ্টা কোন পরিসরে? (ঘ) Provider এক মুহূর্তের জন্য পড়ে যাওয়ায় ১,০০০টি job একসাথে ব্যর্থ হলো। Jitter ছাড়া কতগুলো ঠিক ১,০০০ ms পরে একসাথে ফেরে? Jitter ০.৫-এ প্রথম retry ৫০০ থেকে ১,০০০ ms-এর মধ্যে সমানভাবে ছড়ালে প্রতিটি ১০০ ms-এর ঘরে গড়ে কতটি? (ঝালিয়ে নিন: পর্ব ৬২)
(a) waits = 1000, 2000, 4000, 8000
total = 1000 x (2^4 - 1) = 15,000 ms
(b) 1, 2, 4, 8, 16, 32 -> capped 30, 30, 30 (seconds)
1 + 2 + 4 + 8 + 16 + 30 + 30 + 30 = 121 s = 121,000 ms
(c) [15,000 / 2, 15,000) = [7,500, 15,000) ms
(d) no jitter: all 1,000 at exactly 1,000 ms
jitter 0.5: 500..1000 ms = 5 bins of 100 ms -> 1,000 / 5 = 200 each
যাচাই: পর্ব ৬২-এর seeded run-এ jitter ছাড়া +1000 +2000 +4000 +8000, শেষ চেষ্টা 15000 ms-এ, আর jitter-এ ঘরগুলো 500:226 600:193 …, গড় ২০০-এর চারপাশে। ছাদ না দিলে (খ)-র অষ্টম অপেক্ষা একাই ১২৮ সেকেন্ড হতো। শিক্ষা: jitter মোট সময় কমায় না, সবচেয়ে ব্যস্ত মুহূর্তের চাপ প্রায় পাঁচ ভাগের এক ভাগ করে। ১৫,০০০ ms; ১,২১,০০০ ms; ৭,৫০০ থেকে ১৫,০০০ ms; ১,০০০টি বনাম ঘরে প্রায় ২০০টি।
৭.৩(মাঝারি) (ক) Login-এ একটি account-এর জন্য sliding window: ৬০ সেকেন্ডে ৪টি চেষ্টা; আটকানো চেষ্টা গোনা হয় না। চেষ্টা আসে t = ০, ৫, ১৫, ৪০, ৫০, ৫৮, ৬১, ৬৬ আর ৮০ সেকেন্ডে। কোনগুলো আটকায়, আর কত সেকেন্ড পরে আবার চেষ্টা করা যাবে? (খ) Fixed window-এ মিনিটে ৫টি সীমা; চেষ্টা t = ৫৭, ৫৮, ৫৯, ৫৯, ৫৯, ৬০, ৬০, ৬১, ৬১, ৬২. কতটি ঢোকে, আর ৫টির sliding window-এ কতটি? (গ) একটি অফিসের ৩০০ জন একই public IP থেকে আসেন, প্রত্যেকে মিনিটে গড়ে ১টি অনুরোধ করেন। শুধু প্রতি IP-তে মিনিটে ১০০টি সীমা থাকলে কী হয়? (ঝালিয়ে নিন: পর্ব ৪৯)
(a) 0, 5, 15, 40 -> allowed (4/4)
50 -> blocked, oldest is 0, free at 0 + 60 = 60 -> wait 10 s
58 -> blocked, wait 2 s
61 -> 0 left the window -> allowed
66 -> 5 left -> allowed; 80 -> 15 left -> allowed
(b) fixed : minute 0 = 57,58,59,59,59 (5 ok); minute 1 = 60,60,61,61,62 (5 ok)
10 allowed in 6 seconds = 2 x limit
sliding: first 5 allowed, the rest blocked -> 5
(c) 300 per minute from one IP > 100 -> about 200 people blocked
পর্ব ৪৯-এর run-এও fixed window সীমানায় allowed : 10 আর sliding window 5 দিয়েছিল। (গ)-র শিক্ষা: শুধু IP দিয়ে সীমা দিলে NAT-এর পেছনের নির্দোষ মানুষ আটকান, আর account দিয়ে সীমা দিলে একজন আক্রমণকারী অনেক account-এ ছড়ান; তাই দুটি স্তর, IP-তে বড় সীমা আর account-এ ছোট। ৫০ আর ৫৮ আটকায়, ১০ ও ২ সেকেন্ড; ১০ বনাম ৫; প্রায় ২০০ জন অকারণে আটকান।
// ৭.২: exponential backoff — delay 1000 ms, n-তম অপেক্ষা delay × 2^(n-1)
const waits = (n, d, cap = Infinity) => Array.from({ length: n }, (_, i) => Math.min(d * 2 ** i, cap));
const sum = (a) => a.reduce((x, y) => x + y, 0);
const w4 = waits(4, 1000);
console.log('attempts 5:', w4.join(' + '), '=', sum(w4), 'ms');
const w8 = waits(8, 1000, 30000);
console.log('attempts 9, cap 30 s:', w8.join(' + '), '=', sum(w8), 'ms');
console.log('jitter 0.5: last try in [', sum(w4) / 2, ',', sum(w4), ') ms');
// ৭.৩: sliding window — 60 সেকেন্ডে 4টি; আটকানো চেষ্টা গোনা হয় না
function sliding(limit, win) {
const hits = [];
return (t) => {
while (hits.length && hits[0] <= t - win) hits.shift();
if (hits.length >= limit) return `blocked, wait ${hits[0] + win - t}s`;
hits.push(t);
return `allowed (${hits.length}/${limit})`;
};
}
const s = sliding(4, 60);
for (const t of [0, 5, 15, 40, 50, 58, 61, 66, 80]) console.log(`t=${t}`.padEnd(5), s(t));
// fixed window বনাম sliding: মিনিটে 5, মিনিটের সীমানায় ১০টি চেষ্টা
const burst = [57, 58, 59, 59, 59, 60, 60, 61, 61, 62];
const perMinute = new Map();
const fixedOk = burst.filter((t) => {
const m = Math.floor(t / 60), n = (perMinute.get(m) ?? 0) + 1;
perMinute.set(m, n);
return n <= 5;
}).length;
const sw = sliding(5, 60);
console.log('fixed allowed', fixedOk, '| sliding allowed', burst.filter((t) => sw(t).startsWith('allowed')).length);attempts 5: 1000 + 2000 + 4000 + 8000 = 15000 ms, ছাদসহ = 121000 ms আর jitter 0.5: last try in [ 7500 , 15000 ) ms ৭.২-এর। Sliding window-এর নয়টি লাইনে t=50 blocked, wait 10s, t=58 blocked, wait 2s, বাকিগুলো allowed; শেষ লাইন fixed allowed 10 | sliding allowed 5 ৭.৩(খ)। এখানে আটকানো চেষ্টা গোনা হয়নি; কিছু ব্যবস্থা গোনে, আর তখন উত্তর বদলায়, তাই প্রশ্নের শর্ত পড়ুন।
৮ · টাকা, SMS, token আর idempotency — ছোট সংখ্যার বড় ভুল
ঝালিয়ে নিন: পর্ব ৩২ · REST resource নকশা (পয়সা, PUT বনাম POST), পর্ব ৩৩ · Status code আর idempotency, পর্ব ৪৬ · JWT login (clockTolerance), পর্ব ৬৩ · Email আর notification (SMS segment), পর্ব ৬৫ · Payment integration, পর্ব ৭৩ · Unit test (পয়সার ছক)। Float-এর ভেতরের কথা JavaScript · Numbers-এ।
SMS ভাগ: GSM-7-এ ≤ ১৬০ হলে ১, নইলে ⌈ঘর ÷ ১৫৩⌉; UCS-2 (বাংলা)-তে ≤ ৭০ হলে ১, নইলে ⌈ঘর ÷ ৬৭⌉। টাকা: সব হিসাব পয়সায়, ভাগের বাকি একটি সারিতে। JWT: আসল আয়ু ≈ (exp − iat) + leeway − skew, যেখানে skew = API-র ঘড়ি − আসল সময়। Duplicate = অনুরোধ × retry-র হার × যেখানে প্রথমটি আসলে সফল।
৮.১(সহজ) (ক) বাংলা বার্তা (UCS-2) ৭০, ৭১, ৯৮ আর ১৪০ ঘরের; ইংরেজি বার্তা (GSM-7) ১৬০, ১৭০ আর ৩০৬ ঘরের। প্রতিটি কত ভাগে যায়? (খ) দিনে ২০,০০০টি বার্তা আর প্রতি ভাগ ৳০.৪০ ধরে, বার্তা ১, ২ আর ৩ ভাগের হলে দৈনিক খরচ? (গ) ১০৫ ঘরের একটি ইংরেজি বার্তার শেষে শুধু “ধন্যবাদ” জুড়লে কী হয়? (ঝালিয়ে নিন: পর্ব ৬৩)
(a) bn 70: <= 70 -> 1
bn 71: ceil(71 / 67) = 2
bn 98: ceil(98 / 67) = 2
bn 140: ceil(140 / 67) = 3 (2 x 67 = 134 < 140)
en 160: <= 160 -> 1
en 170: ceil(170 / 153) = 2
en 306: ceil(306 / 153) = 2 (exactly 2 x 153)
(b) 20,000 x 0.40 = Tk 8,000; 2 parts Tk 16,000; 3 parts Tk 24,000
(c) one non-GSM letter makes the whole SMS UCS-2: 105 + 8 = 113 units -> 2 parts
ফাঁদ: ভাগ হলে প্রতি ভাগে ৭০ বা ১৬০ ধরা। জোড়া লাগানোর তথ্য (UDH) প্রতি ভাগের জায়গা খায়, তাই ৬৭ আর ১৫৩. (গ)-র মতো ঘটনা পর্ব ৬৩-এর run-এ দেখা গেছে: enOneBn … UCS-2 86 units -> 2 SMS. সব দাম অনুমিত; আসল দাম চুক্তি আর প্রতিষ্ঠান ভেদে আলাদা। ১, ২, ২, ৩; ১, ২, ২; ৳৮,০০০, ৳১৬,০০০, ৳২৪,০০০; পুরো বার্তা UCS-2, দুই ভাগ।
৮.২(মাঝারি) (ক) “19.99” টাকাকে Math.floor(taka * 100) দিয়ে পয়সা বানালে কত আসে, কেন? (খ) ৳১৯.৯০-এর ১,০০০টি বিক্রি floating-point-এ যোগ করলে আর পয়সায় যোগ করলে কী আসে? (গ) ৯৫,০০০ পয়সার তেলে ১৫% ছাড় কত পয়সা? (ঘ) ৳১০০ ছাড় তিনটি সারিতে ভাগ: পয়সায় সমান ভাগ করে বাকিটা শেষ সারিতে দিলে কী আসে? টাকায় 33.33 × ৩ করলে? (ঝালিয়ে নিন: পর্ব ৩২, ৭৩)
(a) 19.99 x 100 = 1998.9999999999998 in binary float
floor -> 1998 paisa (one paisa lost); parse the text "19.99" -> 1999
(b) float: 19900.000000000047 paisa: 1,000 x 1,990 = 1,990,000
(c) 95,000 x 15 / 100 = 14,250 paisa = Tk 142.50
(d) 10,000 paisa / 3 = 3,333 rem 1 -> 3,333 + 3,333 + 3,334 = 10,000
taka: 33.33 x 3 = 99.99 -> one paisa missing
পর্ব ৭৩-এর ছকে float পথে ৫টির ৩টি সারি এক পয়সা কম দিয়েছিল, ঠিক (ক)-র কারণে। পর্ব ৩২-এর run-এ (খ)-র 19900.000000000047 এসেছিল। (গ) পূর্ণসংখ্যা এল, কিন্তু ১৫% যদি ৯৯,৯৯৯ পয়সায় হতো (১৪,৯৯৯.৮৫), তাহলে rounding-এর নিয়ম আগে থেকে লিখতে হতো। ১৯৯৮ পয়সা; float ভুল, পয়সায় ঠিক ১৯,৯০,০০০; ১৪,২৫০ পয়সা; ৩,৩৩৩ + ৩,৩৩৩ + ৩,৩৩৪, আর টাকায় এক পয়সা হারায়।
৮.৩(মাঝারি) একটি access token ১০ মিনিটের: exp = iat + ৬০০, nbf = iat. Verify-এর নিয়ম jose-এর মতো: exp ≤ now − leeway হলে expired, nbf > now + leeway হলে too early. আসল সময়ে token কতক্ষণ চলে: (ক) ঘড়ি মিললে, leeway ০ আর ৩০ s-এ? (খ) API-র ঘড়ি ৪৫ s এগিয়ে, leeway ৩০ s? (গ) API-র ঘড়ি ২০ s পিছিয়ে, leeway ৩০ s? (ঘ) ২০ s পিছিয়ে, leeway ০? (ঝালিয়ে নিন: পর্ব ৪৬)
API time = real + skew; ok while real + skew < exp + leeway
(a) skew 0, lw 0 : real < iat + 600 -> 600 s
skew 0, lw 30: real < iat + 630 -> 630 s
(b) skew +45, lw 30: real < iat + 600 + 30 - 45 = iat + 585 -> 585 s
(c) skew -20, lw 30: real < iat + 650 -> 650 s
(d) skew -20, lw 0 : too early while real - 20 < iat, i.e. first 20 s
ok from iat + 20 to iat + 619 -> 600 s, but late
পর্ব ৪৬-এর exp-clock.js-এ একই নিয়মে leeway ৫ s-এ 1304 পর্যন্ত ok, 1305-এ expired এসেছিল। (ঘ)-র ঘটনাটি সবচেয়ে বিরক্তিকর: login সফল, অথচ প্রথম ২০ সেকেন্ড প্রতিটি API 401. এজন্যই সেকেন্ডের ঘরে ছোট leeway, আর server-এ NTP দিয়ে ঘড়ি মেলানো। Leeway মিনিটে দিলে ছোট token-এর মানেই থাকে না। ৬০০ আর ৬৩০ s; ৫৮৫ s; ৬৫০ s; ৬০০ s, কিন্তু প্রথম ২০ s “too early”.
৮.৪(মাঝারি) দিনে ১০,০০০টি checkout. ২% ক্ষেত্রে client timeout পেয়ে একবার আবার পাঠায়, আর তার ৬০%-এ প্রথম অনুরোধ server-এ আসলে সফল হয়েছিল। গড় অর্ডার ৮৫,০০০ পয়সা। (ক) Idempotency-Key ছাড়া দিনে কতবার দুবার টাকা কাটা হয়, কত টাকা? (খ) ৩০ দিনে কতটি refund? (গ) Key থাকলে? (ঘ) ৳৮৪০-এর কার্টে key ছাড়া POST তিনবার গেলে কত পয়সা চাওয়া হয়? (ঝালিয়ে নিন: পর্ব ৩২, ৩৩, ৬৫)
(a) retried = 10,000 x 0.02 = 200
double = 200 x 0.6 = 120 per day
extra = 120 x 85,000 = 10,200,000 paisa = Tk 1,02,000
(b) 120 x 30 = 3,600 refunds
(c) 0: the second request gets the stored first response
(d) 3 x 84,000 = 252,000 paisa = Tk 2,520 for a Tk 840 cart
বাকি ৪০% retry-তে কোনো ক্ষতি নেই, কারণ প্রথমটি ব্যর্থ ছিল। Key থাকলে দুটি retry একসাথে এলেও একটিই কাজ করে, যদি key-এর দাবি atomic হয় (পর্ব ৩৩)। (ঘ)-র সংখ্যা পর্ব ৩২-এর run-এর orders: 3. শিক্ষা: ২% শুনতে ছোট, কিন্তু টাকার জগতে প্রতিটি duplicate একটি রাগী ক্রেতা আর একটি হাতের refund. ১২০ বার, ৳১,০২,০০০; ৩,৬০০টি; শূন্য; ২,৫২,০০০ পয়সা।
// ৮.১: SMS ভাগ — GSM-7: 160, তার বেশি হলে 153 করে; UCS-2: 70, তার বেশি হলে 67 করে
const parts = (units, ucs2) => ucs2
? (units <= 70 ? 1 : Math.ceil(units / 67))
: (units <= 160 ? 1 : Math.ceil(units / 153));
const cases = [['bn 70', 70, true], ['bn 71', 71, true], ['bn 98', 98, true],
['bn 140', 140, true], ['en 160', 160, false], ['en 170', 170, false], ['en 306', 306, false]];
for (const [label, u, ucs] of cases) console.log(label.padEnd(7), parts(u, ucs), 'SMS');
const RATE = 0.40, DAILY = 20000; // ধরে নেওয়া দাম আর সংখ্যা
console.log('Tk/day: 1 part', DAILY * RATE, '| 2 parts', 2 * DAILY * RATE, '| 3 parts', 3 * DAILY * RATE);
// ৮.২: টাকা পয়সায়
console.log('floor(19.99 * 100) =', Math.floor(19.99 * 100), '| raw', 19.99 * 100);
let f = 0;
for (let i = 0; i < 1000; i++) f += 19.9;
console.log('1000 x 19.9 as float =', f, '| as paisa =', 1000 * 1990);
console.log('VAT 15% of 95000 paisa =', (95000 * 15) / 100, '| 3 x 19.9 =', 3 * 19.9);
const share = Math.floor(10000 / 3), lines = [share, share, 10000 - 2 * share];
console.log('discount split', lines.join(' + '), '=', lines.reduce((a, b) => a + b, 0));
console.log('float split', (100 / 3).toFixed(2), 'x 3 =', ((100 / 3).toFixed(2) * 3).toFixed(2));সাতটি লাইন ৮.১(ক)-এর: bn 71 2 SMS, bn 140 3 SMS, en 306 2 SMS ইত্যাদি, তারপর Tk/day: 1 part 8000 | 2 parts 16000 | 3 parts 24000. ৮.২-এর লাইনে floor(19.99 * 100) = 1998 | raw 1998.9999999999998, 1000 x 19.9 as float = 19900.000000000047 | as paisa = 1990000, VAT 15% of 95000 paisa = 14250 (একই হিসাব ছাড়েও খাটে), discount split 3333 + 3333 + 3334 = 10000 আর float split 33.33 x 3 = 99.99.
// ৮.৩: JWT — jose-এর মতো নিয়ম: nbf > now + leeway হলে too early, exp <= now - leeway হলে expired
const check = (c, now, lw) =>
c.nbf > now + lw ? 'too early' : c.exp <= now - lw ? 'expired' : 'ok';
const iat = 10000, tok = { nbf: iat, exp: iat + 600 }; // ১০ মিনিটের access token
for (const [skew, lw] of [[0, 0], [0, 30], [45, 30], [-20, 30], [-20, 0]]) {
let first = null, last = null; // API-র ঘড়ি = আসল সময় + skew
for (let real = iat; real <= iat + 700; real++) {
if (check(tok, real + skew, lw) === 'ok') { if (first === null) first = real; last = real; }
}
console.log(`skew ${String(skew).padStart(3)}s leeway ${String(lw).padStart(2)}s:`,
`ok ${first - iat}..${last - iat} -> ${last - first + 1} s`);
}
// ৮.৪: Idempotency-Key ছাড়া দুবার টাকা কাটা (সব সংখ্যা ধরে নেওয়া)
const checkouts = 10000, timeout = 0.02, serverDone = 0.6, avgPaisa = 85000;
const retried = checkouts * timeout, dup = retried * serverDone;
console.log('retried', retried, '| double charges/day', dup, '| extra Tk/day', (dup * avgPaisa) / 100);
console.log('per 30 days', dup * 30, 'refunds | with key: 0');
console.log('POST x3 without key:', 3 * 84000, 'paisa for a Tk 840 cart');পাঁচটি লাইন ৮.৩-এর পাঁচ অবস্থা: skew 0s leeway 0s: ok 0..599 -> 600 s, skew 45s leeway 30s: ok 0..584 -> 585 s, skew -20s leeway 30s: ok 0..649 -> 650 s আর skew -20s leeway 0s: ok 20..619 -> 600 s, যেখানে শুরুর 20 মানে প্রথম ২০ সেকেন্ড “too early”. শেষ তিন লাইন ৮.৪: double charges/day 120 | extra Tk/day 102000, per 30 days 3600 refunds আর 252000 paisa.
৯ · Production — CLS, INP, error budget, Docker আর খরচ
ঝালিয়ে নিন: পর্ব ৮০ · ফুল-স্ট্যাক অ্যাপ Docker-এ (layer cache, image-এর আকার), পর্ব ৮২ · কোথায় চালাবেন (খরচের ছক, cold start), পর্ব ৮৩ · Monitoring (SLO, error budget, burn rate), পর্ব ৮৪ · Web performance (CLS, INP). গভীরে Cloud & DevOps · Alerting আর SLO.
CLS = সবচেয়ে বড় session window-এর যোগফল; আগের shift থেকে ১ s বা বেশি ফাঁক, অথবা window ৫ s ছাড়ালে নতুন window; ক্লিকের পরের shift বাদ। INP: একটি page view-এ প্রতি ৫০টি interaction-এ সবচেয়ে ধীর একটি বাদ, তারপর সবচেয়ে ধীর; রায় অনেক view-এর p75-এ। Error budget = (১ − SLO) × মোট; burn rate = এখনকার error ÷ (১ − SLO). Layer cache: বদলানো ধাপ আর তার পরের সব ধাপ আবার চলে।
৯.১(মাঝারি) (ক) পাতা A-তে layout shift: t = ৪০০ ms-এ ০.০৬, ১,১০০-এ ০.০৫, ১,৯০০-এ ০.০৪, ৩,৩০০-এ ০.০৯, ৩,৫০০-এ ০.০৩ (ক্লিকের ফল), ৩,৮০০-এ ০.০২ আর ৪,৭০০-এ ০.০১. Session window আর CLS কত, রায় কী (ভালো ≤ ০.১, ০.২৫ পর্যন্ত উন্নতি দরকার)? (খ) পাতা B-তে t = ২০০ ms থেকে প্রতি ৯০০ ms-এ ০.০২ করে ৮টি shift. CLS কত, আর সব যোগ করলে কত হতো? (ঝালিয়ে নিন: পর্ব ৮৪)
(a) 400 : window 1 starts, 0.06
1100 : gap 700 -> 0.11
1900 : gap 800 -> 0.15
3300 : gap 1400 >= 1000 -> window 1 closed at 0.15; window 2 = 0.09
3500 : input shift, skipped
3800 : gap 500 -> 0.11
4700 : gap 900 -> 0.12
CLS = max(0.15, 0.12) = 0.15 -> needs improvement
(b) shifts at 200, 1100, ..., 6500; gaps 900 < 1000
at 5600: 5600 - 200 = 5400 >= 5000 -> new window
window 1 = 6 x 0.02 = 0.12, window 2 = 2 x 0.02 = 0.04
CLS = 0.12; plain sum = 8 x 0.02 = 0.16
দুটি ফাঁদ: সব shift যোগ করা (A-তে ০.৩০, B-তে ০.১৬), আর ক্লিকের পরের shift গোনা। B দেখায়, ফাঁক ছোট থাকলেও window ৫ সেকেন্ডে ভাঙে। পর্ব ৮৪-এর cls-session-windows.js একই নিয়মে web-vitals-এর পাঠানো ০.২২৯-এর সাথে মিলেছিল। A: window ০.১৫ আর ০.১২, CLS ০.১৫, উন্নতি দরকার; B: ০.১২, যোগফল হতো ০.১৬.
shift 0.06, window 1 starts
window 1 = 0.06
gaps 700, 800 ms (< 1000)
0.06 + 0.05 + 0.04
window 1 = 0.15
gap 3300 - 1900 = 1400 ms >= 1000
window 1 closed: 0.15
window 2 = 0.09
shift 0.03 after an input
not counted
window 2 = 0.09
gaps 500, 900 ms
0.09 + 0.02 + 0.01
window 2 = 0.12
CLS = max(0.15, 0.12)
= 0.15
0.1 < 0.15 <= 0.25: needs improvement
- প্রথম shift. মাপহীন ছবি এসে লেখা নামাল: ০.০৬. এখান থেকে প্রথম session window শুরু।
- কাছাকাছি দুটি shift. ৭০০ আর ৮০০ ms পরে আরও ০.০৫ আর ০.০৪; ফাঁক ১ সেকেন্ডের কম, তাই একই window, যোগফল ০.১৫.
- বড় ফাঁক। ১,৯০০ থেকে ৩,৩০০-এর ফাঁক ১,৪০০ ms, তাই প্রথম window ০.১৫-এ বন্ধ, আর ০.০৯ দিয়ে দ্বিতীয় window শুরু।
- ক্লিকের ফল। ক্রেতার ক্লিকের ঠিক পরের ০.০৩ প্রত্যাশিত পরিবর্তন, তাই গোনা হয় না। দ্বিতীয় window এখনো ০.০৯.
- শেষ দুটি shift. ৫০০ আর ৯০০ ms ফাঁকে ০.০২ আর ০.০১, দ্বিতীয় window ০.১২. মোট সময় ১,৪০০ ms, ৫ সেকেন্ডের সীমা থেকে অনেক দূরে।
- রায়। CLS হলো সবচেয়ে বড় window, ০.১৫, যোগফল ০.২৭ নয়। ০.১-এর ওপরে আর ০.২৫-এর ভেতরে, তাই “উন্নতি দরকার”। ছবিতে width আর height দিলে প্রথম shift-ই চলে যেত।
৯.২(মাঝারি) (ক) একটি page view-এ ১৩০টি interaction, সবচেয়ে ধীর চারটি ৮২০, ৪৫০, ২৬০ আর ১৩৯ ms. INP কত? (খ) আরেকটি view-এ মাত্র ৩টি interaction: ৬৪, ৩১২ আর ৯৬ ms? (গ) ৮টি page view-এর INP ১২০, ৯০, ২৬০, ১৮০, ৭০, ৩১০, ১৫০ আর ২০০ ms. Nearest-rank-এ p75 কত, আর রায় কী (ভালো ≤ ২০০ ms, ৫০০ পর্যন্ত উন্নতি দরকার)? (ঝালিয়ে নিন: পর্ব ২৩, ৮৪)
(a) skip = floor(130 / 50) = 2 -> drop 820, 450 -> INP = 260 ms
(b) 3 < 50 -> skip 0 -> INP = slowest = 312 ms
(c) sorted: 70, 90, 120, 150, 180, 200, 260, 310
rank = ceil(0.75 x 8) = 6 -> 200 ms -> good (just)
(গ)-তে ৮টির ২টি view ২০০ ms-এর ওপরে, তবু p75 “ভালো”; আর একটি ধীর view এলেই সীমা পেরোত। p75 মানে প্রতি চারজনে একজনের খারাপ অভিজ্ঞতা লুকিয়ে থাকতে পারে। ফাঁদ: সব interaction-এর গড় নেওয়া; INP সবচেয়ে খারাপের কাছাকাছি মান, গড় নয়। পর্ব ৮৪-এর inp-estimate.js-এ ১২০টি interaction-এ INP 310 এসেছিল একই নিয়মে। ২৬০ ms; ৩১২ ms; ২০০ ms, ঠিক সীমায় “ভালো”।
// ৯.১: CLS = সবচেয়ে বড় session window; ফাঁক >= 1 s বা window >= 5 s হলে নতুন window
function cls(entries) {
let best = 0, cur = 0, first = 0, prev = 0;
const wins = [];
for (const e of entries) {
if (e.input) continue; // ক্লিকের পরের shift গোনা হয় না
if (cur > 0 && (e.t - prev >= 1000 || e.t - first >= 5000)) { wins.push(+cur.toFixed(3)); cur = 0; }
if (cur === 0) first = e.t;
cur += e.v; prev = e.t; best = Math.max(best, cur);
}
if (cur > 0) wins.push(+cur.toFixed(3));
return { cls: +best.toFixed(3), wins };
}
const a = [{ t: 400, v: 0.06 }, { t: 1100, v: 0.05 }, { t: 1900, v: 0.04 }, { t: 3300, v: 0.09 },
{ t: 3500, v: 0.03, input: true }, { t: 3800, v: 0.02 }, { t: 4700, v: 0.01 }];
console.log('page A', JSON.stringify(cls(a)));
const b = Array.from({ length: 8 }, (_, i) => ({ t: 200 + i * 900, v: 0.02 })); // প্রতি 900 ms-এ 0.02
console.log('page B', JSON.stringify(cls(b)));
// ৯.২: একটি page view-এর INP — প্রতি ৫০টি interaction-এ সবচেয়ে ধীর একটি বাদ
const inp = (d) => [...d].sort((x, y) => y - x)[Math.min(Math.floor(d.length / 50), d.length - 1)];
const view = Array.from({ length: 130 }, (_, i) => 40 + ((i * 37) % 100));
view[10] = 820; view[70] = 450; view[120] = 260;
console.log('130 interactions, slowest 4:', [...view].sort((x, y) => y - x).slice(0, 4).join(', '), '-> INP', inp(view));
console.log('3 interactions [64, 312, 96] -> INP', inp([64, 312, 96]));
const p75 = (v) => [...v].sort((x, y) => x - y)[Math.ceil(0.75 * v.length) - 1];
console.log('p75 of 8 views =', p75([120, 90, 260, 180, 70, 310, 150, 200]), 'ms');page A {"cls":0.15,"wins":[0.15,0.12]} stepper-এর সাথে মেলে, আর page B {"cls":0.12,"wins":[0.12,0.04]} ৯.১(খ)-এর ৫ সেকেন্ডের নিয়ম দেখায়। ৯.২-এর লাইনে slowest 4: 820, 450, 260, 139 -> INP 260, [64, 312, 96] -> INP 312 আর p75 of 8 views = 200 ms. চেকারের ১৩০টি সময় একটি নির্দিষ্ট সূত্রে তৈরি ((i * 37) % 100), এলোমেলো নয়, তাই প্রতিবার একই ফল।
৯.৩(মাঝারি) Checkout API-র SLO ৯৯.৯%, ৩০ দিনের জানালা, মাসে ৩০ লাখ request ধরা। (ক) Error budget মিনিটে আর ব্যর্থ request-এ কত? (খ) গত এক ঘণ্টায় error ১.৪৪% হলে burn rate কত, এই হারে পুরো budget কত ঘণ্টায় শেষ, আর ঐ এক ঘণ্টায় budget-এর কত শতাংশ খরচ হলো? (গ) মাসের ১২ দিনে ১,৫০০টি ব্যর্থ: কত শতাংশ খরচ, আর এই গতিতে মাস শেষে কত? (ঝালিয়ে নিন: পর্ব ৮৩)
(a) minutes = 30 x 24 x 60 x 0.001 = 43.2 min
requests = 3,000,000 x 0.001 = 3,000 bad
(b) burn = 0.0144 / 0.001 = 14.4
budget gone in 720 h / 14.4 = 50 h
one hour = 14.4 / 720 = 2.0% of the budget
(c) used = 1,500 / 3,000 = 50% in 12 / 30 = 40% of the time
pace = 50% / 40% = 125% by month end
১৪.৪ সংখ্যাটি দৈব নয়: এক ঘণ্টায় মাসের ২% খরচ মানে ঠিক এই burn rate, আর পর্ব ৮৩-এর fast-burn alert এই সীমাতেই বাজে, এক ঘণ্টা আর পাঁচ মিনিটের দুই জানালা মিলিয়ে। (গ)-র ১২৫% মানে নতুন feature-এর ঝুঁকি কমিয়ে স্থিতিতে মন দেওয়ার সময়। ৪৩.২ মিনিট বা ৩,০০০টি; ১৪.৪, ৫০ ঘণ্টা, ২%; ৫০%, মাস শেষে ১২৫%.
৯.৪(সহজ) একটি Next.js image-এর build-এ npm ci ৩৩.৩ s আর next build ৪.১ s নেয় (পর্ব ৮০-এর একটি local run). ঠিক ক্রমে আগে শুধু lockfile কপি হয়, তারপর npm ci, তারপর কোড; ভুল ক্রমে আগে সব কোড কপি। দিনে ২০টি build: ১৮টিতে শুধু কোড বদলায়, ২টিতে lockfile. (ক) দুই ক্রমে দিনে মোট RUN-এর সময়? (খ) সার্ভারের download ২০ Mbps ধরে, ৯৭.৮ MB-এর multi-stage image, ৬৬৯ MB-এর naive image আর base আগে থেকে থাকলে নিজের ১২.১ MB layer নামাতে কত সময়? (ঝালিয়ে নিন: পর্ব ৮০)
(a) good: code-only 4.1 s, lockfile 33.3 + 4.1 = 37.4 s
18 x 4.1 + 2 x 37.4 = 73.8 + 74.8 = 148.6 s
bad : every change reruns npm ci: 20 x 37.4 = 748.0 s
(b) 97.8 x 8 / 20 = 39.1 s
669 x 8 / 20 = 267.6 s
12.1 x 8 / 20 = 4.84 s
যাচাই: ৭৪৮ ÷ ১৪৮.৬ ≈ ৫ গুণ। পর্ব ৮০-এর মডেলে কোড বদলে ~ 4.1s cached 3 আর ভুল ক্রমে ~37.4s এসেছিল; আসল মোট সময় context পাঠানো আর layer export মিলিয়ে একটু বেশি। (খ)-তে layer content hash দিয়ে চেনা হয়, তাই base layer সার্ভারে থাকলে নতুন version-এ মোটামুটি নিজের অংশটুকুই নামে। ১৪৮.৬ বনাম ৭৪৮.০ s; ৩৯.১, ২৬৭.৬ আর ৪.৮৪ s.
৯.৫(মাঝারি) সব দাম ধরে নেওয়া, কোনো প্রতিষ্ঠানের আসল দাম নয়: একটি VPS মাসে ৳২,০০০, ভিড় যা-ই হোক। একটি pay-per-use platform মাসে ৳১,৫০০, তাতে ১০ লাখ request পর্যন্ত, তারপর প্রতি লাখে ৳৬০. (ক) কত request-এ দুটির খরচ সমান? (খ) ৮, ১৮, ৩০ আর ৬০ লাখে pay-per-use কত? (গ) বছরে ১১টি সাধারণ মাসে ৮ লাখ আর ঈদের মাসে ৬০ লাখ হলে বছরে কোনটি সস্তা, কত? (ঝালিয়ে নিন: পর্ব ৮২)
(a) 1,500 + (x - 10) x 60 = 2,000 -> x = 10 + 500 / 60 = 18.33 lakh
(b) 8 lakh: 1,500 (inside the free 10)
18 lakh: 1,500 + 8 x 60 = 1,980
30 lakh: 1,500 + 20 x 60 = 2,700
60 lakh: 1,500 + 50 x 60 = 4,500
(c) pay-per-use: 11 x 1,500 + 4,500 = 21,000
VPS : 12 x 2,000 = 24,000 -> pay-per-use cheaper by 3,000
এই ছকে যা নেই, সেটিই আসল সিদ্ধান্ত বদলায়: VPS-এ update, backup-এর মহড়া আর রাতের crash সামলানোর মানুষের সময়, ঈদের ৬০ লাখে একটি VPS টিকবে কি না, আর ডলারে বিল হলে কার্ডের সীমা ও ব্যাংকের খরচ। পর্ব ৮২-এর খরচের ছকও এভাবেই অনুমিত সংখ্যায়। প্রায় ১৮.৩৩ লাখ; ৳১,৫০০, ৳১,৯৮০, ৳২,৭০০, ৳৪,৫০০; বছরে ৳২১,০০০ বনাম ৳২৪,০০০.
// ৯.৩: error budget আর burn rate — SLO 99.9%, ৩০ দিন, ৩০ লাখ request
const slo = 0.999, days = 30, reqs = 3000000;
const budgetMin = days * 24 * 60 * (1 - slo), budgetReq = Math.round(reqs * (1 - slo));
console.log('budget', budgetMin.toFixed(1), 'min or', budgetReq, 'bad requests');
const burn = 0.0144 / (1 - slo);
console.log('burn', burn.toFixed(1), '| budget gone in', ((days * 24) / burn).toFixed(0), 'h',
'| 1 h uses', ((burn / (days * 24)) * 100).toFixed(1) + '%');
console.log('12 days, 1500 bad: used', ((1500 / budgetReq) * 100).toFixed(0) + '%, on pace',
((1500 / budgetReq / (12 / 30)) * 100).toFixed(0) + '%');
// ৯.৪: Docker layer cache — npm ci 33.3 s, build 4.1 s (পর্ব ৮০-এর একটি local run)
const good = { code: 4.1, deps: 33.3 + 4.1 }, bad = { code: 33.3 + 4.1, deps: 33.3 + 4.1 };
const day = (r) => (18 * r.code + 2 * r.deps).toFixed(1);
console.log('20 builds (18 code, 2 deps): good', day(good), 's | bad', day(bad), 's');
console.log('pull 97.8 MB @20 Mbps', ((97.8 * 8) / 20).toFixed(1), 's | own layers 12.1 MB',
((12.1 * 8) / 20).toFixed(2), 's | naive 669 MB', ((669 * 8) / 20).toFixed(1), 's');
// ৯.৫: খরচ — সব দাম ধরে নেওয়া: VPS মাসে ৳2000; pay-per-use ৳1500, ১০ লাখ পর্যন্ত, তারপর লাখে ৳60
const vps = 2000, base = 1500, free = 10, perLakh = 60;
const payg = (lakh) => base + Math.max(0, lakh - free) * perLakh;
console.log('break-even', (free + (vps - base) / perLakh).toFixed(2), 'lakh |',
[8, 18, 30, 60].map((x) => `${x} lakh: ${payg(x)}`).join(', '));
console.log('year (11 x 8 lakh + 1 x 60 lakh): pay-per-use', 11 * payg(8) + payg(60), '| VPS', 12 * vps);budget 43.2 min or 3000 bad requests, burn 14.4 | budget gone in 50 h | 1 h uses 2.0% আর used 50%, on pace 125% ৯.৩-এর। good 148.6 s | bad 748.0 s আর pull 97.8 MB @20 Mbps 39.1 s | own layers 12.1 MB 4.84 s | naive 669 MB 267.6 s ৯.৪-এর। শেষ দুই লাইন ৯.৫: break-even 18.33 lakh, চারটি মাসের খরচ, আর pay-per-use 21000 | VPS 24000.
১০ · শেষ মুহূর্তের সূত্র-তালিকা
পরীক্ষার আগে এই এক টেবিলই যথেষ্ট। শেষ কলামে সবচেয়ে সাধারণ ফাঁদ; সূত্রের চেয়ে সেটি মনে রাখাই বেশি কাজের।
| বিষয় | সূত্র | ফাঁদ | পর্ব |
|---|---|---|---|
| নামার সময় | byte × ৮ ÷ bit/s; একটি অনুরোধ ≈ RTT + নামা | ৮ ভুলে যাওয়া; kB আর KiB গুলিয়ে ফেলা | ৭৯, ৮৪ |
| Compression | (আগে − পরে) ÷ আগে; মাসে = পার্থক্য × view | ছোট chunk-এ মোট বাড়ে; ছবিতে gzip বৃথা | ৭৯, ৮৪ |
| LCP বাজেট | ২,৫০০ − TTFB − RTT − অন্য দেরি; byte = সময় × bit/s ÷ ৮ | lazy করা LCP ছবি; দেরিতে খোঁজা | ৮৪ |
| Render গোনা | memo ছাড়া n(১ + m); memo-সহ n + অস্থির × m | inline object বা function-এ memo বৃথা | ১৪, ১৬, ২৩ |
| পাতা | ⌈N ÷ L⌉; শেষ = N − (P − ১)L | ⌊ ⌋ নিয়ে শেষ পাতা হারানো | ৩৪ |
| Offset বনাম keyset | p × L; সব পাতা L·P(P+১)/২; keyset L + ১ | ছোট পাতায় offset আরও খারাপ | ৩৪ |
| N+1 | ১ + (সম্পর্ক) × N; সময় ≈ trip × RTT | nested লুপে গুণ না করা | ৪২ |
| Hit rate | DB = peak(১ − h); h = ১ − budget ÷ peak | ৯৫ আর ৯৯%-কে প্রায় সমান ভাবা | ৪৪, ৮৭ |
| গড় latency | h·hit + (১ − h)·miss | গড় দেখে p99 ভুলে যাওয়া | ৪৪ |
| Jitter, stampede | ছড়ানো = ২·TTL·j; ঢেউ = req/s × rebuild | single-flight-কে সব process-এর ভাবা | ৪৪ |
| Capacity | DAU × অনুরোধ ÷ ৮৬,৪০০ × peak | গড়ের জন্য নকশা; ৩,৬০০ দিয়ে ভাগ | ৮৭ |
| Little | L = λ × W (W সেকেন্ডে) | ms-কে সেকেন্ডে না বদলানো | ৩৮, ৮৭ |
| Pool সীমা | server × process × pool ≤ max − সংরক্ষিত | serverless-এর প্রতিটি কপির নিজের pool | ৩৮, ৮৭ |
| Queue | বৃদ্ধি = আগমন − গতি; অপেক্ষা = queue ÷ গতি | খালি হওয়ার হারে আগমন বাদ দেওয়া | ৬২, ৮৭ |
| Backoff | delay × ২n−১; মোট delay(২k − ১) | attempts আর অপেক্ষার সংখ্যা গুলিয়ে ফেলা | ৬২ |
| Rate limit | fixed-এ সবচেয়ে খারাপ ২ × সীমা; sliding-এ সীমা | শুধু IP দিয়ে সীমা | ৪৯ |
| SMS | GSM-7 ১৬০ / ১৫৩; UCS-2 ৭০ / ৬৭ | একটি বাংলা শব্দে পুরো বার্তা UCS-2 | ৬৩ |
| টাকা | পয়সায় integer; ভাগের বাকি এক সারিতে | floor(19.99 * 100) = ১৯৯৮ | ৩২, ৭৩ |
| JWT ঘড়ি | আয়ু ≈ (exp − iat) + leeway − skew | পিছিয়ে থাকা ঘড়িতে “too early” | ৪৬ |
| Duplicate | অনুরোধ × retry × প্রথমটি সফল | সব retry-কে duplicate ধরা | ৩৩, ৬৫ |
| CLS | সবচেয়ে বড় session window (১ s ফাঁক, ৫ s সীমা) | সব shift যোগ; ক্লিকের shift গোনা | ৮৪ |
| INP | প্রতি ৫০-এ একটি বাদ; রায় p75-এ | গড় নেওয়া | ২৩, ৮৪ |
| Error budget | (১ − SLO) × মোট; burn = error ÷ (১ − SLO) | ৯৯.৯৯% লক্ষ্যে সাধারণ মাসও ব্যর্থ | ৮৩ |
| Docker | বদলানো ধাপ আর পরের সব আবার চলে | কোড আগে কপি করে npm ci-র cache নষ্ট | ৮০ |
| খরচ | স্থির = base + (x − free) × হার | মানুষের সময় আর উৎসবের ভিড় বাদ দেওয়া | ৮২ |
AI-এর যুগে — ফুল-স্ট্যাকের হিসাব যাচাই
AI-কে capacity বা cache-এর অঙ্ক দিলে পরিষ্কার টেবিলে আত্মবিশ্বাসী উত্তর আসে। তাতে প্রায়ই একক গুলিয়ে যায়, গড়কে peak বলা হয়, আর কখনো কোনো hosting প্রতিষ্ঠানের “আসল দাম” বা benchmark সংখ্যা বানিয়ে বসানো হয়। ভুলগুলো চেনা:
| AI-এর ভুল | পরিণাম | কীভাবে ধরবেন |
|---|---|---|
| Byte আর bit, kB আর KiB গুলিয়ে ফেলা | Download সময় ৮ গুণ ভুল | একক আলাদা লাইনে লিখতে বলুন, উল্টো হিসাব করুন |
| গড় req/s দিয়ে server বা pool মাপা | ঈদের রাতে timeout | Peak factor আর উৎসবের ভিড় জিজ্ঞেস করুন |
| “memo দিলেই দ্রুত”; useMemo সব জায়গায় | বাড়তি কোড, লাভ নেই | Profiler-এ render গুনুন, অস্থির prop খুঁজুন |
| বাংলা SMS-এ ১৬০ অক্ষর ধরা | খরচ দুই-তিন গুণ বেশি | UCS-2-এর ৭০ / ৬৭ নিয়মে নিজে গুনুন |
টাকা float-এ, toFixed দিয়ে “ঠিক” করা | খাতা মেলে না | পয়সায় integer, ছক ধরে test (পর্ব ৭৩) |
| Hosting বা SMS-এর দাম, বা “X ১০ গুণ দ্রুত” বানানো | ভিত্তিহীন সিদ্ধান্ত | উৎস চান; নিজের load test বা চুক্তির দাম বসান |
| Error লুকাতে retry বাড়ানো, jitter বা ছাদ ছাড়া | Retry storm, দুবার টাকা কাটা | Backoff-এর তালিকা হিসাব করুন, idempotency key রাখুন |
AI-কে উত্তরদাতা নয়, যাচাইকারী হিসেবে ব্যবহার করুন। কোনো secret, আসল log, ক্রেতার তথ্য বা প্রতিষ্ঠানের গোপন সংখ্যা prompt-এ দেবেন না।
যাচাইয়ের চেকলিস্ট
নিজেকে যাচাই করুন
প্রতিটি প্রশ্নে খাতায় হিসাব করে তবে উত্তর বাছুন। সব তথ্য প্রশ্নের ভেতরেই আছে, আর সব গতি ও দাম অনুমিত।
ক্রমদৈনিক ব্যবহারকারী থেকে connection pool-এর মাপ বের করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- DAU আর প্রতি জনের দৈনিক অনুরোধ লেখা
- ৮৬,৪০০ দিয়ে ভাগ করে গড় req/s
- Peak factor দিয়ে গুণ করে peak req/s
- প্রতি অনুরোধের SQL দিয়ে গুণ করে peak SQL/s
- Little-এর সূত্রে ব্যস্ত connection আর headroom
- Server × process × pool-কে max_connections-এর সাথে মেলানো
(সহজ) ১,২০,০০০ byte-এর একটি gzip করা JS ফাইল ১.৬৩৮৪ Mbps (সেকেন্ডে ১৬,৩৮,৪০০ bit) নেটওয়ার্কে নামতে কত সময় লাগে (RTT বাদে)?
- প্রায় ০.৫৯ s
- প্রায় ৭৩ ms
- প্রায় ৪.৭ s
- প্রায় ১.১৭ s
(সহজ) ৮,২৬০টি পণ্য, প্রতি পাতায় ২৫টি। মোট পাতা আর শেষ পাতার পণ্য কত?
- ৩৩১টি পাতা, শেষটিতে ১০
- ৩৩০টি পাতা, শেষটিতে ১০
- ৩৩১টি পাতা, শেষটিতে ২৫
- ৩৩০টি পাতা, শেষটিতে ০
(মাঝারি) ২,০০০টি সারি offset pagination-এ প্রতি পাতায় ৫০টি করে সব পাতা পড়া হলো; পাতা p পড়তে p × ৫০টি সারি ছুঁতে হয়। মোট কতটি সারি ছোঁয়া হলো?
- ২,০০০
- ৪০,০০০
- ৪১,০০০
- ৮০,০০০
(মাঝারি) ৩০টি অর্ডারের তালিকা; লুপে প্রতিটির customer আর items আলাদা SQL-এ আসে। প্রতিটি SQL আলাদা round trip, RTT ৫ ms, ডেটাবেসের কাজ বাদ। মোট SQL আর সময়?
- ৩১টি, ১৫৫ ms
- ৬১টি, ৩০৫ ms
- ৬০টি, ৩০০ ms
- ৩টি, ১৫ ms
(মাঝারি) Peak-এ ২,৪০০ SQL/s, সবই cache করা যায়; ডেটাবেস আরামে ৬০০ SQL/s নেয়। কমপক্ষে কত hit rate লাগবে?
- ৭৫%
- ২৫%
- ৮০%
- ৬০%
(মাঝারি) ৮০০ SQL/s, প্রতিটি SQL connection ১৫ ms ধরে রাখে। Little-এর সূত্রে গড়ে কতটি connection ব্যস্ত?
- প্রায় ৫৩
- ১২০
- ১২
- ১.২
(সহজ) DAU ১ লাখ, প্রতি জন দিনে ৩০টি অনুরোধ, peak factor ৫. Peak req/s প্রায় কত?
- ৩৫
- ১৭৪
- ৮৩৩
- ৪,১৬৭
(সহজ) Backoff delay ৫০০ ms, attempts ৪ (প্রথম চেষ্টাসহ), jitter নেই। শেষ চেষ্টা প্রথমটির কত ms পরে?
- ৭,৫০০ ms
- ১,৫০০ ms
- ২,০০০ ms
- ৩,৫০০ ms
(সহজ) একটি বাংলা SMS-এ ১৪০টি UCS-2 ঘর। ৭০-এর বেশি হলে প্রতি ভাগে ৬৭ ঘর ধরে কত ভাগে যাবে?
- ১টি
- ২টি
- ৩টি
- ৪টি
(মাঝারি) SLO ৯৯.৫%, ৩০ দিনের জানালা, মাসে ১০ লাখ request. Error budget মিনিটে আর ব্যর্থ request-এ কত?
- ২১৬ মিনিট, ৫,০০০টি
- ৪৩.২ মিনিট, ১,০০০টি
- ২১.৬ মিনিট, ৫০০টি
- ৩৬০ মিনিট, ৫,০০০টি
(মাঝারি) SLO ৯৯.৯%; গত এক ঘণ্টায় error ০.৬%. Burn rate কত, আর এই হারে ৩০ দিনের budget কত দিনে শেষ?
- ০.৬, ৫০ দিনে
- ৬, ৫ দিনে
- ৬০, ১২ ঘণ্টায়
- ৬, ৩০ দিনে
(কঠিন) Access token-এর exp − iat = ৯০০ s, verify-এ leeway ৬০ s, আর API-র ঘড়ি আসল সময়ের চেয়ে ৯০ s এগিয়ে। আসল সময়ে token প্রায় কতক্ষণ চলে?
- ৮৭০ s
- ৯৬০ s
- ৭৫০ s
- ১,০৫০ s
(মাঝারি) Docker build-এ npm ci ৪০ s, build ৫ s. দিনে ১০টি build, তার ৯টিতে শুধু কোড বদলায়, ১টিতে lockfile. ঠিক ক্রম (lockfile আগে কপি) আর ভুল ক্রমে (সব কোড আগে কপি) দিনে মোট RUN-এর সময়?
- ৫০ s আর ৪৫০ s
- ৯০ s আর ৪০৫ s
- ৯০ s আর ৪৫০ s
- ৪৫ s আর ৪৫০ s
(মাঝারি) Layout shift: ২০০ ms-এ ০.০৫, ৯০০-এ ০.০৪, ২,৫০০-এ ০.০৮, ৩,০০০-এ ০.০৩; কোনোটি ক্লিকের পরে নয়। ১ s ফাঁকে নতুন session window ধরে CLS কত?
- ০.২০
- ০.১১
- ০.০৯
- ০.০৮
(মাঝারি) চারটি page view-এর INP ৮০, ১৫০, ২১০ আর ৪০০ ms. Nearest-rank-এ p75 কত, আর রায় কী (ভালো ≤ ২০০, ৫০০ পর্যন্ত উন্নতি দরকার)?
- ২১০ ms, ভালো
- ১৫০ ms, ভালো
- ৪০০ ms, উন্নতি দরকার
- ২১০ ms, উন্নতি দরকার
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:
মনে রাখুন
- একক আগে: byte না bit, kB না KiB, ms না সেকেন্ড, গড় না peak. অর্ধেক ভুল এখানেই।
- ডেটাবেস দেখে শুধু miss আর লুপ: hit rate-এর শেষ কয়েক শতাংশ, N+1-এর প্রতিটি সারি, offset-এর প্রতিটি পাতা সরাসরি ভার বাড়ায়।
- Query-র সময়ই pool ঠিক করে: L = λ × W; ধীর query মানে বেশি connection, আর সীমা max_connections.
- ঢেউকে সময়ে ছড়ান: queue, jitter আর sliding window একই কাজ করে, চূড়া নামায়, মোট কাজ নয়।
- ছোট সংখ্যা, বড় ক্ষতি: এক পয়সা, একটি বাংলা শব্দ, ২০ সেকেন্ডের ঘড়ি, ২% retry. হিসাবটি লিখে দেখুন, তারপর চেকারে মেলান।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৯০ · MCQ প্রশ্নব্যাংক — ফুল-স্ট্যাক ওয়েবপরবর্তী পর্বপুরো কোর্স থেকে ২৬৪টি MCQ, প্রতিটি বিকল্পের ব্যাখ্যাসহ।
- ফুল-স্ট্যাক ওয়েব ফ্রেমওয়ার্ক · কোর্স হোম৯৩টি পর্বপরের পর্ব ৯০, MCQ প্রশ্নব্যাংক, আর পুরো রোডম্যাপ এখান থেকে।
- পর্ব ৮৭ · একটি অ্যাপ বড় করাআবার পড়ুনCapacity, Little-এর সূত্র, hit rate আর ঈদের সেলের queue এক গল্পে।
- পর্ব ৮৪ · Web performanceআবার পড়ুনLCP, INP, CLS, cache header আর compression-এর পুরো তত্ত্ব।
- পর্ব ৪২ · N+1 সমস্যাআবার পড়ুনinclude, select, query budget test আর EXPLAIN.
- পর্ব ৪৪ · Cache — Redisআবার পড়ুনCache-aside, invalidation, jitter আর stampede-এর পাহারা।
- System Design · Estimation অনুশীলনপাশের ট্র্যাকবড় ব্যবস্থার মোটা দাগের capacity হিসাব।
- Cloud & DevOps · Numerical অনুশীলনপাশের ট্র্যাকAvailability, SLO, CIDR আর খরচের অঙ্ক।
- সাইবার নিরাপত্তা · Numerical অনুশীলনপাশের ট্র্যাকEntropy, lockout, rate limit আর DDoS capacity.