এই পর্বে যা শিখবেন
- Scalability-র মাপা যায় এমন সংজ্ঞা, আর কোন load parameter কোন স্তরে চাপ দেয়
- Vertical আর horizontal scaling-এর সুবিধা, সীমা, খরচ আর availability-র তুলনা — সংখ্যায়
- Stateless সার্ভিস কেন horizontal scaling-এর চাবি, আর লুকানো state কোথায় থাকে
- Amdahl-এর সূত্র আর Universal Scalability Law — প্রমাণসহ চূড়ার হিসাব আর retrograde scaling
- Bottleneck স্তর খোঁজা, data স্তর কেন কঠিন, আর autoscaling-এর metric, নিয়ম, warm-up, cooldown ও headroom
১ · Scaling মানে কী — বেশি চাপ, একই latency, সহনীয় খরচ
একটি কাল্পনিক পরীক্ষার-ফল সেবার কথা ভাবুন। সারা বছর দিনে কয়েক হাজার মানুষ পাতাটি খোলেন। ফল প্রকাশের দিন সকাল ১০টায় কয়েক মিনিটের মধ্যে ঢোকেন কয়েক লাখ। প্রশ্ন একটাই: চাপ এতগুণ বাড়লে সেবা কি আগের মতোই দ্রুত উত্তর দেবে, আর তার জন্য খরচ কি অযৌক্তিক হারে বাড়বে? এটাই scalability-র প্রশ্ন, আর এই মডিউলের ছয়টি পর্ব এর উত্তর খোঁজে।
Scaling মানে চাপ (load) বাড়লেও latency — বিশেষ করে p99 — লক্ষ্যের ভেতরে রাখা, আর প্রতি অনুরোধের খরচ সহনীয় রাখা। তিনটি সংখ্যা একসাথে দেখতে হয়: কতটা চাপ, কত দ্রুত উত্তর, কত খরচে। একটি ধরে রেখে আরেকটি ছেড়ে দিলে সেটা scaling নয় — যেমন চাপ সামলাতে গিয়ে latency দশ গুণ হয়ে যাওয়া।
“আমাদের সিস্টেম কি scalable?” — এর উত্তর হ্যাঁ বা না নয়। পর্ব ০২-এ দেখেছেন, চাপকে কয়েকটি load parameter দিয়ে বর্ণনা করতে হয়: প্রতি সেকেন্ডে অনুরোধ, পড়া-লেখার অনুপাত, মোট ডেটা, একসাথে যুক্ত ব্যবহারকারী, fan-out. আসল প্রশ্ন: কোন সংখ্যা দ্বিগুণ হলে কোথায় টান পড়বে? প্রতিটি আলাদা স্তরে চাপ দেয় — পড়া বাড়লে cache আর replica, ডেটা বাড়লে sharding, খোলা সংযোগ বাড়লে সংযোগ সামলানোর স্তর (পর্ব ১০)।
Performance-এর সমস্যা মানে একজন ব্যবহারকারীর জন্যও সেবা ধীর। Scalability-র সমস্যা মানে একজনের জন্য দ্রুত, কিন্তু হাজারজনের ভিড়ে ধীর। প্রথমটির উত্তর ভালো কোড আর index; দ্বিতীয়টির উত্তর এই পর্বের দুই পথ।
ঈদের আগের দিন একটি রেস্তোরাঁয় অতিথি তিনগুণ। মালিকের হাতে দুটি পথ। এক: আরও বড় চুলা আর আরও দ্রুত হাতের প্রধান রাঁধুনি — রান্নাঘর একটিই, শুধু শক্তিশালী। দুই: আরও রাঁধুনি আর চুলা, সামনে একজন ম্যানেজার যিনি অর্ডার ভাগ করেন। দ্বিতীয় পথে দুটি নতুন ঝামেলা: সবাই একটিই ফ্রিজ থেকে মাল নিলে ফ্রিজের সামনে লাইন পড়ে; আর প্রত্যেককে যদি বাকি সবাইকে “লবণ শেষ” জানাতে হয়, তাহলে রাঁধুনি বাড়লে কথাই বাড়ে, রান্না নয়। এই পর্বের পুরো গল্প এই রান্নাঘরেই।
Horizontal scaling (scale out) = একই রকম আরও যন্ত্র যোগ করা
২ · Vertical scaling — বড় যন্ত্র, সহজ পথ, কিন্তু ছাদ আছে
Vertical scaling মানে একই সার্ভারকে আরও শক্তিশালী করা: বেশি CPU core, বেশি RAM, দ্রুত disk (NVMe SSD), দ্রুত network card. Cloud-এ এর মানে সাধারণত যন্ত্রের ধরন (instance type) বদলানো।
এর সুবিধাগুলো বড় আর প্রায়ই অবহেলিত। কোড প্রায় বদলাতে হয় না। সব ডেটা এক জায়গায়, তাই transaction, join আর strong consistency সহজ। Network-এর দেরি নেই, “অর্ধেক কাজ হয়ে বাকিটা আটকে যাওয়া” (partial failure, পর্ব ৩৮) নেই। বহু সেবা কখনো একটি ভালো যন্ত্রের সীমা ছোঁয় না। তবে একটি যন্ত্র কত সামলায়, তা কাজের ধরনভেদে বহু গুণ আলাদা — load test ছাড়া কোনো সংখ্যা বিশ্বাস করবেন না।
কিন্তু চারটি সীমা আছে:
🧱 হার্ডওয়্যারের ছাদ
সবচেয়ে বড় যন্ত্রেরও সীমা আছে। লেখার সময়ে সাধারণ বড় সার্ভারে core কয়েকশোর ঘরে আর memory কয়েক TB-র ঘরে (বিশেষ যন্ত্রে আরও বেশি); এর ওপরে আর “বড়” নেই।
সংখ্যা প্রদানকারী ও সময়ভেদে বদলায়📈 খরচের বাঁক
ছোট-মাঝারি আকারে দাম মোটামুটি আকারের সমানুপাতিক; সবচেয়ে বড়, বহু-socket, বিপুল-memory যন্ত্রে প্রতি একক ক্ষমতার দাম বেশি। Cloud-এ একই family-র ভেতরে দাম প্রায়ই সরলরেখায় বাড়ে — আসল বাঁক মূল্যতালিকায় নিজে মিলিয়ে নিন।
অংশ ৮-এ ধরে নেওয়া দামে হিসাব🔀 Core দ্বিগুণ ≠ কাজ দ্বিগুণ
একটি যন্ত্রের ভেতরেও thread-গুলো lock-এর জন্য লাইনে দাঁড়ায়, cache-এর তথ্য মেলায়, আর বড় যন্ত্রে দূরের socket-এর memory ধীর (NUMA). অংশ ৫-এর সূত্র যন্ত্রের ভেতরেও খাটে।
lock, cache coherence, NUMA⚠️ একটি যন্ত্র, একটি ঝুঁকি
যন্ত্রটিই single point of failure. আর বড় করতে প্রায়ই বিরতি লাগে: অনেক cloud-এ instance-এর ধরন বদলাতে যন্ত্রটি বন্ধ করে আবার চালু করতে হয় — কয়েক মিনিটের বিরতি, প্রদানকারী ও ধরনভেদে আলাদা। Database হলে এর মানে পুরো সেবা বন্ধ, যদি না আগে standby-তে সরে যান।
SPOF, resize-এর downtimeযন্ত্রের ভেতরের এই প্রতিযোগিতা বিস্তারিত দেখতে Operating Systems কোর্সের thread পর্ব আর Computer Architecture কোর্সের multicore আর cache coherence পর্ব দেখুন।
একটি সৎ নিয়ম — “আগে বড় যন্ত্র”: ছোট-মাঝারি সেবায়, বিশেষ করে database-এ (অংশ ৬), প্রথম পদক্ষেপ প্রায়ই বড় যন্ত্র; horizontal-এর জটিলতা কিনুন যখন ছাদ, availability বা খরচ বাধ্য করে। তবে availability-র জন্য অন্তত দুটি যন্ত্র লাগে — primary আর standby (পর্ব ০৫)।
৩ · Horizontal scaling — বেশি যন্ত্র, প্রায় সীমাহীন, কিন্তু সমন্বয়ের দাম
Horizontal scaling মানে একই রকম আরও যন্ত্র — প্রতিটি একটি instance — যোগ করা, আর সামনে একটি load balancer বসানো, যে অনুরোধগুলো ভাগ করে দেয় আর অসুস্থ instance-কে বাদ রাখে। কীভাবে ভাগ করে — algorithm, L4 বনাম L7, health check — সব পরের পর্বে (পর্ব ১৪)। যন্ত্রগুলো সাধারণত বাজারে সহজলভ্য, সাধারণ দামের commodity hardware.
ব্যাংকের কাউন্টারের কথা ভাবুন। এক পথ: একজন অসাধারণ দ্রুত ক্যাশিয়ার — তিনি অসুস্থ হলে পুরো শাখা বন্ধ। অন্য পথ: পাঁচটি কাউন্টার আর দরজায় টোকেন-যন্ত্র, যে প্রত্যেককে খালি কাউন্টারে পাঠায়। একটি কাউন্টার বন্ধ হলে লাইন একটু লম্বা হয়, কিন্তু শাখা চলে। টোকেন-যন্ত্রটিই load balancer. শর্ত একটাই: যেকোনো কাউন্টার যেকোনো গ্রাহকের কাজ করতে পারবে — অর্থাৎ গ্রাহকের ফাইল কোনো একজন ক্যাশিয়ারের ড্রয়ারে নয়, সবার নাগালের আলমারিতে।
অনুরোধগুলো যদি একে অপরের থেকে স্বাধীন হয়, ক্ষমতা প্রায় রৈখিক বাড়ে: ১০টি instance মানে প্রায় ১০ গুণ। সাথে আসে বাস্তবে প্রায় ছাদহীন ক্ষমতা, আপনা-আপনি redundancy (N+1, পর্ব ০৫), সেবা বন্ধ না করে একটি একটি করে নতুন সংস্করণ বসানো (rolling deployment), চাপ অনুযায়ী বাড়ানো-কমানো (অংশ ৭), আর একটি যন্ত্র নষ্ট হলে পুরো বন্ধের বদলে একটু কম ক্ষমতা।
দামও আছে। সামনে load balancer লাগে, সেটিও redundant. State সার্ভার থেকে সরাতে হয় (অংশ ৪)। ভাগ করা সবকিছু — counter, lock, cache — network-এ মেলাতে হয়, আর সেই খরচ যন্ত্রের সংখ্যার সাথে বাড়ে (অংশ ৫)। আর অনেক যন্ত্রের partial failure খুঁজতে লাগে ভালো observability (পর্ব ৫০)।
পাশাপাশি দুই পথ — বড় যন্ত্র বনাম বেশি যন্ত্র
Vertical (scale up): one bigger box · Horizontal (scale out): load balancer + identical stateless instances + shared state
| দিক | Vertical (scale up) | Horizontal (scale out) |
|---|---|---|
| সীমা | সবচেয়ে বড় যন্ত্রের ছাদ | ভাগ করা অংশ আর সমন্বয়ের খরচ (অংশ ৫) |
| খরচের ধরন | বড় আকারে প্রতি একক দামি | প্রায় রৈখিক, সাথে LB আর পরিচালনার খরচ |
| Availability | একটি যন্ত্র = SPOF; standby লাগে | N+1 সহজ; একটি গেলে ক্ষমতা কিছুটা কমে |
| বড় বা ছোট করা | প্রায়ই restart, ধীর | instance যোগ-বাদ, মিনিটে বা সেকেন্ডে |
| Consistency | সহজ — এক জায়গায় ডেটা | ভাগ করা state-এ কঠিন (পর্ব ২২, ৪০) |
| কোথায় বেশি মানায় | database, ছোট-মাঝারি সেবা, শুরুর দিন | stateless app, worker, বড় ও ওঠানামার চাপ |
৪ · Stateless সার্ভিস — horizontal scaling-এর চাবি
Horizontal scaling-এর মূল শর্ত: যেকোনো instance যেকোনো অনুরোধ সামলাতে পারবে। তাহলেই load balancer স্বাধীনভাবে ভাগ করে, autoscaler যেকোনোটি যোগ-বাদ দেয়, আর একটি crash করলে কেউ কিছু হারান না। এমন সার্ভিসকে বলে stateless. কথাটির অর্থ “state নেই” নয় — state সার্ভারের ভেতরে নেই। তা চলে গেছে database, cache বা একটি session store-এ, অথবা client-এর কাছে একটি স্বাক্ষরিত token-এ। কোথায় রাখবেন, sticky session কখন চলে, token-এর সুবিধা-অসুবিধা — সব পর্ব ১৫-এ; এখানে শুধু নীতিটি।
নিচের অ্যাপটি লগইনের session নিজের memory-তে রাখে। দ্বিতীয় সার্ভার যোগ করতেই কী ভাঙে, আর stateless নকশা কীভাবে মেটায় — ধাপে ধাপে:
| অনুরোধ | |
| কে সামলাল | |
| ফল |
- একটি সার্ভার, সব ঠিক। রিনা লগইন করে কার্টে ২টি বই রাখলেন। S1 তাঁর session (
r7: কে, কার্টে কী) নিজের memory-তে রাখল আর browser-কে cookie দিলsid=r7. সার্ভার একটিই, তাই memory-তে রাখা দিব্যি চলে। - ভিড় বাড়ল, S2 আর round-robin LB যোগ হলো। রিনার পরের অনুরোধ (
sid=r7) পালাক্রমে গেল S2-তে। S2-এর memory-তেr7নেই — সে রিনাকে চেনেই না। ফল: “আবার লগইন করুন”, কার্ট খালি। সার্ভার বাড়ানোয় ক্ষমতা বাড়ল, অথচ সেবা ভাঙল। - তাড়াহুড়োর সমাধান: sticky session. LB cookie দেখে রিনাকে সবসময় S1-এ পাঠায়। আবার চলে। কিন্তু যাঁরা আগে থেকে লগইন করা, তাঁরা সবাই S1-এ আটকে — S1 ব্যস্ত, S2 অলস; load আর সমান ভাগ হয় না।
- রাত ১১টায় S1 crash. (Autoscaler S1 সরালেও একই হতো।) S1-এর memory-র সব session মুছে গেল; LB রিনাকে S2-তে পাঠাল, যে তাঁকে চেনে না। S1-এর প্রত্যেক ব্যবহারকারী একসাথে লগআউট — sticky session সমস্যা লুকিয়েছিল, মেটায়নি।
- Stateless করা হলো। Session এখন একটি ভাগ করা session store-এ (যেমন Redis). রিনা আবার লগইন করলেন; নতুন session
r8গেল store-এ। এখন যে সার্ভারেই অনুরোধ যাক, সে cookie-র id দিয়ে store থেকে পড়ে নেয়। LB আবার সাধারণ round robin-এ ফিরল। - অবাধে বাড়ানো-কমানো। Autoscaler S3 যোগ করল আর S1 সরাল — রিনার পরের অনুরোধ গেল নতুন S3-এ, সে store থেকে
r8পড়ল, কার্টে ২টি বই ঠিকঠাক। কেউ লগআউট হলেন না। তবে session store এখন নিজেই stateful — তার redundancy আর scaling (পর্ব ০৫, ৩০) আপনার দায়িত্ব।
Session-ই একমাত্র লুকানো state নয়:
| লুকানো state | scale out করলে কী ভাঙে | কোথায় সরাবেন |
|---|---|---|
| লগইন session, কার্ট | অন্য instance ব্যবহারকারীকে চেনে না | session store বা স্বাক্ষরিত token (পর্ব ১৫) |
| Local disk-এ আপলোড করা ফাইল | অন্য instance-এ ফাইল নেই; instance মুছলে ফাইলও যায় | object storage (পর্ব ২৬) |
| Memory-তে counter বা rate limit | প্রতিটি আলাদা গোনে — ১০টি instance-এ সীমা কার্যত ১০ গুণ | ভাগ করা store-এ গণনা (পর্ব ৪৯) |
| নিজস্ব in-memory cache | প্রতিটির আলাদা, কখনো পুরোনো কপি | ছোট TTL হলে চলে; নইলে distributed cache (পর্ব ৩০) |
| প্রতিটি instance-এ নির্ধারিত সময়ের কাজ (cron) | একই কাজ N বার চলে — N বার ইমেইল | একটি scheduler বা queue (পর্ব ৩৭) |
| খোলা WebSocket সংযোগ | সংযোগটিই state; instance সরালে সবাই একসাথে reconnect | connection registry আর ধীরে draining (পর্ব ১০) |
ধারণাটি পুরোনো। ১৯৮৬ সালে database গবেষক Michael Stonebraker “The Case for Shared Nothing” লেখায় যুক্তি দেন: যে নকশায় node-রা CPU, memory বা disk কিছুই ভাগ করে না — shared-nothing — সেটিই সবচেয়ে সহজে বাড়ে। ২০১১ সালের দিকে Heroku-র সহ-প্রতিষ্ঠাতা Adam Wiggins-এর The Twelve-Factor App নির্দেশিকা web অ্যাপের জন্য একই কথা বলে: অ্যাপ চলবে stateless process হিসেবে, টিকে থাকা ডেটা যাবে আলাদা backing service-এ, আর scale out হবে process বাড়িয়ে।
Stateless app state-কে অদৃশ্য করে না, সরিয়ে দেয়। Session store বা database এখন সবার ভাগ করা অংশ — সম্ভাব্য single point of failure আর bottleneck; app যত সহজে বাড়ে, চাপ তত দ্রুত এখানে জমে।
৫ · Scaling-এর সীমা — Amdahl-এর সূত্র আর Universal Scalability Law
পর্ব ০১-এর একটি ভাবনার প্রশ্নে এক ঝলক দেখেছেন: সার্ভার দ্বিগুণ মানেই ক্ষমতা দ্বিগুণ নয়। এবার সূত্রে আর প্রমাণে। রান্নাঘরের দুটি ঝামেলা — ফ্রিজের লাইন আর সবাইকে “লবণ শেষ” জানানো — এদের নাম contention (σ) আর coherency (κ)।
Amdahl-এর সূত্র — ভাগ না-হওয়া অংশের দেয়াল
ধরুন একটি যন্ত্রে একগুচ্ছ কাজ শেষ হতে সময় লাগে T. এর একটি ভগ্নাংশ σ এমন, যা ভাগ করা যায় না — একটি ভাগ করা জিনিসের (একটি lock, একটি primary database-এর row) সামনে একে একে চলতেই হয়। একে বলে serial fraction. বাকি (1 − σ) অংশ N টি যন্ত্রে সমান ভাগ করা যায়। তাহলে N যন্ত্রে সময় T(σ + (1 − σ)/N), আর গতি বাড়ে:
N → ∞ হলে S → 1/σ
দ্বিতীয় রূপটি আসে লব-হরকে N দিয়ে গুণ করে: Nσ + (1 − σ) = 1 + σ(N − 1). এটাই Amdahl-এর সূত্র — IBM-এর প্রকৌশলী Gene Amdahl ১৯৬৭ সালের একটি সম্মেলনে যুক্তিটি দেন, সূত্রের রূপ পরে অন্যরা লেখেন। যত যন্ত্রই যোগ করুন, লাভ কখনো 1/σ ছাড়াবে না: σ = ৫% হলে সর্বোচ্চ ২০ গুণ, σ = ৩% হলে ৩৩.৩ গুণ। Throughput-এর ভাষায় একই কথা: C(N) = N যন্ত্রের throughput ÷ একটি যন্ত্রের throughput.
সমস্যার আকার যন্ত্রের সাথে বাড়লে — বেশি ব্যবহারকারী, তাই বেশি সার্ভার — ছবিটা আশাব্যঞ্জক; ১৯৮৮ সালে John Gustafson এই যুক্তি দেন (Gustafson-এর সূত্র, ভাবনার প্রশ্ন ২)।
Universal Scalability Law — যখন যন্ত্রগুলোকে কথাও বলতে হয়
Amdahl ধরে নেন, যন্ত্রগুলো একে অপরের খোঁজ রাখে না। বাস্তবে রাখতে হয়: মান বদলালে সবার cache থেকে পুরোনো কপি বাতিল, সবার সাথে সবার heartbeat, lock কার হাতে তা নিয়ে সম্মতি। N টি যন্ত্রে এমন জোড়া N(N − 1)টি — খরচ বাড়ে প্রায় N²-এর হারে। Neil Gunther এই খরচ যোগ করে মডেলটি প্রথম প্রকাশ করেন মোটামুটি ১৯৯৩ সালে, পরে Guerrilla Capacity Planning (২০০৭) বইয়ে বিস্তারিত লেখেন; নাম দেন Universal Scalability Law (USL). কোনো লেখায় দুটি ধ্রুবককে α আর β, কোথাও σ আর κ লেখা হয়; অর্থ একই।
লিখি C = N/D, যেখানে D = 1 + σ(N − 1) + κN(N − 1), আর D′ = σ + κ(2N − 1). ভাগফলের নিয়মে dC/dN = (D − N·D′)/D². লবটি খুলে দেখুন: D − N·D′ = 1 + σN − σ + κN² − κN − σN − 2κN² + κN = 1 − σ − κN². এটি N ছোট থাকলে ধনাত্মক (C বাড়ছে), আর κN² = 1 − σ হলে শূন্য। তাই চূড়া
N* = √((1 − σ)/κ)
N*-এর পর লব ঋণাত্মক — যন্ত্র যোগ করলে মোট throughput কমে। একে বলে retrograde scaling. κ = 0 হলে চূড়া নেই, সূত্রটি Amdahl-এ ফিরে যায়; আর σ = κ = 0 হলে C(N) = N, নিখুঁত রৈখিক।
| খরচ | Web সিস্টেমে কোথা থেকে আসে | কীভাবে কমে |
|---|---|---|
| σ — লাইনে অপেক্ষা | একটিমাত্র primary database, একটি জনপ্রিয় row-এর lock, একটি কেন্দ্রীয় counter, একটি global lock | ভাগ করে ফেলা (sharding, পর্ব ২৩), lock ছোট করা, কাজ queue-তে সরানো |
| κ — পরস্পরকে খবর দেওয়া | সব instance-কে cache বাতিলের বার্তা, সবার সাথে সবার heartbeat, বহু node-এর একই lock-এ লড়াই, প্রতিটি লেখা সব কপিতে নিশ্চিত করা | partition করে প্রত্যেককে নিজের ভাগ দেওয়া (পর্ব ১৬), gossip (পর্ব ৪৪), TTL দিয়ে বাতিল |
নিচের ক্যালকুলেটরে Amdahl আর USL পাশাপাশি। σ = ০.০৩ আর κ = ০.০০১ ধরে নেওয়া মান; বাস্তবে এগুলো load test-এর মাপ থেকে বের করা হয় (অংশ ৮-এর “আরও গভীরে”)।
// Throughput multiplier C(N) with N servers: Amdahl vs USL.
// sigma = contention (share of work that must queue for one shared thing),
// kappa = coherency (cost of every pair of servers keeping each other in sync).
const sigma = 0.03, kappa = 0.001; // ASSUMED - in practice fitted to a load test
const amdahl = (N) => N / (1 + sigma * (N - 1));
const usl = (N) => N / (1 + sigma * (N - 1) + kappa * N * (N - 1));
const nStar = Math.sqrt((1 - sigma) / kappa); // where dC/dN = 0
let best = 1;
for (let N = 1; N <= 64; N++) if (usl(N) > usl(best)) best = N;
console.log("Amdahl ceiling 1/sigma = " + (1 / sigma).toFixed(1) + "x");
console.log("USL peak sqrt((1-s)/k) = " + nStar.toFixed(2) +
" -> best whole N = " + best + ", C = " + usl(best).toFixed(2) + "x");
console.log(" N Amdahl USL USL/N note");
let prev = 0;
for (const N of [1, 2, 4, 8, 16, 24, best, 40, 48, 64]) {
const c = usl(N);
const note = N === best ? "peak" : (c < prev ? "retrograde (less than before)" : "");
console.log(String(N).padStart(4) + amdahl(N).toFixed(2).padStart(8) +
c.toFixed(2).padStart(8) + ((100 * c / N).toFixed(0) + "%").padStart(8) + " " + note);
prev = c;
}আউটপুট:
Amdahl ceiling 1/sigma = 33.3x
USL peak sqrt((1-s)/k) = 31.14 -> best whole N = 31, C = 10.95x
N Amdahl USL USL/N note
1 1.00 1.00 100%
2 1.94 1.94 97%
4 3.67 3.63 91%
8 6.61 6.32 79%
16 11.03 9.47 59%
24 14.20 10.70 45%
31 16.32 10.95 35% peak
40 18.43 10.72 27% retrograde (less than before)
48 19.92 10.29 21% retrograde (less than before)
64 22.15 9.25 14% retrograde (less than before)
এক: ছোট N-এ দুটি মডেল প্রায় সমান (৮টিতে 6.61 বনাম 6.32) — κ প্রায় অদৃশ্য, তাই কয়েকটি সার্ভারের পরীক্ষা দেখে ভবিষ্যৎ বলা বিপজ্জনক। দুই: চূড়া 31.14, সূত্র √(০.৯৭ ÷ ০.০০১) মিলে যায়; ৩১টি সার্ভারে মাত্র 10.95x — দক্ষতা ৩৫%. তিন: চূড়াটি চ্যাপ্টা — ২৪টিতেই 10.70x; শেষ ৭টি সার্ভার দিল মাত্র ২%. আর ৬৪টিতে 9.25x, প্রায় ১৬টির (9.47x) সমান — চার গুণ সার্ভারে কম কাজ। kappa ০.০০০১ করে চালিয়ে দেখুন চূড়া কোথায় সরে।
এবার বক্ররেখাটি বিন্দু ধরে ধরে পড়ি। প্রতিটি ধাপে হরের দুটি খরচ আলাদা করে দেখুন — কোনটি কখন বড় হয়ে ওঠে:
| N | |
| σ(N − 1) | |
| κN(N − 1) | |
| হর D | |
| USL C(N) | |
| Amdahl | |
| দক্ষতা C ÷ N | |
| বড় খরচ |
- N = ১: ভিত্তি। একটি সার্ভার নিজেই নিজের মাপকাঠি — C(1) = 1 ÷ 1 = ১ গুণ। লাইনও নেই, খবর দেওয়াও নেই — হরের দুটি খরচই শূন্য।
- N = ২: প্রায় নিখুঁত। হর = 1 + 0.03 + 0.002 = 1.032, তাই C = 2 ÷ 1.032 ≈ ১.৯৪ গুণ — দক্ষতা ৯৭%. Amdahl-ও প্রায় একই (১.৯৪)। দুটি সার্ভারের পরীক্ষা দেখে মনে হবে “রৈখিক!” — এটাই ফাঁদ।
- N = ৮: σ এগিয়ে। σ(N − 1) = 0.21, κN(N − 1) = 0.056 — লাইনের খরচ কথার খরচের প্রায় ৪ গুণ। C ≈ ৬.৩২ গুণ, Amdahl বলছে ৬.৬১। দুটি রেখা এখনো কাছাকাছি, কিন্তু দক্ষতা নেমে ৭৯%.
- N = ১৬: κ দ্রুত বাড়ছে। σ-এর খরচ বাড়ে N-এর হারে (0.45), κ-এর খরচ N²-এর হারে (0.24) — ৮ থেকে ১৬-তে σ দ্বিগুণের কিছু বেশি, κ চার গুণের বেশি। C ≈ ৯.৪৭, Amdahl ১১.০৩ — রেখা দুটি আলাদা হচ্ছে; দক্ষতা ৫৯%.
- N = ৩১: চূড়া। N* = √(0.97 ÷ 0.001) ≈ ৩১.১৪, আর পূর্ণসংখ্যায় সেরা ৩১: হর 1 + 0.90 + 0.93 = 2.83, C ≈ ১০.৯৫ গুণ। এখানে κN² ≈ 1 − σ — একটি নতুন সার্ভারের অবদান আর তার আনা বাড়তি সমন্বয়ের খরচ সমান। Amdahl তখনো ১৬.৩২ দেখাচ্ছে।
- N = ৪৮: retrograde. κ-এর খরচ (2.256) এখন σ-কে (1.41) ছাড়িয়ে গেছে। C নেমে ১০.২৯ — ৩১টির চেয়ে ১৭টি বেশি সার্ভার, অথচ কম কাজ।
- N = ৬৪: দ্বিগুণ খরচে কম কাজ। κN(N − 1) = 4.032, σ-এর (1.89) দ্বিগুণের বেশি; C ≈ ৯.২৫ গুণ, দক্ষতা মাত্র ১৪%. Amdahl দেখাচ্ছে ২২.১৫ — শুধু σ দেখলে আপনি আরও সার্ভার কিনতেন। শিক্ষা: মাপুন, σ আর κ বের করুন, চূড়ার অনেক আগে থামুন — বা κ কমানোর নকশা করুন।
৬ · Bottleneck কোন স্তরে — ভুল স্তর বাড়ালে কিছুই বদলায় না
ঈদের আগে ফেরিঘাটে বাসের লম্বা সিরিয়াল। কর্তৃপক্ষ ঢাকা থেকে আরও ৫০টি বাস ছাড়লেন — পার হওয়ার হার কি বাড়ল? না; ঘাটে ফেরি দুটিই, ঘণ্টায় তারা যতগুলো বাস তোলে ততগুলোই পার হয়। নতুন বাসগুলো শুধু সিরিয়াল লম্বা করল, আর ঘাটের রাস্তা আটকে ফেরিতে ওঠাও ধীর করল। ফেরিই এখানে bottleneck.
একটি অনুরোধ কয়েকটি স্তর পার হয় — load balancer, app, cache, database, বাইরের কোনো সেবা। পুরো পথের সর্বোচ্চ throughput ঠিক করে সবচেয়ে সরু স্তরটি (পর্ব ০৪-এর অংশ ৮)। একটি উদাহরণ, সংখ্যাগুলো ধরা যাক: প্রতিটি অনুরোধে database-এ ২টি query লাগে; database গ্রহণযোগ্য latency-তে সেকেন্ডে প্রায় ৪,০০০টি query সামলায় — তাই পুরো সেবা সর্বোচ্চ ২,০০০ অনুরোধ/সেকেন্ড। App স্তরে ৮টি instance, প্রতিটি ৪০০ অনুরোধ/সেকেন্ড পারে — মোট ৩,২০০। এখন ২,০০০ অনুরোধে database ১০০% ব্যস্ত, app মাত্র প্রায় ৬২%. App instance দ্বিগুণ করে ১৬টি করলে app-এর ক্ষমতা ৬,৪০০, কিন্তু throughput সেই ২,০০০-ই। আরও খারাপ: প্রতিটি instance-এর connection pool-এ ২০টি connection হলে database-এ ১৬০-এর বদলে ৩২০টি connection — বেশি লড়াই, database আরও ধীর। এটাই বাস্তবের retrograde scaling.
কীভাবে খুঁজবেন
প্রতিটি স্তরের জন্য তিনটি প্রশ্ন করুন — Brendan Gregg-এর প্রচলিত USE পদ্ধতি এভাবেই সাজানো: Utilization — কতটা ব্যস্ত (CPU, disk, connection)? Saturation — কাজ কি লাইনে জমছে (queue-র দৈর্ঘ্য, অপেক্ষার সময়)? Errors — timeout বা প্রত্যাখ্যান বাড়ছে কি? যে স্তরে লাইন জমছে, সেটিই bottleneck; সময় কোথায় যাচ্ছে তা স্তর ধরে দেখায় distributed tracing (পর্ব ৫০)।
| যা দেখছেন | সম্ভাব্য bottleneck | app instance বাড়ালে? |
|---|---|---|
| App CPU ৯০%, DB শান্ত, p99 বাড়ছে | App স্তর | সাহায্য করবে |
| App CPU ৩০%, DB CPU বা disk ৯৫%, query-র সময় বাড়ছে | Database | করবে না, বরং ক্ষতি |
| সবাই অলস, কিন্তু বাইরের payment API-র উত্তর ধীর | বাইরের নির্ভরতা | করবে না; timeout আর retry-নীতি (পর্ব ৪৮) |
Data স্তর কেন কঠিন
Stateless app-এর একটি কপি বানাতে লাগে শুধু একটি নতুন instance চালু করা। Database-এর কপি বানাতে লাগে ডেটাও কপি করা — কয়েক TB হলে কয়েক ঘণ্টা — আর তারপর প্রতিটি নতুন লেখা সব কপিতে পৌঁছে দেওয়া। লেখার চাপ ভাগ করতে ডেটা ভাগ করতে হয়, আর তখন দুই ভাগ জুড়ে transaction কঠিন (পর্ব ২৫)। এজন্যই data স্তরে পথটি সাধারণত ধাপে ধাপে, সস্তা থেকে দামি:
| সমস্যা | প্রথম চেষ্টা | তারপর | বিস্তারিত |
|---|---|---|---|
| ধীর query | index আর query ঠিক করা | বড় যন্ত্র (vertical) | পর্ব ২১ |
| পড়ার চাপ বেশি | cache | read replica | পর্ব ২৮, ২২ |
| লেখার চাপ বেশি | batching, queue দিয়ে ঢেউ সমান করা | sharding | পর্ব ৩১, ২৩ |
| ডেটা এক যন্ত্রে আঁটে না | পুরোনো ডেটা আলাদা storage-এ | sharding | পর্ব ২৬, ২৩ |
| connection অনেক বেশি | connection pooler, app-এর সর্বোচ্চ instance সীমা | — | অংশ ৭ |
Read replica-র দাম: replica সাধারণত primary-র একটু পেছনে থাকে — প্রোফাইল বদলে সঙ্গে সঙ্গে পাতা খুললে পুরোনো ছবি দেখা যেতে পারে। Data স্তর বাড়ানো মানে প্রায়ই consistency-র কিছুটা ছাড় (পর্ব ২২, ৪০)।
৭ · Autoscaling — চাপ অনুযায়ী নিজে থেকে বাড়া-কমা
পর্ব ০৩-এর হিসাবে peak-এর জন্য ৩২টি সার্ভার লেগেছিল, অথচ দিনের বেশিরভাগ সময় তারা প্রায় ১৮% ব্যস্ত। Autoscaling চাপ মেপে instance-এর সংখ্যা নিজে বাড়ায়-কমায় — cloud-এর elasticity-র সবচেয়ে পরিচিত রূপ। শর্ত: app স্তর stateless হতে হবে (অংশ ৪)।
কোন metric দেখে
- CPU utilisation: সবচেয়ে প্রচলিত; কিন্তু I/O-র অপেক্ষায় থাকা সার্ভিসে কম দেখায়, আর ১০০%-এ আটকে যায় বলে অতিরিক্ত চাপ কতটা তা বোঝায় না।
- Instance-প্রতি অনুরোধের হার: load balancer থেকে মাপা আসল চাহিদা, সীমায় আটকে যায় না; একটি instance কত পারে তা load test-এ জানা থাকলে (পর্ব ০৩) সবচেয়ে সরাসরি।
- Queue-র দৈর্ঘ্য: পেছনের worker-দের জন্য সেরা — জমে থাকা কাজ ÷ প্রতি worker-এর হার = কতজন লাগবে (পর্ব ৩১)।
- p99 latency: ব্যবহারকারীর অভিজ্ঞতার সবচেয়ে কাছের, কিন্তু এর মান অনেক ওঠানামা করে (noisy) আর দেরিতে আসে; আর latency database-এর জন্য বাড়লে app বাড়ানো ক্ষতি করে (অংশ ৬)। তাই এটি সতর্কসংকেত, একমাত্র নিয়ম নয়।
চার ধরনের নিয়ম
🎯 Target tracking
Target tracking: একটি metric একটি লক্ষ্যে ধরে রাখা — যেমন instance-প্রতি ৬০% ব্যস্ততা। নতুন সংখ্যা = ⌈বর্তমান সংখ্যা × বর্তমান মান ÷ লক্ষ্য⌉. Kubernetes-এর Horizontal Pod Autoscaler-এর ডকুমেন্টেশনে মূল সূত্রটি এমনই।
সাধারণ ব্যবহারের প্রথম পছন্দ🪜 Step scaling
Step scaling: সীমা কতটা ছাড়াল তার ধাপে কাজ — ৭০%-এর ওপরে ২টি যোগ, ৮৫%-এর ওপরে ৫টি; বড় ধাক্কায় বড় পদক্ষেপ।
জরুরি ধাক্কার জন্য বাড়তি নিয়ম🗓️ Scheduled
Scheduled scaling: ঘড়ি ধরে — ফল প্রকাশের কয়েক মিনিট আগে সর্বনিম্ন ৫২টি, ভিড় শেষে আবার কম; ঈদের আগের টিকিট বিক্রির মতো জানা ভিড়ে।
জানা সময়ের ভিড়🔮 Predictive
Predictive scaling: আগের দিন-সপ্তাহের ধাঁচ থেকে পূর্বাভাস করে warm-up-এর আগেভাগে বাড়ানো; নিয়মিত দৈনিক ঢেউয়ে ভালো।
নিয়মিত দৈনিক ধাঁচবাড়ান দ্রুত, কমান ধীরে — আর warm-up-এর দেরি
দুই দিকের ভুলের দাম সমান নয়: কম instance মানে ব্যবহারকারী ফেরত যান, বেশি মানে কয়েক মিনিটের বাড়তি ভাড়া। তাই scale out দ্রুত, scale in ধীরে। বারবার ওঠানামা (flapping) ঠেকাতে থাকে cooldown বা stabilization window: গত কয়েক মিনিটের সবচেয়ে বড় সুপারিশটি ধরে রাখা। লেখার সময়ে Kubernetes-এর HPA-তে কমানোর এই জানালার ডিফল্ট প্রায় ৫ মিনিট (সংস্করণভেদে বদলাতে পারে)। কমানোর সময় instance-কে হঠাৎ বন্ধ না করে আগে নতুন অনুরোধ বন্ধ, চলমানগুলো শেষ করতে দেওয়া — connection draining.
সবচেয়ে বড় সমস্যা warm-up. নতুন instance-কে চালু হতে হয় (virtual machine হলে প্রায়ই এক বা একাধিক মিনিট, container হলে সাধারণত কয়েক সেকেন্ড), তারপর প্রোগ্রাম load, connection pool খোলা, cache ভরা, JIT compile (চলার সময় কোডকে যন্ত্রের ভাষায় রূপান্তর) — আর load balancer-এর health check পরপর কয়েকবার পাস করা। ততক্ষণ পুরোনোরাই সব চাপ টানে। নিচের সময়রেখার সংখ্যা ধরা যাক — প্রতিটি instance ১০০ অনুরোধ/সেকেন্ড পর্যন্ত ভালো p99 দেয়, লক্ষ্য ৬০%, metric instance-প্রতি অনুরোধের হার:
- স্বাভাবিক
09:59৬০% - Breach
10:00চাপ দ্বিগুণ - সিদ্ধান্ত
10:01২০টি চাই - Warm-up
10:02চালু হচ্ছে - সামলে ওঠা
10:03আবার ৬০% - অপেক্ষা
10:20window - Scale in
10:25২০ → ১২
| ঘড়ি | |
| চাপ (অনুরোধ/সেকেন্ড) | |
| Instance | |
| Instance-প্রতি চাপ | |
| জমা বাড়তি অনুরোধ (ব্যর্থ বা খুব ধীর) |
- ০৯:৫৯ — স্বাভাবিক অবস্থা। ১০টি instance, মোট চাপ ৬০০ অনুরোধ/সেকেন্ড, অর্থাৎ instance-প্রতি ৬০ — ঠিক লক্ষ্যে। প্রতিটির হাতে ৪০ অনুরোধের headroom আছে; ছোটখাটো ঢেউ এতেই মিশে যায়।
- ১০:০০ — ফল প্রকাশ, চাপ দ্বিগুণ। হঠাৎ ১,২০০ অনুরোধ/সেকেন্ড, instance-প্রতি ১২০ — কিন্তু প্রতিটি পারে ১০০. প্রতি সেকেন্ডে ২০০টি অনুরোধ বাড়তি: লাইনে জমছে, ধীর হচ্ছে, কিছু timeout. Metric লক্ষ্য ছাড়িয়েছে (breach), কিন্তু autoscaler এখনো কিছু করেনি — একটি মুহূর্তের ঝাঁকুনিতে সাড়া দেওয়া ঠিক নয়।
- ১০:০১ — সিদ্ধান্ত। পরপর ৩টি ২০-সেকেন্ডের মাপ লক্ষ্যের ওপরে, তাই alarm. Target tracking: ⌈১০ × ১২০ ÷ ৬০⌉ = ২০, অর্থাৎ ১০টি নতুন চালু। লক্ষ করুন: metric যদি CPU হতো, তা ১০০%-এ আটকে থাকত — ⌈১০ × ১০০ ÷ ৬০⌉ = ১৭, কম পড়ত, আরেক দফা লাগত। এর মধ্যে জমেছে ২০০ × ৬০ = ১২,০০০ বাড়তি অনুরোধ।
- ১০:০২ — warm-up. নতুন ১০টি যন্ত্রে OS, প্রোগ্রাম, connection pool, cache চালু হচ্ছে, তারপর health check দুবার পাস করতে হবে। পুরোনো ১০টিই সব টানছে — বাড়তি অনুরোধ জমে ২৪,০০০.
- ১০:০৩ — সামলে ওঠা। ২০টি instance traffic নিচ্ছে, instance-প্রতি আবার ৬০. মোট ৩ মিনিটের বিপদ — ১ মিনিট সিদ্ধান্তে, ২ মিনিট warm-up-এ: ২০০ × ১৮০ = ৩৬,০০০ অনুরোধ ব্যর্থ বা খুব ধীর — autoscaling ঠিকঠাক কাজ করেও।
- ১০:২০ — চাপ কমল, কিন্তু অপেক্ষা। চাপ ৭০০, instance-প্রতি ৩৫. সুপারিশ: ⌈২০ × ৩৫ ÷ ৬০⌉ = ⌈১১.৬৭⌉ = ১২. কিন্তু stabilization window ৫ মিনিট — চাপ ফিরে এলে আবার warm-up-এর দেরি হতো; কয়েক মিনিটের বাড়তি ভাড়া সস্তা বিমা।
- ১০:২৫ — scale in, ধীরে। ৫ মিনিট ধরে সুপারিশ ১২-তেই, তাই ৮টি instance-কে load balancer থেকে সরানো হলো; ৩০ সেকেন্ড draining-এ চলমান অনুরোধ শেষ হলো, তারপর বন্ধ। এখন ৭০০ ÷ ১২ ≈ ৫৮ — লক্ষ্যের ভেতরে। ভিড়ের সময় জানা থাকলে আগেই বাড়িয়ে রাখা যেত — নিচের simulation সেটাই মাপে।
এবার একই ধরনের ঘটনা মিনিট ধরে, দুই নিয়মে। Reactive: শুধু target tracking. Scheduled: একই target tracking, সাথে একটি নির্ধারিত নিয়ম — ফল প্রকাশ (মিনিট ১০) জানা, তাই মিনিট ৬ থেকে ১৯ পর্যন্ত সর্বনিম্ন ৫২টি instance (পূর্বাভাসের peak প্রায় ৩,১০০ ÷ ৬০ ≈ ৫১.৭, ওপরে গোল)। চাপে ±৩% এলোমেলো ওঠানামা — seed বাঁধা, তাই প্রতিবার একই ফল। ৮৫%-এর বেশি ব্যস্ততায় সেই মিনিটের সব উত্তর “slow” (M/M/1-এ সেখানে অপেক্ষা সেবার সময়ের কয়েক গুণ, পর্ব ০৪), আর ক্ষমতার বেশি অংশ “drop”:
// Autoscaler, minute by minute. Exam results go live at minute 10 (known in advance).
// ASSUMED: 1 instance = 100 req/s at good p99; target utilisation 60%;
// a new instance needs WARM minutes (boot + warm-up) before it takes traffic.
const CAP = 100, TARGET = 0.6, WARM = 2;
let seed = 13; // mulberry32: seeded, repeatable
const rand = () => { seed = (seed + 0x6D2B79F5) | 0; let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
const shape = [9,9,9,9,9,9,9,9,9,9, 24,30,30,30,30,30,30,30,24,18, 14,12,11,10,10,10,10,10,10,10];
const load = shape.map(h => Math.round(h * 100 * (0.97 + 0.06 * rand()))); // req/s
function run(plan) { // plan = [from, to, at least N instances] or null
let ready = 15, boot = [], recs = [], out = [], slow = 0, drop = 0, cost = 0;
for (let t = 0; t < load.length; t++) {
ready += boot.filter(b => b === t).length; boot = boot.filter(b => b > t);
const cap = ready * CAP, u = load[t] / cap;
const d = Math.max(0, load[t] - cap) * 60, s = u > 0.85 ? Math.min(load[t], cap) * 60 : 0;
drop += d; slow += s; cost += ready + boot.length; // booting instances are paid too
out.push([ready, boot.length, u, s, d]);
let want = Math.ceil(load[t] / (CAP * TARGET)); // target tracking on minute t
if (plan && t + 1 >= plan[0] && t + 1 < plan[1]) want = Math.max(want, plan[2]);
recs.push(want);
const keep = Math.max(...recs.slice(-5)); // scale in: highest of last 5 min
if (want > ready + boot.length) while (ready + boot.length < want) boot.push(t + 1 + WARM);
else if (keep < ready && boot.length === 0) ready = keep;
}
return { out, slow, drop, cost };
}
const R = run(null), S = run([6, 20, 52]), k = (x) => Math.round(x / 1000) + "k";
const cell = (r) => (r[0] + (r[1] ? "+" + r[1] : "")).padStart(6) +
(Math.round(r[2] * 100) + "%").padStart(5) + k(r[3]).padStart(5) + k(r[4]).padStart(5);
console.log("min req/s │ R: inst util slow drop │ S: inst util slow drop");
for (let t = 4; t < 27; t++)
console.log(String(t).padStart(3) + String(load[t]).padStart(6) + " │" + cell(R.out[t]) + " │" + cell(S.out[t]));
for (const [name, x] of [["reactive ", R], ["scheduled", S]])
console.log(name + " slow " + k(x.slow) + ", dropped " + k(x.drop) + ", instance-minutes " + x.cost);আউটপুট (inst-এ “+” মানে warm-up-এ থাকা instance):
min req/s │ R: inst util slow drop │ S: inst util slow drop
4 875 │ 16 55% 0k 0k │ 16 55% 0k 0k
5 873 │ 16 55% 0k 0k │ 16 55% 0k 0k
6 878 │ 15 59% 0k 0k │ 16+36 55% 0k 0k
7 892 │ 15 59% 0k 0k │ 16+36 56% 0k 0k
8 903 │ 15 60% 0k 0k │ 52 17% 0k 0k
9 920 │ 15+1 61% 0k 0k │ 52 18% 0k 0k
10 2422 │ 15+1 161% 90k 55k │ 52 47% 0k 0k
11 3047 │ 16+25 190% 96k 87k │ 52 59% 0k 0k
12 2962 │ 16+35 185% 96k 82k │ 52 57% 0k 0k
13 2940 │ 41+10 72% 0k 0k │ 52 57% 0k 0k
14 3011 │ 51 59% 0k 0k │ 52 58% 0k 0k
15 2976 │ 51 58% 0k 0k │ 52 57% 0k 0k
16 3014 │ 51 59% 0k 0k │ 52 58% 0k 0k
17 3021 │ 51 59% 0k 0k │ 52 58% 0k 0k
18 2346 │ 51 46% 0k 0k │ 52 45% 0k 0k
19 1816 │ 51 36% 0k 0k │ 52 35% 0k 0k
20 1394 │ 51 27% 0k 0k │ 52 27% 0k 0k
21 1211 │ 51 24% 0k 0k │ 52 23% 0k 0k
22 1089 │ 51 21% 0k 0k │ 52 21% 0k 0k
23 976 │ 40 24% 0k 0k │ 52 19% 0k 0k
24 1015 │ 31 33% 0k 0k │ 31 33% 0k 0k
25 994 │ 24 41% 0k 0k │ 24 41% 0k 0k
26 975 │ 21 46% 0k 0k │ 21 46% 0k 0k
reactive slow 282k, dropped 224k, instance-minutes 943
scheduled slow 0k, dropped 0k, instance-minutes 1160
Reactive: মিনিট ১০-এ চাপ 2422, চালু মাত্র ১৫টি — ব্যস্ততা 161%. সিদ্ধান্ত হলো মিনিটের শেষে (⌈২৪২২ ÷ ৬০⌉ = ৪১, তাই 16+25), কিন্তু নতুনগুলো traffic নিল মিনিট ১৩-তে। তিন মিনিটে dropped 224k আর slow 282k. Scheduled: মিনিট ৮ থেকেই ৫২টি তৈরি — ভিড়ের দুই মিনিট আগে; ক্ষতি শূন্য, কিন্তু মিনিট ৮–৯-এ ব্যস্ততা মাত্র 17% — সেই অলস ক্ষমতার দাম 1160 বনাম 943 instance-মিনিট, প্রায় ২৩% বেশি। আর scale in লক্ষ করুন: মিনিট ২২ পর্যন্ত ৫১টি ধরে রাখা, তারপর ধাপে ধাপে 40 → 31 → 24 → 21 — ৫ মিনিটের জানালার সবচেয়ে বড় সুপারিশ ধরে। WARM বদলে দেখুন (অনুশীলন ৪)।
Autoscaler নিজেই যেন ক্ষতি না করে। সর্বোচ্চ সীমা রাখুন database-এর connection-এর হিসাবে, সর্বনিম্ন N+1 আর প্রতি zone-এ অন্তত একটি, আর সর্বোচ্চে পৌঁছালে alarm. আর হঠাৎ ভিড়ে বাড়তি অনুরোধ সস্তায় ফিরিয়ে দেওয়া (load shedding) বা ভিড়ের সারি (virtual waiting room) — এসব overload সামলানোর কৌশল পর্ব ১৮-এ।
৮ · Headroom, N+1, খরচ আর scale-এর একক
Autoscaling থাকলেও বাড়তি ক্ষমতা হাতে রাখতে হয়। Headroom-এর তিনটি স্তর আলাদা করে ভাবুন:
- Queue-র জন্য: utilisation ১০০%-এর কাছে গেলে অপেক্ষা বিস্ফোরিত হয় (M/M/1-এ মোট সময় S ÷ (1 − ρ): ρ = ০.৬-এ ২.৫S, ০.৯-এ ১০S; পর্ব ০৪), তাই peak-এ মোটামুটি ৫০–৭০% লক্ষ্য।
- ব্যর্থতার জন্য: একটি instance বা একটি পুরো zone হারালেও বাকিরা peak টানতে পারবে কি? এটাই N+1, N+2 বা zone-ভিত্তিক হিসাব (পর্ব ০৫, আর পর্ব ০৩-এর অংশ ৮)।
- প্রতিক্রিয়ার সময়ের জন্য: autoscaler সাড়া দিতে যতক্ষণ নেয়, ততক্ষণ চাপ যতটা বাড়তে পারে — বাড়তি ক্ষমতা ≥ চাপ বাড়ার হার × (সিদ্ধান্ত + warm-up). প্রতি মিনিটে ২০০ অনুরোধ/সেকেন্ড বাড়লে আর সাড়া দিতে ৪ মিনিট লাগলে অন্তত ৮০০ অনুরোধ/সেকেন্ড বাড়তি রাখতে হবে।
খরচের হিসাব — এক বড় বাক্স বনাম অনেক ছোট
অংশ ২-এর খরচের বাঁক আর পর্ব ০৫-এর k-of-n অঙ্ক একসাথে: peak-এ ৩২ একক ক্ষমতা লাগে। দাম আপেক্ষিক একক-এ আর ধরে নেওয়া: ৮ একক পর্যন্ত দাম আকারের সমানুপাতিক, তার ওপরে প্রতিবার আকার দ্বিগুণে প্রতি এককের দাম ১.৫ গুণ। যেকোনো একটি যন্ত্রের availability ধরা হলো ৯৯.৫% (crash আর patch-এর restart মিলিয়ে)। একাধিক যন্ত্রের সামনে একটি LB জোড়া (খরচ ২ একক, প্রতিটি ৯৯.৯%):
// One big machine or many small ones for the same peak? Cost and availability.
// ASSUMED price curve in RELATIVE units (not real prices): up to size 8 the price
// grows in step with size; above 8 every doubling costs 1.5x more per unit.
const price = (size) => size <= 8 ? size : size * Math.pow(1.5, Math.log2(size / 8));
const A1 = 0.995; // ASSUMED availability of ANY one machine (crash, patch reboot)
const LB = { cost: 2, a: 1 - 0.001 ** 2 }; // LB pair (or failover pair), 99.9% each
function atLeast(k, n, a) { // P(at least k of n machines are up)
let sum = 0, c = 1; // c = C(n, i)
for (let i = 0; i <= n; i++) {
if (i >= k) sum += c * a ** i * (1 - a) ** (n - i);
c = c * (n - i) / (i + 1);
}
return sum;
}
console.log("size price per unit");
for (const s of [1, 2, 4, 8, 16, 32, 64])
console.log(String(s).padStart(4) + price(s).toFixed(0).padStart(7) + (price(s) / s).toFixed(2).padStart(10));
const NEED = 32; // capacity units needed at peak
const designs = [["1 x 32 vertical", 32, 1], ["2 x 32 active-passive", 32, 2],
["4 x 8 no spare", 8, 4], ["5 x 8 N+1", 8, 5], ["34 x 1 N+2", 1, 34]];
console.log("\ndesign cost full peak (down/yr) half peak");
for (const [name, size, n] of designs) {
const k = NEED / size, extra = n > 1 ? LB.a : 1; // >1 machine: LB pair in front
const full = atLeast(k, n, A1) * extra, half = atLeast(Math.ceil(k / 2), n, A1) * extra;
const cost = n * price(size) + (n > 1 ? LB.cost : 0);
console.log(name.padEnd(23) + cost.toFixed(0).padStart(5) +
((100 * full).toFixed(3) + "% (" + ((1 - full) * 8760).toFixed(1) + " h)").padStart(21) +
((100 * half).toFixed(4) + "%").padStart(13));
}আউটপুট:
size price per unit
1 1 1.00
2 2 1.00
4 4 1.00
8 8 1.00
16 24 1.50
32 72 2.25
64 216 3.38
design cost full peak (down/yr) half peak
1 x 32 vertical 72 99.500% (43.8 h) 99.5000%
2 x 32 active-passive 146 99.997% (0.2 h) 99.9974%
4 x 8 no spare 34 98.015% (173.9 h) 99.9999%
5 x 8 N+1 42 99.975% (2.2 h) 99.9999%
34 x 1 N+2 36 99.933% (5.8 h) 99.9999%
ধরে নেওয়া দামে একটি ৩২-এককের যন্ত্র 72, অথচ চারটি ৮-এককের মোট 32 (LB-সহ 34). কিন্তু খালি 4 x 8-এর availability সবচেয়ে খারাপ — 98.015%, কারণ চারটির যেকোনো একটি গেলেই peak আর সামলানো যায় না। একটি বাড়তি যন্ত্র (5 x 8 N+1) মাত্র 42-এ দেয় 99.975% — বড় বাক্সের অর্ধেকের কিছু বেশি দামে বছরে ৪৩.৮-এর বদলে ২.২ ঘণ্টা। শেষ কলাম: বড় বাক্স হয় পুরো চালু, নয় পুরো বন্ধ (99.5000%); ছোট যন্ত্রের বহর অর্ধেক ক্ষমতায় প্রায় সবসময় টিকে থাকে। আর 34 x 1 N+2 (99.933%) 5 x 8 N+1-এর চেয়ে খারাপ: যন্ত্র বেশি হলে একসাথে কয়েকটি নষ্ট থাকার সম্ভাবনাও বাড়ে, তাই বাড়তি যন্ত্রও মোটের সাথে বাড়াতে হয়। হিসাবে নেই failover-এর সময় আর অনেক যন্ত্র চালানোর প্রকৌশল-খরচ।
Scale-এর একক — VM, container, serverless
একক যত হালকা, তত দ্রুত বাড়ানো যায়, warm-up-এর সমস্যা তত ছোট। সময়গুলো মোটামুটি মাত্রা; প্রদানকারী, image আর runtime-ভেদে বহু গুণ আলাদা:
| একক | চালু হতে মোটামুটি | সুবিধা | যা খেয়াল রাখবেন |
|---|---|---|---|
| Virtual machine | এক বা একাধিক মিনিট | পূর্ণ OS, কঠিন বিচ্ছিন্নতা | ধীর warm-up; আগেভাগে বাড়ানো জরুরি |
| Container | কয়েক সেকেন্ড (প্রোগ্রামের warm-up আলাদা) | হালকা, একই যন্ত্রে অনেকগুলো | যন্ত্রে জায়গা না থাকলে আগে নতুন VM লাগে |
| Serverless function | মিলিসেকেন্ড থেকে কয়েক সেকেন্ড | প্রতি অনুরোধে বাড়ে, অলস হলে শূন্য | cold start, সময়সীমা, প্রদানকারীর নিজস্ব সীমা |
Container, image আর orchestration কীভাবে deploy-এর অংশ হয়, তা পর্ব ৫৩-এ (Deployment আর release). ভেতরের প্রযুক্তি দেখতে Operating Systems কোর্সের container আর virtual machine পর্ব পড়ুন।
আরও গভীরে: USL-এর ধ্রুবক দুটি load test থেকে সরলরেখা টেনে পাওয়া যায় — সূত্র সাজালে (N ÷ C(N) − 1) ÷ (N − 1) = σ + κN, তাই কয়েকটি N-এ মাপা throughput থেকে এই রাশিটিকে N-এর বিপরীতে বসালে ছেদবিন্দু σ, ঢাল κ; Gunther-এর Guerrilla Capacity Planning (২০০৭) বইয়ে পদ্ধতিটি বিস্তারিত। Google-এর গবেষকদের “Autopilot: Workload Autoscaling at Google” (EuroSys ২০২০) দেখায়, অতীত ব্যবহারের ধাঁচ থেকে স্বয়ংক্রিয়ভাবে প্রতিটি কাজের CPU-memory সীমা (vertical) আর কপির সংখ্যা (horizontal) — দুটোই ঠিক করা যায়, আর তাতে হাতে বসানো সীমার চেয়ে অপচয় কমে।
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- Vertical আর horizontal scaling-এর পার্থক্য? — বড় যন্ত্র বনাম বেশি যন্ত্র; প্রথমটি সহজ কিন্তু ছাদ, দামের বাঁক আর SPOF আছে; দ্বিতীয়টিতে লাগে load balancer, stateless নকশা আর সমন্বয়।
- Stateless সার্ভিস কেন সহজে বাড়ে? — যেকোনো instance যেকোনো অনুরোধ নিতে পারে, কারণ state ভাগ করা store-এ; তাই LB ইচ্ছামতো ভাগ করে আর autoscaler ইচ্ছামতো যোগ-বাদ দেয়।
- ৫% কাজ serial হলে সর্বোচ্চ কত গুণ? — Amdahl: 1/σ = ২০ গুণ, যত যন্ত্রই দিন।
- USL-এ চূড়া কোথায়? — N* = √((1 − σ)/κ); σ = ০.০৩, κ = ০.০০১ হলে প্রায় ৩১টি যন্ত্রে, প্রায় ১১ গুণ; তার পর throughput কমে।
- Scale out দ্রুত, scale in ধীরে কেন? — কম ক্ষমতার দাম ব্যবহারকারী হারানো, বেশির দাম কয়েক মিনিটের ভাড়া; আর warm-up-এর দেরিতে আবার বাড়াতে সময় লাগে।
৯ · AI-এর যুগে scaling-এর নকশা যাচাই
“আমার অ্যাপকে ১ কোটি ব্যবহারকারীর জন্য scalable করার নকশা চাই” লিখলে AI নিমেষে Kubernetes, autoscaling, Kafka, Redis আর microservices-এর ছবি দেবে। কিন্তু কিছু ভুল প্রায় নিয়ম করে আসে:
| AI-এর ভুল | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
| “Kafka, Redis আর microservices যোগ করুন” | সেকেন্ডে ৫০টি অনুরোধে একটি ভালো যন্ত্রই যথেষ্ট; প্রতিটি নতুন অংশ খরচ আর ব্যর্থতার পথ বাড়ায় | peak QPS জিজ্ঞেস করুন; প্রতিটি অংশ কোন সমস্যা মেটায়, লিখে নিন |
| রৈখিক scaling ধরে নেওয়া — “১০টি সার্ভার = ১০ গুণ” | ভাগ করা database, lock আর সমন্বয় (σ, κ) উপেক্ষা | ভাগ করা অংশ খুঁজুন; load test করে USL বসান |
| হিসাবের ভুল | ১০০% ক্ষমতা দিয়ে instance গোনে, লক্ষ্য-utilisation বা N+1 বাদ দেয়, ওপরে গোল করে না | নিজে আবার হিসাব; অংশ ৭-এর মতো ছোট simulation |
| “Autoscaling সব ভিড় সামলাবে” | warm-up, metric-এর দেরি আর database-এর connection সীমা উপেক্ষা | প্রতিক্রিয়ার সময় × চাপ বাড়ার হার হিসাব; জানা ভিড়ে scheduled নিয়ম |
| “Database-ও app-এর মতো scale out করুন, replica দিলে ডেটা consistent থাকবে” | data স্তর কঠিন; replica পিছিয়ে থাকে; ACID-এর consistency আর replica-গুলোর consistency (CAP অর্থে) আলাদা জিনিস | replication-এর ধরন, lag আর কোন পড়া primary থেকে যাবে জিজ্ঞেস করুন |
| বানানো সীমা বা দাম | “সবচেয়ে বড় instance-এ এত core, ঘণ্টায় এত টাকা” — আত্মবিশ্বাসী কিন্তু প্রায়ই পুরোনো বা ভুল | প্রদানকারীর অফিসিয়াল ডকুমেন্টেশন আর মূল্যতালিকা |
AI-কে কাজে লাগান হিসাব যাচাই আর ঝুঁকির তালিকায়; সংখ্যা আর অনুমান নিজে দিন, আর উত্তরে অনুমানগুলো আলাদা করে চান:
AI-এর দেওয়া scaling-নকশা হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১“আগে বড় যন্ত্র, পরে বেশি যন্ত্র” — অভিজ্ঞ প্রকৌশলীরা কেন বলেন, vertical scaling-কে অবহেলা করবেন না?
কারণ আজকের একটি সার্ভার বিশাল: কয়েক ডজন থেকে কয়েকশো core, শত শত GB থেকে কয়েক TB memory, আর NVMe disk; বহু সেবার কাজের ডেটা একটি যন্ত্রের memory-তেই এঁটে যায়। এক যন্ত্রে network-এর দেরি নেই, distributed transaction নেই, partial failure নেই — নকশা, পরীক্ষা আর debug সবই সহজ।
একটি প্রকাশিত উদাহরণ: Stack Overflow-এর প্রকৌশলী Nick Craver ২০১৬ সালে তাদের architecture নিয়ে লেখায় জানান, বিশ্বের ব্যস্ততম সাইটগুলোর একটি হয়েও তারা চলত মোটামুটি এক ডজন web server আর হাতে গোনা কয়েকটি বড়, বিপুল memory-র SQL Server যন্ত্রে। এটি সেই সময়ের অবস্থা, পরে বদলাতে পারে।
গবেষণাতেও একই সতর্কবার্তা। Frank McSherry, Michael Isard আর Derek Murray-র “Scalability! But at what COST?” (HotOS ২০১৫) প্রবন্ধে দেখা যায়, কিছু প্রকাশিত distributed graph-processing সিস্টেম বহু core-এ যে কাজ করছিল, একটি ল্যাপটপের একটি thread-এ যত্ন করে লেখা কোড তা সমান বা আরও দ্রুত করে। তাঁদের প্রস্তাবিত মাপ COST: কতগুলো core লাগলে distributed সিস্টেমটি একটি ভালো single-thread সমাধানকে হারায় — কিছু ক্ষেত্রে উত্তর ছিল “কখনোই না”। অর্থাৎ “scalable” মানেই “দ্রুত” নয়; সমন্বয়ের খরচ (অংশ ৫) শুরুতেই অনেকটা খেয়ে ফেলে।
তবু horizontal কেন? তিন কারণে: চাপ সবচেয়ে বড় যন্ত্রের ছাদ ছাড়ালে; availability-র জন্য — একটি যন্ত্র SPOF, তাই database-এও অন্তত primary আর standby লাগে; আর বড় আকারে দামের বাঁক ও ওঠানামার চাপের জন্য। ব্যবহারিক মিশ্রণটি প্রায়ই এমন: stateless app স্তর শুরু থেকেই কয়েকটি instance-এ (সস্তা, সহজ), আর database যতদিন সম্ভব বড় যন্ত্র আর standby-তে।
মূল উপলব্ধি: scale out একটি দাম দিয়ে কেনা ক্ষমতা। আগে মাপুন, একটি যন্ত্রকে পুরো কাজে লাগান, তারপর ভাগ করুন — আর ভাগ করার সিদ্ধান্তটি নিন ছাদ, availability বা খরচের সংখ্যা দেখে।
প্র ০২Amdahl (১৯৬৭) বলেন লাভ সীমিত, Gustafson (১৯৮৮) বলেন প্রায় রৈখিক — web সেবার জন্য কার কথা খাটে?
১৯৬৭ সালের AFIPS Spring Joint Computer Conference-এ IBM-এর Gene Amdahl একটি ছোট প্রবন্ধে যুক্তি দেন: প্রতিটি কাজের কিছু অংশ একে একে চলতেই হয় — ডেটা গোছানো, ফল জোড়া — তাই বহু প্রসেসর জুড়ে লাভ সীমিত। পরিচিত সূত্রটি পরে অন্যরা তাঁর যুক্তি থেকে লেখেন, আর বহু বছর এটি massively parallel যন্ত্রের বিরুদ্ধে যুক্তি হিসেবে ব্যবহৃত হয়।
১৯৮৮ সালে Sandia National Laboratories-এর John Gustafson Communications of the ACM-এ “Reevaluating Amdahl's Law” লেখেন। তাঁর দল ১,০২৪-প্রসেসরের একটি যন্ত্রে কয়েকটি বৈজ্ঞানিক সমস্যায় প্রায় ১,০০০ গুণের কাছাকাছি লাভ দেখায়। কৌশল: সমস্যার আকার স্থির না রেখে যন্ত্রের সাথে বাড়ানো — বড় যন্ত্র পেলে মানুষ বড় সমস্যাই সমাধান করে। তখন serial অংশ মোট কাজের তুলনায় ছোট হয়ে যায়, আর লাভ দাঁড়ায় প্রায় N − σ(N − 1). স্থির আকারের প্রশ্নটিকে বলে strong scaling, আকার বাড়ানোরটিকে weak scaling.
Web সেবায় দুটোই আছে। Throughput-এর প্রশ্ন — “দ্বিগুণ ব্যবহারকারী এলে কী হবে?” — Gustafson-ধাঁচের: অনুরোধগুলো স্বাধীন, তাই stateless app স্তর প্রায় রৈখিক বাড়ে। একটি অনুরোধের latency — যেমন একটি search query কয়েকশো shard-এ ভাগ করা — Amdahl-ধাঁচের: ফল জোড়ার ধাপ আর সবচেয়ে ধীর shard (tail latency, পর্ব ০৪) লাভ আটকে দেয়। আর ব্যবহারকারী বাড়লেও সবাই যদি একটিই primary database-এ লেখেন, সেই ভাগ করা অংশ Amdahl-কে ফিরিয়ে আনে; সাথে USL-এর κ সমন্বয়ের খরচ যোগ করে।
দুই সূত্রের পূর্ণ তুলনা, hardware-এর দৃষ্টিতে, আছে Computer Architecture কোর্সের parallel architecture পর্বে।
মূল উপলব্ধি: আগে প্রশ্নটি ঠিক করুন — একই কাজ দ্রুত করা (strong), নাকি একই সময়ে বেশি কাজ (weak)? System design-এ সাধারণত দ্বিতীয়টি, কিন্তু ভাগ করা অংশটি খুঁজে না দেখলে প্রথমটির দেয়ালে ধাক্কা লাগবেই।
প্র ০৩সার্ভার যোগ করলে throughput উল্টো কমে যায় — retrograde scaling বাস্তবে কোথায় দেখা যায়, আর কীভাবে এড়াবেন?
সবচেয়ে পরিচিত দৃশ্য database connection. ধরা যাক প্রতিটি app instance-এর pool-এ ২০টি connection: ১০টি instance মানে database-এ ২০০টি, ৫০টি মানে ১,০০০টি। PostgreSQL প্রতিটি connection-এর জন্য একটি আলাদা OS process চালায়; একসাথে বহু query চললে তারা lock, latch, CPU cache আর context switch নিয়ে লড়াই করে, আর একটি সীমার পর মোট throughput কমে। PostgreSQL-এর community wiki শুরুর অনুমান হিসেবে সক্রিয় connection-এর সংখ্যা মোটামুটি “core-এর দ্বিগুণ + কার্যকর disk-এর সংখ্যা” রাখার কথা বলে (কাজভেদে আলাদা)। এজন্যই PgBouncer-এর মতো connection pooler, আর autoscaler-এ সর্বোচ্চ instance-এর সীমা।
দ্বিতীয় উৎস “সবাই সবাইকে জানায়” নকশা। প্রতিটি instance নিজের local cache রাখে আর কোনো মান বদলালে বাকি সবাইকে বাতিলের বার্তা পাঠায়: N টি instance-এ প্রতিটি পরিবর্তনে N − ১টি বার্তা, আর সবাই লিখলে মোট প্রায় N². সবার সাথে সবার heartbeat, বহু node-এর একটি distributed lock-এ লড়াই (পর্ব ৪৩) — একই ধাঁচ। একটি যন্ত্রের ভেতরেও তাই: বহু core একই cache line লিখলে coherence-এর বার্তায় গতি নামে (Computer Architecture-এর multicore-coherence পর্ব)। USL-এর κ ঠিক এই খরচ।
Neil Gunther মডেলটি প্রথম প্রকাশ করেন মোটামুটি ১৯৯৩ সালে, আর পরে Guerrilla Capacity Planning (২০০৭) বইয়ে বহু মাপা সিস্টেমের ডেটায় σ আর κ বসিয়ে দেখান; তাঁর উদাহরণে কিছু সিস্টেমের মাপা throughput সত্যিই চূড়ায় উঠে নেমে আসে।
কী করবেন: load test-এ N বাড়িয়ে মাপুন আর σ, κ বের করুন; চূড়ার অনেক আগে কাজ চালান; κ কমান — সবাইকে বার্তার বদলে ছোট TTL, broadcast-এর বদলে partition করে প্রত্যেককে নিজের ভাগ (consistent hashing, পর্ব ১৬), সবার সাথে সবার বদলে gossip (পর্ব ৪৪); আর σ কমান — একটি জনপ্রিয় row বা counter-কে ভাগ করে।
মূল উপলব্ধি: সার্ভার বাড়িয়েও throughput না বাড়লে প্রথম প্রশ্ন হলো, “কে কার জন্য অপেক্ষা করছে, আর কে কাকে খবর দিচ্ছে?” — প্রথমটি σ, দ্বিতীয়টি κ.
প্র ০৪Autoscaling কি হঠাৎ ভিড়ের সমাধান? পরীক্ষার ফল প্রকাশ বা ঈদের আগের টিকিট বিক্রির মতো ঘটনায় কী করা উচিত?
Autoscaling প্রতিক্রিয়া দেখায় — ভিড় এসে metric না বাড়া পর্যন্ত কিছু করে না। মোট দেরি = metric জমা হওয়া + মূল্যায়নের সময় + instance চালু + warm-up + health check. অংশ ৭-এর simulation-এ তা ৩ মিনিট, আর তাতেই প্রায় ২.২৪ লাখ অনুরোধ বাদ পড়ল। ফল প্রকাশের মুহূর্তে ভিড় আসে সেকেন্ডে — একে বলে flash crowd — আর সবাই প্রায় একসাথে পাতা খোলেন, বারবার refresh করেন (thundering herd). সুখবর হলো, এমন ভিড়ের সময় সাধারণত আগে থেকেই জানা।
তাই প্রথম উত্তর ক্যালেন্ডার: scheduled scaling দিয়ে আগেভাগে বাড়ানো, cache আগে থেকে ভরে রাখা, আর load balancer বা CDN-এর মতো ভাগ করা স্তরের প্রস্তুতি প্রদানকারীর ডকুমেন্টেশনে মিলিয়ে নেওয়া। দ্বিতীয় উত্তর কাজ কমানো: ফলের পাতাগুলো আগেই তৈরি করে static ফাইল বানিয়ে CDN-এ রাখা (static pre-generation, পর্ব ১৭), যাতে প্রতিটি অনুরোধ database পর্যন্ত না যায়। তৃতীয় উত্তর ভিড়কে নিয়ন্ত্রণে আনা: ভিড়ের সারি, load shedding আর ভারী feature সাময়িক বন্ধ রেখে graceful degradation (পর্ব ১৮)।
Autoscaling-এর নিজের ফাঁদও আছে। Latency-ভিত্তিক নিয়ম যদি database ধীর হওয়ায় app instance বাড়ায়, বাড়তি instance বাড়তি connection খোলে, database আরও ধীর হয়, latency আরও বাড়ে — একটি ধনাত্মক feedback-চক্র, যা metastable failure-এর দিকে ঠেলে দিতে পারে। তাই সর্বোচ্চ সীমা, কারণ-ভিত্তিক metric আর সীমায় পৌঁছালে alarm.
Predictive scaling আগের দিনের ধাঁচ শিখে আগেভাগে বাড়ায় — প্রতিদিন রাত ৮টার ঢেউয়ে চমৎকার। কিন্তু বছরে একবারের ঘটনা ইতিহাসে প্রায় থাকে না; সেখানে মানুষের দেওয়া সময়সূচিই বেশি নির্ভরযোগ্য।
মূল উপলব্ধি: autoscaling ধীরে বাড়া চাপের উত্তর। হঠাৎ কিন্তু জানা ভিড়ের উত্তর হলো পরিকল্পনা, কাজ কমানো আর নিয়ন্ত্রিত প্রবেশ — autoscaling তার ওপরে একটি নিরাপত্তা-জাল মাত্র।
প্র ০৫Cloud-এর elasticity কি সবসময় টাকা বাঁচায়? কখন স্থির ক্ষমতা বা নিজের যন্ত্র সস্তা হতে পারে?
Elasticity-র প্রতিশ্রুতি: যতক্ষণ দরকার ততক্ষণ যন্ত্র, শুধু ততটুকুরই দাম। লাভ নির্ভর করে peak আর গড়ের অনুপাতের ওপর। অংশ ৭-এর simulation-এ reactive নিয়ম ৩০ মিনিটে ৯৪৩ instance-মিনিট খরচ করল; পুরো সময় peak-এর ৫২টি রাখলে লাগত ৫২ × ৩০ = ১,৫৬০ — প্রায় ৪০% সাশ্রয়, scheduled নিয়মে (১,১৬০) প্রায় ২৬%. কিন্তু চাপ যদি দিনরাত প্রায় সমান হয়, বাড়ানো-কমানোর কিছু থাকে না, তাই সাশ্রয়ও প্রায় শূন্য।
লুকানো খরচও আছে। অনেক প্রদানকারী দীর্ঘমেয়াদি চুক্তিতে প্রতি ঘণ্টার দাম কম রাখে (শর্ত ও হার প্রদানকারী-ভেদে আলাদা), তাই একটি প্রচলিত মিশ্রণ: স্থির ভিত্তি-চাপ চুক্তিতে, ওঠানামার অংশ চাহিদামতো। এর বাইরে ডেটা বাইরে পাঠানোর (egress) খরচ, warm-up-এর জন্য সারাক্ষণ বাড়তি headroom, আর ভয়ে কখনো scale in না করার অভ্যাস — elasticity-র লাভ নিঃশব্দে খেয়ে ফেলে।
প্রকাশিত দুটি উদাহরণ: Dropbox ২০১৬ সালে তাদের engineering blog-এ জানায়, তারা ব্যবহারকারীদের বেশিরভাগ ফাইল-ডেটা Amazon S3 থেকে নিজেদের তৈরি storage ব্যবস্থায় (Magic Pocket) সরিয়েছে — তাদের যুক্তি ছিল, বিশাল আর অনুমানযোগ্য চাপে নিজের hardware-এ খরচ আর নিয়ন্ত্রণ দুটোই ভালো। 37signals (Basecamp আর HEY-এর নির্মাতা) ২০২২–২৩ সালে cloud ছেড়ে নিজেদের কেনা সার্ভারে ফেরার ঘোষণা দেয় আর তাদের নিজেদের হিসাবে বড় সাশ্রয়ের দাবি করে। দুটিই তাদের নিজেদের পরিস্থিতির সিদ্ধান্ত; অনিশ্চিত, দ্রুত বাড়ন্ত চাপের নতুন সেবায় আগেভাগে যন্ত্র কেনা ঝুঁকিপূর্ণ।
মাপুন: প্রতি ১,০০০ অনুরোধের খরচ, সপ্তাহজুড়ে utilisation আর প্রকৌশল-সময়ের দাম।
মূল উপলব্ধি: elasticity একটি সুবিধা, দান নয়। ওঠানামা বেশি হলে লাভ, চাপ স্থির হলে স্থির ক্ষমতা সস্তা হতে পারে — অনুমান নয়, সংখ্যা দিয়ে সিদ্ধান্ত নিন।
অনুশীলন
খাতা-কলম আর উপরের ক্যালকুলেটরগুলো পাশে রাখুন। আগে নিজে করুন, তারপর উত্তর মিলিয়ে নিন।
-
USL-এর চূড়া: একটি সেবার load test থেকে পাওয়া গেল σ = ০.০২ আর κ = ০.০০০৪. (ক) কতটি সার্ভারে throughput সবচেয়ে বেশি? (খ) সেখানে C(N) কত, দক্ষতা কত, আর একই N-এ Amdahl কী বলত? (গ) নকশা বদলে κ অর্ধেক করা গেলে চূড়া কোথায় আর কত উঁচু? (ঘ) κ না বদলে σ অর্ধেক করলে?
(ক) N* = √((১ − ০.০২) ÷ ০.০০০৪) = √২,৪৫০ ≈ ৪৯.৫. পূর্ণসংখ্যায় ৪৯ আর ৫০ — দুটিতেই C ≈ ১৬.৮৯ (চূড়াটি ঠিক মাঝখানে বলে দুটি সমান)।
(খ) N = ৫০: হর = ১ + ০.০২ × ৪৯ + ০.০০০৪ × ৫০ × ৪৯ = ২.৯৬, তাই C ≈ ১৬.৮৯ গুণ, দক্ষতা প্রায় ৩৩.৮%. Amdahl বলত ৫০ ÷ ১.৯৮ ≈ ২৫.২৫ গুণ — প্রায় ৫০% বেশি আশাবাদী।
(গ) κ = ০.০০০২: N* = √(০.৯৮ ÷ ০.০০০২) = √৪,৯০০ = ৭০. হর = ১ + ০.০২ × ৬৯ + ০.০০০২ × ৭০ × ৬৯ = ১ + ১.৩৮ + ০.৯৬৬ = ৩.৩৪৬, তাই C(৭০) ≈ ২০.৯২ গুণ।
(ঘ) σ = ০.০১: N* = √(০.৯৯ ÷ ০.০০০৪) ≈ ৪৯.৭৫ — চূড়া প্রায় একই জায়গায়! কিন্তু উঁচু: C(৫০) = ৫০ ÷ (১ + ০.৪৯ + ০.৯৮) = ৫০ ÷ ২.৪৭ ≈ ২০.২৪ গুণ। শিক্ষা: κ কমালে চূড়া ডানে সরে (আরও সার্ভার কাজে লাগে), σ কমালে চূড়া প্রায় একই জায়গায় থেকে উঁচু হয়। ক্যালকুলেটরে
sigmaআরkappaবদলে মিলিয়ে নিন। -
কোন পথ — vertical না horizontal? তিনটি কাল্পনিক পরিস্থিতিতে সিদ্ধান্ত আর কারণ লিখুন। (ক) একটি সমবায় সমিতির হিসাবের অ্যাপ: ৫০ জন কর্মী, PostgreSQL, প্রতিটি লেনদেনে কড়া নির্ভুলতা লাগে, চাপ সারাদিন প্রায় সমান, database যন্ত্রের CPU ৭০%. (খ) ছবি ছোট করার (image resize) একটি API: প্রতিটি অনুরোধ স্বাধীন আর CPU-ভারী, দিনে কয়েকবার হঠাৎ ১০ গুণ ভিড়। (গ) একটি বাড়ন্ত অনলাইন দোকানের primary database: ইতিমধ্যে বড় যন্ত্রে, ৯০% অনুরোধ পড়া, peak-এ CPU ৮০%, আর চাপ প্রতি মাসে ১০% বাড়ছে।
(ক) Vertical, সাথে standby. চাপ ছোট আর স্থির, consistency সবচেয়ে জরুরি। এক ধাপ বড় যন্ত্র ৭০%-কে আরামদায়ক জায়গায় নামাবে, কোডও বদলাতে হবে না। Availability-র জন্য একটি standby replica আর নিয়মিত backup — কিন্তু লেখা ভাগ করার মতো horizontal জটিলতা এখানে অকারণ।
(খ) Horizontal, stateless worker আর autoscaling. অনুরোধ স্বাধীন, তাই প্রায় রৈখিক বাড়ে। কাজ একটি queue-তে (পর্ব ৩১), autoscaling-এর metric queue-র দৈর্ঘ্য, ছবি object storage-এ (পর্ব ২৬); হঠাৎ ভিড়ের জন্য দ্রুত চালু হয় এমন একক, যেমন container.
(গ) মিশ্র, ধাপে ধাপে. আগে সময়ের হিসাব: মাসে ১০% বাড়লে ৮০% থেকে ১০০%-এ পৌঁছাতে লাগে ln(১.২৫) ÷ ln(১.১) ≈ ২.৩ মাস. তাৎক্ষণিক: query আর index ঠিক করা (পর্ব ২১), cache (পর্ব ২৮) আর read replica (পর্ব ২২) — ৯০% পড়া সরলে primary অনেক হালকা। দরকারে আরেক ধাপ বড় যন্ত্র: সত্যিই দ্বিগুণ ক্ষমতা দিলে ৪০% থেকে ১০০% ছুঁতে লাগে ln(২.৫) ÷ ln(১.১) ≈ ৯.৬ মাস। সেই সময়ে লেখার জন্য sharding-এর (পর্ব ২৩) পরিকল্পনা শুরু।
-
প্রতিদিনের জানা peak-এর জন্য autoscaling নিয়ম লিখুন: একটি কাল্পনিক খাবার-ডেলিভারি অ্যাপ। রাতে চাপ ২,০০০ অনুরোধ/সেকেন্ড; প্রতিদিন সন্ধ্যা ৬:৩০ থেকে ৭:০০-এর মধ্যে চাপ সমান হারে বেড়ে ৮,০০০ হয়, রাত ১০টা পর্যন্ত থাকে। Load test: একটি instance ২৫০ অনুরোধ/সেকেন্ড পর্যন্ত p99 লক্ষ্যের ভেতরে। লক্ষ্য utilisation ৬০%, warm-up ৩ মিনিট, সার্ভার দুটি zone-এ। Database সর্বোচ্চ প্রায় ১,৫০০টি connection সইতে পারে, আর প্রতিটি instance-এর pool-এ ২০টি। নিয়মগুলো সংখ্যাসহ লিখুন।
একটি instance-এর কার্যকর ক্ষমতা: ২৫০ × ০.৬ = ১৫০ অনুরোধ/সেকেন্ড।
সর্বনিম্ন (রাত): ২,০০০ ÷ ১৫০ ≈ ১৩.৩ → ১৪; N+1 → ১৫; দুই zone-এ সমান ভাগে ১৬ (৮ + ৮)।
Peak: ৮,০০০ ÷ ১৫০ ≈ ৫৩.৩ → ৫৪; N+1 → ৫৫; দুই zone-এ সমান ভাগে ৫৬ (২৮ + ২৮)। ৫৬টিতে ব্যস্ততা ৮,০০০ ÷ (৫৬ × ২৫০) ≈ ৫৭%. একটি zone গেলে ২৮ × ২৫০ = ৭,০০০ < ৮,০০০ — ঘাটতি মেনে load shedding (পর্ব ১৮), নাকি প্রায় দ্বিগুণ খরচে প্রতি zone-এ পুরো peak — এটি ব্যবসায়িক সিদ্ধান্ত।
Scheduled নিয়ম: সন্ধ্যা ৬:২০-এ সর্বনিম্ন ৫৬ (warm-up আর নিরাপত্তার সময় হাতে রেখে), রাত ১০:৩০-এ আবার ১৬. শুধু reactive হলে: চাপ বাড়ছে মিনিটে ২০০, সাড়া দিতে ১ মিনিট মাপ + ৩ মিনিট warm-up, তাই প্রায় ৮০০ অনুরোধ/সেকেন্ড পিছিয়ে — ৮,০০০-এ ৪৮টি instance, ব্যস্ততা ৮,০০০ ÷ (৪৮ × ২৫০) ≈ ৬৭%. টিকে যাবে, কিন্তু একই সময়ে কিছু বিকল হলে আর নয়।
Target tracking (সারাক্ষণ): metric instance-প্রতি অনুরোধ, লক্ষ্য ১৫০ (CPU-র চেয়ে ভালো, কারণ সীমায় আটকে যায় না)। Scale out দ্রুত; scale in-এ ১০ মিনিটের stabilization window আর ধীরে ধীরে কমানো, connection draining-সহ।
সর্বোচ্চ সীমা: ১,৫০০ ÷ ২০ = ৭৫টির বেশি instance মানেই database-এর connection শেষ; admin কাজ আর migration-এর জন্য জায়গা রেখে সর্বোচ্চ ৭০.
-
Output আগে অনুমান করুন: অংশ ৭-এর autoscaler simulation-এ
WARM = 2বদলেWARM = 1করলে (নতুন instance ১ মিনিটে traffic নেয়) reactive নিয়মে কয় মিনিট ঘাটতি থাকবে, মোট slow আর dropped মোটামুটি কত হবে, আর দুই নিয়মের instance-মিনিট কি বদলাবে? আগে লিখুন, তারপর চালিয়ে মেলান।চালালে reactive-এর গুরুত্বপূর্ণ সারি আর শেষ দুই লাইন:
10 2422 │ 16 151% 96k 49k │ 52 47% 0k 0k 11 3047 │ 16+25 190% 96k 87k │ 52 59% 0k 0k 12 2962 │ 41+10 72% 0k 0k │ 52 57% 0k 0k 13 2940 │ 51 58% 0k 0k │ 52 57% 0k 0k reactive slow 192k, dropped 136k, instance-minutes 943 scheduled slow 0k, dropped 0k, instance-minutes 1160ঘাটতি ৩ মিনিট থেকে কমে ২ মিনিট (মিনিট ১০ আর ১১)। মিনিট ১০-এর শেষে নেওয়া সিদ্ধান্তের instance এখন মিনিট ১২-তেই traffic নেয়, আর মিনিট ৮-এর সিদ্ধান্তের একটি instance মিনিট ১০-এই তৈরি (তাই
16, ব্যস্ততা151%). মোটdropped২২৪k থেকে136k,slow২৮২k থেকে192k— তবু শূন্য নয়, কারণ ১ মিনিটের মাপের দেরি থেকেই যায়। Scheduled নিয়মে আগের মতোই শূন্য, শুধু ৫২টি তৈরি হয় মিনিট ৮-এর বদলে ৭-এ।অবাক করার মতো অংশ: instance-মিনিট বদলায়নি (
943আর1160). সিদ্ধান্তগুলো নির্ভর করে শুধু চাপের ওপর, আর warm-up-এ থাকা instance-এরও ভাড়া লাগে — তাই ভাড়ার হিসাব একই, শুধু যন্ত্র কাজে লাগে আগে। দ্রুত চালু হওয়া একক এজন্যই প্রায় বিনামূল্যে ক্ষতি কমায়।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি autoscaling ঘটনার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 📈 কয়েকটি পরপর মাপে metric লক্ষ্যের ওপরে থাকল
- 🧮 নিয়ম নতুন সংখ্যা হিসাব করল আর নতুন instance চালু করতে বলল
- 🚀 নতুন instance চালু হলো, প্রোগ্রাম, cache আর connection গরম হলো
- ✅ Health check পাস করায় load balancer তাদের traffic দিতে শুরু করল
- ⏳ চাপ কমার পরও stabilization window পার হওয়া পর্যন্ত অপেক্ষা
- 📉 বাড়তি instance draining শেষে সরানো হলো
Vertical scaling (একই যন্ত্রকে বড় করা) সম্পর্কে কোনটি সত্য?
- কোড প্রায় না বদলেই বেশি ক্ষমতা মেলে, কিন্তু একটি ছাদ আছে আর যন্ত্রটি single point of failure থেকে যায়
- প্রতিটি ধাপে দাম ঠিক আকারের সমানুপাতিক হারে বাড়ে
- যন্ত্র বড় করলে availability আপনা-আপনি বাড়ে
- এর জন্য load balancer আর stateless সার্ভার অবশ্যই লাগে
একটি app server ব্যবহারকারীদের লগইন session নিজের memory-তে রাখে। দ্বিতীয় একটি সার্ভার আর round-robin load balancer যোগ করার পর ব্যবহারকারীরা বারবার লগআউট হচ্ছেন। মূল সমাধান কোনটি?
- Load balancer-কে আরও শক্তিশালী যন্ত্রে নেওয়া
- Session সব সার্ভার পড়তে পারে এমন একটি ভাগ করা store-এ, বা client-এর কাছে স্বাক্ষরিত token-এ সরানো
- দ্বিতীয় সার্ভারটি বন্ধ করে দেওয়া
- Round robin-এর বদলে random ভাগ চালু করা
একটি কাজের ৫% অংশ একটি ভাগ করা অংশ দিয়ে একে একে চলতে হয়, বাকি ৯৫% পুরোপুরি ভাগ করা যায়। Amdahl-এর সূত্রে যত যন্ত্রই যোগ করা হোক, গতি সর্বোচ্চ কত গুণ হতে পারে?
- ৯৫ গুণ
- ২০ গুণ
- ১০০ গুণ
- সীমাহীন — যন্ত্র যত, গতি তত
Universal Scalability Law-এ C(N) = N ÷ (1 + σ(N − 1) + κN(N − 1)). σ = 0.04 আর κ = 0.0004 হলে মোটামুটি কতটি সার্ভারে throughput সবচেয়ে বেশি?
- প্রায় ২৫টিতে
- প্রায় ৪৯টিতে
- প্রায় ১০০টিতে
- সীমা নেই — যত বেশি সার্ভার, তত বেশি throughput
একটি সেবার p99 latency বেড়েছে। App server-গুলোর CPU ৩০%, কিন্তু database-এর CPU ৯৫% আর তার সব connection ব্যস্ত। App server দ্বিগুণ করলে সম্ভবত কী হবে?
- Latency প্রায় অর্ধেক হবে
- Database-এর CPU কমে যাবে
- প্রায় কিছুই বদলাবে না, বরং বাড়তি connection-এ database আরও চাপে পড়তে পারে
- পুরো সেবার ক্ষমতা দ্বিগুণ হবে
ঠিক রাত ৮টায় পরীক্ষার ফল প্রকাশ হবে, আর এক মিনিটের মধ্যে চাপ ৪ গুণ হবে বলে জানা। নতুন instance traffic নিতে ৩ মিনিট লাগে। সবচেয়ে ভালো কৌশল কোনটি?
- শুধু CPU-ভিত্তিক target tracking চালু রাখা
- Scale in-এর cooldown শূন্য করে দেওয়া
- ভিড় শুরু হলে on-call প্রকৌশলী হাতে instance যোগ করবেন
- ৮টার কয়েক মিনিট আগে scheduled নিয়মে instance বাড়িয়ে রাখা, সাথে target tracking চালু রাখা
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Scaling মানে চাপ বাড়লেও p99 লক্ষ্যে আর প্রতি অনুরোধের খরচ সহনীয় রাখা; কোন load parameter কোন স্তরে চাপ দেয়, আগে তা দেখুন।
- Vertical সহজ আর consistency-বান্ধব, কিন্তু ছাদ, দামের বাঁক আর SPOF আছে; horizontal-এ লাগে load balancer, stateless নকশা আর সমন্বয়ের দাম।
- Stateless মানে যেকোনো instance যেকোনো অনুরোধ নিতে পারে — state ভাগ করা store-এ সরে, মুছে যায় না; লুকানো state (ফাইল, counter, cron, সংযোগ) খুঁজুন।
- Amdahl: N ÷ (1 + σ(N − 1)), ছাদ 1/σ; USL: N ÷ (1 + σ(N − 1) + κN(N − 1)), চূড়া √((1 − σ)/κ), তার পর retrograde. ভুল স্তর বাড়ালে কিছুই বদলায় না — data স্তর সবচেয়ে কঠিন।
- Autoscaling দেরিতে সাড়া দেয়: বাড়ান দ্রুত, কমান ধীরে, সর্বোচ্চ-সর্বনিম্ন সীমা রাখুন, আর জানা ভিড়ে scheduled নিয়ম; headroom রাখুন queue, ব্যর্থতা আর প্রতিক্রিয়ার সময়ের জন্য।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৪ · Load balancing — L4 বনাম L7, algorithm আর health checkপরবর্তী পর্বঅনুরোধ কীভাবে instance-গুলোর মধ্যে ভাগ হয়, আর অসুস্থ instance কীভাবে বাদ পড়ে।
- সিস্টেম ডিজাইন · কোর্স হোম৬৬টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- Parallel architecture — Amdahl আর Gustafsonগভীরে যানStrong আর weak scaling, hardware-এর দৃষ্টিতে সমান্তরালতার সীমা।
- Container — namespace আর cgroupপাশের ট্র্যাকযে একক দিয়ে আজকের বেশিরভাগ সেবা scale হয়, তার ভেতরের কাজ।
- Data center নেটওয়ার্ক — leaf-spine আর load balancerপাশের ট্র্যাকহাজারো যন্ত্রকে একসাথে জুড়ে রাখা নেটওয়ার্কের নকশা।