Course-এর সূচি

9.2

একটা Stateful দোকান: Session, ডেটা আর Read Scaling

stickiness, cookie, server-side session, ডেটাবেস, replica আর caching, আর যে Security Group-গুলো সবকিছুকে জুড়ে রাখে।

28 মিনিট পড়া + Animation

টিপস: ডটেড-আন্ডারলাইন করা যেকোনো শব্দে ট্যাপ বা Hover করলে তার অর্থ দেখবে।

এই লেসন শেষে তুমি পারবে

  • একটা web application কখন stateful হয়, আর কয়েকটা server-এ request ভাগ হলে shopping cart কেন ভেঙে পড়ে, সেটা বোঝাতে।
  • একটা design-কে ধাপে ধাপে এগিয়ে নিতে: “cart server-এর memory-তে” থেকে sticky session, cookie-তে cart, তারপর ElastiCache বা DynamoDB-তে server-side session, আর প্রতিটা ধাপের দাম কী, সেটা বলতে।
  • কোন ডেটা কোথায় থাকবে ঠিক করতে: স্বল্পমেয়াদি session-এর state, RDS-এ টেকসই user-এর ডেটা, আর cache-এ বারবার পড়া ডেটা।
  • Read scale করতে read replica নাকি ElastiCache, বেছে নিতে, আর replica lag বা cache-এর পুরনো মান user-এর ওপর কী প্রভাব ফেলে, আগে থেকেই বলতে।
  • Multi-AZ দিয়ে প্রতিটা data tier-কে AZ নষ্ট হওয়া থেকে বাঁচাতে, আর একে অন্যকে reference করা security group দিয়ে tier-গুলোকে তালাবদ্ধ করতে।
  • প্রতিটা ধাপের পরীক্ষার ভাষা চিনতে: “instance terminate হলে session হারায়”, “legacy app, code বদলানো যাবে না”, “code না বদলে read scale করো”, “ডেটাবেসে ঢোকা সীমিত করো”।

#এমন দোকান, যাকে মনে রাখতে হয়

#My Cloth-এর গল্প আর তার চাহিদা

লেসন ৯.১-এর stateless app-কে শুধু সময় বলে দিতে হতো। যেকোনো server যেকোনো request-এর উত্তর দিতে পারত, কারণ কোনো উত্তর আগের কোনো উত্তরের ওপর নির্ভর করত না। এবার পরিচয় করো My Cloth-এর সাথে, একটা অনলাইন কাপড়ের দোকান। এর একটা shopping cart আছে, একই মুহূর্তে শত শত ক্রেতা, আর চাহিদাগুলো এমন:

  • horizontally scale করতে হবে (বড় একটা server না, বেশি server), আর web tier-কে যতটা পারা যায় stateless রাখতে হবে;
  • ক্রেতা কখনোই cart হারাবে না, কোনো server নষ্ট হলেও না, Auto Scaling কোনো server সরিয়ে দিলেও না;
  • ক্রেতার তথ্য (নাম, ঠিকানা, আগের order) থাকবে একটা ডেটাবেসে;
  • পুরো ব্যবস্থাটা একটা AZ হারালেও টিকে থাকবে, আর এমনভাবে তালাবদ্ধ থাকবে যে ডেটা পর্যন্ত শুধু ঠিক tier-টাই পৌঁছাতে পারে।

শুরু করব লেসন ৯.১ যেখানে শেষ হয়েছিল সেখান থেকে: কয়েকটা AZ জুড়ে একটা Auto Scaling group, সামনে একটা ALB। তারপর একটা একটা করে সমস্যায় পড়ব আর সমাধান করব, একজন solutions architect যেভাবে করেন।

#Stateless আর stateful-এর তফাত

একটা server stateless, যখন এখনকার request-এর উত্তর দিতে আগের কোনো request থেকে তার কিছুই লাগে না। আর stateful, যখন দুই request-এর মাঝে তাকে কিছু একটা মনে রাখতে হয়, এটাকেই বলে state: কে login করে আছে, cart-এ কী আছে, checkout-এর কোন ধাপে ক্রেতা আছে।

HTTP নিজে stateless; তাই state-কে কোথাও না কোথাও থাকতেই হয়: server-এর memory-তে, client-এর কাছে, অথবা একটা shared store-এ। এই বাছাইটাই ঠিক করে application কতটা ভালো scale করবে, আর একটা server মরে গেলে কী হবে। Cookie, session আর token নিয়ে সাধারণ আলোচনা আছে System Design-এর State, cookie আর session লেসনে; এই লেসনে আমরা সেগুলো AWS-এ খাটাব।

#ধাপ ১: cart থাকে server-এর memory-তে

দোকানের প্রথম version প্রতিটা cart রাখে যে server request সামলাল, তার RAM-এ (বা local disk-এ)। একটা server হলে চলে যায়। Load balancer-এর পেছনে কয়েকটা server হলেই সব ভেঙে পড়ে।

Load balancer কিন্তু ঠিক নিজের কাজটাই করছে: সে প্রতিটা request আলাদা করে ভাগ করে (লেসন ৬.৪)। ভুলটা design-এর, ALB-র না। সমস্যাটা আর পরের চারটা ধাপ, একটাই diagram-এ দেখে নাও:

My Cloth: cart কোথায় থাকে, ধাপে ধাপে

দুটো AZ-এ দুটো web server, সামনে একটা ALB। প্রতিটা tab design-এর একটা ধাপ; প্রতিটা ধাপ আগের ধাপের সমস্যা মেটায়, আবার নিজে একটা নতুন সমস্যা আনে।

পরিস্থিতি

সবচেয়ে সহজ design: প্রতিটা server নিজের memory-তে cart মনে রাখে।

eu-west-1
AZ a
AZ b
ক্রেতা
ALB
web-1
web-2
শার্ট যোগ
ধাপ 1 / 4
1

My Cloth অনলাইনে কাপড় বিক্রি করে, একসাথে শত শত ক্রেতা। তাই আগে থেকেই দুটো AZ-এ দুটো web server চলছে, সামনে একটা ALB (লেসন ৯.১)। এক ক্রেতা একটা শার্টে “Add to cart” চাপল।

#ধাপ ২: sticky session

#তাৎক্ষণিক সমাধান

সবচেয়ে দ্রুত সমাধানে code-ই লাগে না: ALB-র target group-এ stickiness (session affinity) চালু করো। ALB browser-কে একটা cookie দেয় (duration-based stickiness-এ AWSALB, আর তোমার নিজের application cookie মেনে চললে AWSALBAPP), আর ওই cookie নিয়ে আসা পরের সব request যায় একই target-এ। ক্রেতা বারবার server ১-এই পৌঁছায়, যেখানে তার cart।

#লেসন ৬.৪-এ যা দেখেছি

লেসন ৬.৪-এ এটা খুঁটিয়ে দেখেছি; এখানে ছোট করে মনে করিয়ে দিই। Session ID আর session data আলাদা জিনিস। Cookie শুধু একটা টোকেন, রেস্টুরেন্টের টোকেনের মতো, যাতে লেখা “কাউন্টার ১”; আসল cart লেখা থাকে server ১-এর RAM-এ, যেমন রাঁধুনির খাতায় লেখা থাকে order। Server ১ crash করলে বা restart নিলে RAM মুছে যায়। পরের request তখনও cookie নিয়ে আসে, ALB দেখে server ১ নেই, বাধ্য হয়ে বেছে নেয় server ২, আর server ২ বলে “Your cart is empty”। Stickiness-এ ডেটা একটা server-এর পেটের ভেতর আটকা থাকে: server মরলে ডেটাও মরে।

#Stickiness-এর দাম

  • Instance মরলে cart হারায়। একটা crash, health check-এ fail বা hardware বদল, ওই instance-এর সব session সাথে নিয়ে যায়।
  • Scale in আর deployment-এও cart হারায়। কোন instance terminate হবে, Auto Scaling সেটা বাছে তার termination policy দেখে, কে কেনাকাটা করছে দেখে না (লেসন ৬.৫)। Deregistration delay চলতি request শেষ করতে দেয়, কিন্তু RAM তবুও যায়। Rolling deployment সব server-ই বদলায়, তাই সব cart খালি করে দিতে পারে।
  • Load অসমান হয়। ক্রেতারা যে server-এ আটকেছে সেখানেই থাকে। ব্যস্ত server ব্যস্তই থাকে, আর scale out-এ নতুন চালু হওয়া instance পায় শুধু নতুন ক্রেতা, তাই scale out যতটা কাজে আসার কথা ততটা আসে না।
  • Cookie-র ওপর নির্ভর। যে client cookie নেয় না, সে প্রতিটা request-এ নতুন করে route হয়।

চারটা server-এ সমান ভাগ থাকলে একটা instance terminate হওয়া মানে একসাথে প্রায় এক-চতুর্থাংশ cart খালি।

#State বয়ে নিক browser

Server যদি cart মনে না রাখে, তাহলে অন্য কাউকে রাখতে হবে। সবচেয়ে সহজ “অন্য কেউ” হলো browser। Server পুরো cart একটা cookie-তে লিখে দেয়, যেমন cart=shirt-M-blue,trousers-32, আর browser প্রতিটা request-এর সাথে সেটা ফেরত পাঠায়। যেকোনো server সেটা পড়তে পারে, তাই:

  • web tier সত্যিকারের stateless: ALB ইচ্ছেমতো ভাগ করে, Auto Scaling যেকোনো instance সরাতে পারে, stickiness লাগে না;
  • server-এর দিকে কিছুই রাখতে হয় না, বাড়তি কোনো খরচও নেই।

#এটা কেন বেশিদিন চলে না

  • আকার। নিয়ম অনুযায়ী browser-কে একটা cookie-তে অন্তত ৪,০৯৬ byte রাখতে পারতে হয় (নাম, value আর attribute মিলিয়ে, RFC 6265), আর বাস্তবে সর্বোচ্চও মোটামুটি ততটুকুই; বড় cookie চুপচাপ ফেলে দেয়। Size, রং আর দাম সহ বিশটা জিনিসের cart-ই সেখানে পৌঁছে যায়। (ALB এখানে বাধা না: সে ১৬ KB-র একটা header আর মোট ৬৪ KB-র request header নেয়।)
  • ভারী request। Browser domain-এর সব cookie পাঠায় প্রতিটা request-এ, ছবি, CSS আর API call-সহ। যে page ৪০টা request করে, তাতে ৩.৫ KB-র একটা cookie মানে প্রতি page view-এ প্রায় ১৪০ KB বাড়তি upload, তার অনেকটাই ধীর mobile network-এ।
  • Cookie থাকে client-এর হাতে। ক্রেতা developer tools খুলে শার্টের দাম ৳১ করে দিতে পারে। তাই server-কে প্রতিটা cookie যাচাই করতে হয়: এমন secret দিয়ে HMAC sign করো যা শুধু server-রা জানে, আর signature না মিললে cookie বাতিল করো। Sign করা cookie-ও কিন্তু পড়া যায়, তাই এতে গোপন কিছু রাখা চলবে না; তথ্য সংবেদনশীল হলে encrypt করো।
  • আবার হিসাব আর অন্য device। Checkout-এর সময় দাম আর stock server-কে ডেটাবেস দেখে আবার যাচাই করতেই হয়, আর cart ক্রেতার সাথে অন্য device বা browser-এ যায় না।

JWT-এর মতো sign করা token একই ধারণা, শুধু পরিচয়ের জন্য: ছোট, sign করা, পড়া যায়, প্রতিটা request-এর সাথে যায়। “এই user কে” বলার জন্য দারুণ, ক্রমশ বড় হতে থাকা shopping cart-এর জন্য খারাপ।

#ধাপ ৪: shared store-এ server-side session

#যায় শুধু একটা ID

এবার আগের দুই ধাপের ভালো অর্ধেকটা জুড়ে দাও। Cookie-তে থাকে শুধু একটা session ID: লম্বা একটা random মান (ধরো ১২৮টা random bit), যার নিজের কোনো অর্থ নেই। Session data, মানে cart, থাকে web server-এর বাইরে একটা shared store-এ, ওই ID ধরে।

  1. প্রথম request: session তৈরি

    web-1 একটা random ID বানায়, cart লেখে store-এ sess:<id> নামে, ধরো ৩০ মিনিটের TTL দিয়ে, আর ফেরত পাঠায় Set-Cookie: session_id=<id>; HttpOnly; Secure; SameSite=Lax।

  2. পরের যেকোনো request: store থেকে পড়া

    পরের click হয়তো পড়ল web-2-তে। সে store থেকে sess:<id> পড়ে, শার্ট আর প্যান্ট দুটোই দেখে, নতুন জিনিস যোগ করে, session আবার লিখে দেয় আর TTL নতুন করে শুরু করে, তাই সক্রিয় ক্রেতার session কখনো মেয়াদোত্তীর্ণ হয় না।

  3. একটা server মরল: cart-এর কিছুই হলো না

    Crash, scale in বা deployment server সরিয়ে দেয়, কিন্তু তাদের ভেতরে কোনো cart ছিল না। Web tier আবার stateless, stickiness-এর আর দরকার নেই।

Cookie cart-এর সব সমস্যাও এতে মিটে যায়: cookie ছোট্ট, request হালকা, store যতটা দেয় cart ততটা বড় হতে পারে, আর ID বদলালে ক্রেতা পায় শুধু “এমন session নেই”: ১২৮-bit-এর random ID আন্দাজ করা যায় না, তাই বদলে অন্য কারও cart খোলা যায় না।

#ElastiCache নাকি DynamoDB?

Note আর slide দুটোতেই উত্তর হিসেবে দুটোর নামই আছে, আর দুটোই ঠিক। তফাত হলো ডেটা কীভাবে রাখে।

Amazon ElastiCache (Valkey বা Redis OSS)Amazon DynamoDB
ডেটা কোথায় থাকেতোমার মাপমতো node-এর memory-তে, অথবা ElastiCache ServerlessManaged একটা table, Region-এর তিনটা AZ-এ রাখা
Read latencyMicrosecondএক অঙ্কের millisecond
মেয়াদSET … EX 1800: মেয়াদ ফুরানোর মুহূর্তেই key উধাওএকটা TTL attribute (epoch সেকেন্ডে); মেয়াদোত্তীর্ণ item পেছনে বসে মোছে কয়েক দিনের মধ্যে, তাই read-এর সময় সেগুলো বাদ দিতে হয়
Failure সামলানোঅন্য AZ-এ replica আর Multi-AZ automatic failover লাগে; replication asynchronousভেতরেই আছে: node নেই, failover সামলানোর ঝামেলাও নেই
CapacityNode-এর আকার বা Serverless-এর সীমাOn-demand বা provisioned; item সর্বোচ্চ ৪০০ KB
কখন নেবেপ্রতি page-এ অনেক read, সবচেয়ে কম latency, ডেটাবেসের জন্য আগে থেকেই একটা cache চালাচ্ছSession টেকসই হতে হবে, কোনো server-ই চাও না, বা traffic হঠাৎ হঠাৎ বাড়ে-কমে

পরীক্ষা আর বাস্তব দুই জায়গাতেই দুটো খুঁটিনাটি জরুরি:

  • Session store হিসেবে Memcached দুর্বল। এর replica নেই, failover নেই, তাই একটা node গেলে তার session-ও যায়। Note-এ যখন বলছে “ElastiCache (Redis/Memcached)”, session-এর জন্য সেটা পড়ো replica সহ Valkey বা Redis OSS (লেসন ৭.৫); Memcached হলো এমন সাধারণ cache-এর জন্য যা হারালেও চলে।
  • DynamoDB-র TTL কোনো ঘড়ি না। মেয়াদোত্তীর্ণ item এটা বিনা পয়সায় মোছে, write capacity খরচ না করে, কিন্তু মেয়াদ ফুরানোর কিছু পরে। যে item-এর expires_at পেরিয়ে গেছে, application-কে নিজেই সেটাকে বাতিল ধরতে হবে।

দুটোতেই code বদলাতে হয়: application-কে session পড়তে আর লিখতে হবে store-এ। এটাই architecture দিয়ে সমাধানের দাম, আর এই কারণেই code বদলানো একেবারে অসম্ভব হলেই শুধু stickiness উত্তর থাকে।

#চারটা design চালিয়ে দেখো, তারপর ভাঙো

একটা design বেছে নাও, জিনিস যোগ করো, তারপর server crash করাও, scale in করো, cookie বদলাও, ৪ KB পার করো বা cache node নষ্ট করো। প্রতিবার চাপা মানে একটা request:

Cart থাকে কোথায়? একটা failure simulator

একটা design বেছে নাও, কয়েকটা জিনিস cart-এ দাও, তারপর কিছু একটা ভাঙো। প্রতিবার বোতাম চাপা মানে একই ক্রেতার একটা request।

Design

প্রতিটা server cart রাখে নিজের memory-তে। ALB প্রতিটা request পাঠায় পরের সুস্থ server-এ (round robin)।

Browser

কোনো cookie নেই

ALB

round robin

web-1

RAM: খালি

web-2

RAM: খালি

web-3

RAM: খালি

Request আর failure

কী ঘটল

এখনো কিছু ঘটেনি। “পরের জিনিস যোগ করো” চাপো।

নিজেকে যাচাই করোপ্রশ্ন

My Cloth পুরো cart রাখে একটা sign করা cookie-তে। খালি cart-এ ক্রেতা একেকটা প্রায় ১৮০ byte-এর জিনিস যোগ করছে; cookie-র নাম আর attribute-এ লাগে আরও প্রায় ৪০ byte, signature-এ ৪৪ byte। ৪,০৯৬ byte-এ browser cookie ফেলে দেওয়ার আগে মোটামুটি কয়টা জিনিস ধরবে?

#ধাপ ৫: user-এর ডেটার জায়গা ডেটাবেস

Cart আধা ঘণ্টা পরে মুছে গেলেও চলে; ক্রেতার নাম, ঠিকানা, password hash আর order-এর ইতিহাস কিন্তু না। এগুলো টেকসই, relational ডেটা, তাই যায় Amazon RDS-এ (লেসন ৭.১), অথবা Aurora-তে (লেসন ৭.৩)।

  • Login-এর সময় web tier RDS থেকে account পড়ে; checkout-এ একটা transaction-এ order লেখে (stock যাচাই, order-এর row, payment-এর অবস্থা, সব একসাথে), তারপর cart-এর session মুছে দেয়।
  • ভাগটা এখন পরিষ্কার: স্বল্পমেয়াদি state (session, cart) ElastiCache বা DynamoDB-তে, আসল হিসাবের খাতা RDS-এ, আর web server-এ কিছুই না।
  • Session চাইলে account-এর কয়েকটা field-ও (page-এর মাথায় ক্রেতার নাম) রেখে দিতে পারে, যাতে প্রতিটা page view-এ RDS-এ যেতে না হয়।

এখন ডেটাবেসই design-এর সবচেয়ে ব্যস্ত অংশ, আর পরের দুটো ধাপ তাকে নিয়েই: অতিরিক্ত read, আর তার AZ নষ্ট হলে কী হয়।

#ধাপ ৬: read scale করা

#একটা primary-র ওপর অনেক বেশি read

কাপড়ের দোকান লেখে যত, পড়ে তার অনেক গুণ বেশি: প্রতিটা product page, category-র list আর search ডেটাবেসে যায়, অথচ order দেয় অল্প কয়েকটা request। Sale-এর সময় একমাত্র RDS instance-এর CPU ফুরিয়ে যায়। বড় instance class (vertical scaling) কিছু সময় কিনে দেয়, কিন্তু তার একটা ছাদ আছে। তার বদলে বারবার হওয়া read-গুলো primary-র ঘাড় থেকে নামাও, দুটোর একটা উপায়ে।

#উপায় ১: read replica

RDS read replica বানাও (MySQL, MariaDB, PostgreSQL, Oracle আর SQL Server-এ একটা source-এর সর্বোচ্চ ১৫টা; লেসন ৭.১)। Primary প্রতিটা পরিবর্তন তাদের কাছে asynchronously copy করে, আর প্রতিটা replica-র নিজের endpoint থাকে। Application write পাঠায় primary endpoint-এ, read পাঠায় replica-তে।

  • Application-এ বদল সামান্য: read-এর জন্য দ্বিতীয় একটা connection string। Query একই SQL থাকে।
  • Replica যেকোনো query-র উত্তর দেয়, তাই সাধারণ browsing, search আর report-এর জন্য মানানসই।
  • প্যাঁচটা হলো replica lag। Replica এক মুহূর্ত পিছিয়ে থাকতে পারে: শেষ জ্যাকেটটা primary-তে বিক্রি হয়ে গেছে, অথচ এক-দুই সেকেন্ড replica তখনও দেখাচ্ছে “১টা বাকি”। যে read একটু পুরনো হলেও চলে, সেটা replica থেকে নাও; আর সিদ্ধান্ত (checkout-এর সময়ের stock, order-এর মোট দাম) আর user-এর নিজের সদ্য লেখা ডেটা (read-your-own-writes) পাঠাও primary-তে।

#উপায় ২: RDS-এর সামনে ElastiCache (lazy loading)

ডেটাবেসের সামনে ElastiCache বসাও আর lazy loading চালাও (cache-aside, লেসন ৭.৫):

  1. আগে cache পড়ো, যেমন product:42।
  2. Hit: memory থেকে এক millisecond-এর অনেক কম সময়ে উত্তর; RDS কিছুই করে না।
  3. Miss: RDS পড়ো, উত্তর দাও, আর ফলটা TTL দিয়ে cache-এ লিখে রাখো, যাতে পরের read hit হয়।

Hit ratio ৯৫% হলে সেকেন্ডে ১০,০০০ product read থেকে RDS-এ যায় মাত্র ৫০০। প্যাঁচের দুটো দিক আছে: application-কে cache ব্যবহার করার মতো করে লিখতে হয় (সত্যিকারের code বদল), আর cache-এর মান পুরনো হয়ে যেতে পারে: RDS-এ দাম বদলালেও cache-এ পুরনো দামই থাকে, যতক্ষণ না TTL ফুরায় বা code key-টা মুছে দেয়। Row বদলালেই key মুছে দাও বা নতুন মান লিখে দাও (write-through), নিরাপত্তার জন্য TTL রাখো, আর যে মান একদম ঠিক হতে হবে তার জন্য cache কখনো ব্যবহার কোরো না।

Read scale করা: replica নাকি cache

প্রতিটা order লেখার বিপরীতে product page পড়া হয় হাজার বার। Primary-র ঘাড় থেকে read নামানোর দুটো উপায়, প্রত্যেকটার নিজস্ব প্যাঁচ আছে।

পরিস্থিতি

Web tierASG
RDS primaryCPU 95%
read + write
ধাপ 1 / 2
1

প্রতিটা page view একটাই RDS instance-এ read query চালায়, প্রতিটা order-ও। Sale-এর সময় primary-র CPU ৯০% ছাড়িয়ে যায়, আর বাকি সবকিছুর সাথে checkout-ও ধীর হয়ে পড়ে।

Read replicaElastiCache
Application-এ বদলসামান্য: একটা read endpointহ্যাঁ: code-এ caching logic
Read latencyMillisecondMicrosecond
কীসের উত্তর দেয়যেকোনো SQL queryশুধু যে key রেখেছ
পুরনো হওয়াReplica lag (সাধারণত এক সেকেন্ডের কম)TTL বা invalidation পর্যন্ত
আর কোথায় কাজে লাগেReport, promote করে disaster recoverySession (ধাপ ৪), counter, leaderboard

অনেক দোকান দুটোই চালায়: সাধারণ read-এর জন্য replica, সবচেয়ে ব্যস্ত page-এর জন্য cache, আর session-এর জন্য সেই ElastiCache cluster (বা আলাদা একটা)।

#ধাপ ৭: প্রতিটা data tier-এ Multi-AZ

Web tier আগে থেকেই কয়েকটা AZ জুড়ে আছে। এক AZ-এ একটাই কপি রাখা data tier সেই পরিশ্রম জলে ফেলে দেয়: তার AZ নষ্ট হলে সব server সুস্থ, অথচ দোকান বন্ধ।

  • RDS Multi-AZ: অন্য AZ-এর একটা standby প্রতিটা write পায় synchronously। Primary নষ্ট হলে RDS একই DNS endpoint standby-র দিকে ঘুরিয়ে দেয়, সাধারণত ৬০ থেকে ১২০ সেকেন্ডে, commit হওয়া কোনো ডেটা না হারিয়ে। Standby কোনো read সামলায় না; সেটা replica-র কাজ (লেসন ৭.১)।
  • ElastiCache Multi-AZ (Valkey বা Redis OSS): অন্য AZ-এ একটা replica, automatic failover সহ। Primary node নষ্ট হলে ElastiCache replica-কে primary বানায়, সাধারণত কয়েক সেকেন্ডে, আর primary endpoint তার পিছু নেয়। Replication asynchronous, তাই শেষ মুহূর্তের কিছু write হারাতে পারে; session-এর জন্য সেটা মেনে নেওয়া যায়। ElastiCache Serverless নিজেই কয়েকটা AZ-এ replicate করে।
  • Session যদি DynamoDB-তে থাকে, সেটা এমনিতেই তিনটা AZ-এ রাখা: কিছুই সাজাতে হয় না।

খেয়াল করো, RDS failover-এর সময় প্রতিটা tier-এ Multi-AZ থাকার লাভটা কী: এক-দুই মিনিট checkout অপেক্ষা করে আর আবার চেষ্টা করে, কিন্তু ঘোরাঘুরি আর cart ঠিকঠাক চলে, কারণ session আছে আরেকটা store-এ, যেটা নষ্ট হয়নি।

ElastiCache আর RDS-এর Multi-AZ

দুটো data store-ই অন্য AZ-এ একটা দ্বিতীয় কপি রাখে। একটা node, এমনকি পুরো একটা AZ হারালেও session আর order টিকে থাকে।

পরিস্থিতি

eu-west-1
AZ a
AZ b
Web tierASG
ElastiCacheprimary
RDSprimary
ElastiCachereplica
RDSstandby
set sessasync
ধাপ 1 / 2
1

Web tier একটা session লেখে AZ a-র ElastiCache primary-তে। AZ b-র replica তার কপি পায় asynchronously, এক মুহূর্ত পরে।

#ধাপ ৮: tier থেকে tier, শিকলের মতো security group

শেষ চাহিদা হলো, ডেটা পর্যন্ত শুধু ঠিক tier-টাই পৌঁছাবে। Security group-এর rule-এ source হিসেবে আরেকটা security group দেওয়া যায় (লেসন ৩.৫), আর এটাই tier-গুলোকে শিকলের মতো জুড়ে দেয়:

Security groupকার গায়েInbound ruleSource
alb-sgALBTCP ৮০ আর ৪৪৩0.0.0.0/0 (internet)
app-sgপ্রতিটা web server (launch template দিয়ে)TCP ৮০ (বা app-এর port)alb-sg
cache-sgElastiCacheTCP ৬৩৭৯ (Valkey/Redis OSS)app-sg
db-sgRDSTCP ৩৩০৬ (MySQL; PostgreSQL হলে ৫৪৩২)app-sg

এটা কেন সবচেয়ে মজবুত:

  • Internet-এর মুখোমুখি শুধু ALB। কেউ তাকে এড়িয়ে web server-এ পৌঁছাতে পারে না, আর internet থেকে কেউ ডেটাবেসের port ছুঁতে পারে না।
  • Address না, সদস্যপদ। যে instance-এর গায়ে app-sg, সে-ই data tier-এ পৌঁছায়, তাই Auto Scaling-এর চালু করা instance সাথে সাথে কাজ করে, কোনো IP list আপডেট করতে হয় না। যার গায়ে নেই, সে বাতিল, একই VPC আর একই subnet-এ থাকলেও।
  • Stateful। উত্তর নিজে থেকেই ফিরে আসে; এই connection-গুলোর জন্য outbound বা ফেরার rule লাগে না।
  • স্তরে স্তরে। Web আর data tier রাখো private subnet-এ, public IP ছাড়া, আর RDS রাখো “not publicly accessible”। Security group আর subnet-এর design, দুটোর প্রত্যেকটাই একা একজন আক্রমণকারীকে থামায়।

যে rule ডেটাবেসের port খোলে 0.0.0.0/0 থেকে, বা পুরো VPC CIDR থেকে, সেটা নিঃশব্দে শিকল ভেঙে দেয়: তখন VPC-র যেকোনো instance RDS-এ পৌঁছায়।

Tier থেকে tier, শিকলের মতো security group

প্রতিটা tier শুধু তার সামনের tier-এর security group থেকে আসা traffic নেয়। Internet-এর মুখোমুখি শুধু load balancer।

পরিস্থিতি

VPC 10.0.0.0/16
alb-sg
app-sg
cache-sg, db-sg
ক্রেতা
আক্রমণকারী
ALB
Web tier
অন্য EC2misc-sg
ElastiCache
RDS
HTTPS 443

Inbound rule (বাকি সব security group আটকে দেয়)

Security groupPortSource
alb-sg80, 4430.0.0.0/0
app-sg80alb-sg
cache-sg6379app-sg
db-sg3306app-sg
ধাপ 1 / 3
1

ক্রেতারা HTTPS-এ ALB-তে পৌঁছায়। Internet-এর জন্য খোলা একমাত্র group হলো alb-sg: 0.0.0.0/0 থেকে port ৮০ আর ৪৪৩।

#Three-tier-এর পুরো ছবি

ধাপগুলো জোড়া দিলে পাও classic three-tier web application, এই পুরো লেসনের লক্ষ্য:

Tierসেখানে কী চলেকীভাবে scale করেAZ নষ্ট হলে কীভাবে টেকেকে পৌঁছাতে পারে
Presentation / প্রবেশপথRoute 53 alias → public subnet-এ ALBAWS নিজে সামলায়চালু প্রতিটা AZ-এ ALB-র nodeInternet, ৮০/৪৪৩-এ
Application (web)Auto Scaling group-এ stateless EC2, private subnet-এHorizontally, Auto Scaling দিয়েদুই বা তিন AZ-এ instance; health check নষ্টগুলো বদলে দেয়শুধু alb-sg
DataElastiCache (session, ব্যস্ত read) আর RDS (user, order), private subnet-এRead-এর জন্য replica আর cache; write-এর জন্য বড় classElastiCache Multi-AZ replica; RDS Multi-AZ standbyশুধু app-sg

লেসন ৯.৩ পরের টুকরোটা যোগ করে: EFS দিয়ে শেয়ার করা file (upload করা ছবি), আর দ্রুত নতুন instance চালু করা।

#কোথায় গোলমাল হয়

লক্ষণসম্ভাব্য কারণসমাধান
Cart-এ জিনিস আসে আর যায়; click-এর ফাঁকে ক্রেতা logout হয়ে যায়Session server-এর RAM-এ, stickiness নেইSession বাইরে সরাও (ElastiCache / DynamoDB); stickiness শুধু জোড়াতালি হিসেবে
প্রতিবার scale in বা deployment-এর পর cart খালিRAM-এ state, সাথে sticky sessionSession বাইরে সরাও
Scale out-এর পর নতুন instance বসে থাকেStickiness পুরনো ক্রেতাদের পুরনো server-এ আটকে রাখেSession বাইরে সরাও, বা stickiness-এর duration কমাও
বড় cart উধাও; mobile-এ request ধীরCookie-তে cart, প্রায় ৪ KB-র বেশি; প্রতিটা request-এ ভারী cookieCookie-তে session ID, cart store-এ
ক্রেতা বদলানো দামে কিনে ফেলেSign না করা cart cookie server বিশ্বাস করেছেSign আর যাচাই করো, দাম server-এ আবার হিসাব করো, আরও ভালো server-side session
সবাই একসাথে logoutএকমাত্র cache node নষ্ট (বা Memcached)Multi-AZ সহ Valkey/Redis OSS replica, Serverless, বা DynamoDB
বিক্রি হয়ে যাওয়া জিনিসে “১টা বাকি”পিছিয়ে থাকা replica বা পুরনো cache থেকে পড়াStock-এর সিদ্ধান্ত primary-তে; লেখার সময় invalidate
এক-দুই মিনিট checkout ব্যর্থ, browsing চলছেRDS Multi-AZ failover চলছেপ্রত্যাশিত: retry আর reconnect; DNS cache ছোট রাখো
অচেনা একটা instance ডেটাবেসে পৌঁছে গেছেডেটাবেসের port 0.0.0.0/0 বা VPC CIDR-এর জন্য খোলাSource = শুধু app-sg

#System design-এ এর জায়গা

  • State ঠেলে দাও stateless tier-এর কিনারায়: client-এর কাছে (ছোট, sign করা, শুধু পরিচয়), নয়তো একটা shared store-এ (বাকি সব)। Web tier নিজে এমন কিছু রাখে না যা হারালে ক্ষতি।
  • ডেটার আয়ু দেখে store বাছো: কয়েক সেকেন্ড থেকে কয়েক ঘণ্টার session state cache-এ বা TTL সহ DynamoDB-তে; বছরের পর বছরের order backup সহ relational ডেটাবেসে।
  • Write-এর আগে read scale করো: primary-কে shard করার তুলনায় replica আর cache সস্তা। Write একটা instance ছাপিয়ে গেলে পরের ধাপ Aurora (লেসন ৭.৩) বা অন্য data model, যা ডেটাবেস বাছাইয়ের পরের একটা chapter-এ আসবে।
  • প্রতিটা tier-এর নিজের failure-এর পরিকল্পনা লাগে: ডেটার জন্য Multi-AZ, web tier-এর জন্য কয়েকটা AZ, ALB-তে health check।
  • Security group-ই architecture-এর বর্ণনা: ওপরের চারটা rule পড়লেই একজন reviewer বুঝে যায় কে কার সাথে কথা বলে।

#পরীক্ষার keyword → পরের ধাপ

#নিজে করে দেখো

Command-গুলোতে বানানো নাম: target group tg-shop, security group sg-0alb…, sg-0app…, sg-0db…, sg-0cache…।

# Stage 2: duration-based stickiness on the target group (1 hour)
aws elbv2 modify-target-group-attributes --target-group-arn <tg-shop-arn> \
  --attributes Key=stickiness.enabled,Value=true Key=stickiness.type,Value=lb_cookie \
               Key=stickiness.lb_cookie.duration_seconds,Value=3600

# Stage 4a: a session store on ElastiCache (Valkey) with a replica in another AZ
aws elasticache create-replication-group --replication-group-id shop-sessions \
  --replication-group-description "My Cloth sessions" --engine valkey \
  --cache-node-type cache.t4g.small --num-cache-clusters 2 \
  --automatic-failover-enabled --multi-az-enabled \
  --cache-subnet-group-name private-data --security-group-ids sg-0cache1111

# Write and read a session with a 30-minute TTL (from a web server)
redis-cli -h <primary-endpoint> --tls SET sess:7f3a '{"cart":["shirt","trousers"]}' EX 1800
redis-cli -h <primary-endpoint> --tls TTL sess:7f3a

# Stage 4b: or a DynamoDB session table with TTL on the expires_at attribute
aws dynamodb create-table --table-name shop-sessions \
  --attribute-definitions AttributeName=session_id,AttributeType=S \
  --key-schema AttributeName=session_id,KeyType=HASH --billing-mode PAY_PER_REQUEST
aws dynamodb update-time-to-live --table-name shop-sessions \
  --time-to-live-specification Enabled=true,AttributeName=expires_at

# Stage 6: a read replica for product pages
aws rds create-db-instance-read-replica --db-instance-identifier shop-db-replica-1 \
  --source-db-instance-identifier shop-db

# Stage 7: make the primary Multi-AZ
aws rds modify-db-instance --db-instance-identifier shop-db --multi-az --apply-immediately

# Stage 8: chain the security groups
aws ec2 authorize-security-group-ingress --group-id sg-0app2222 \
  --protocol tcp --port 80 --source-group sg-0alb1111
aws ec2 authorize-security-group-ingress --group-id sg-0db3333 \
  --protocol tcp --port 3306 --source-group sg-0app2222
aws ec2 authorize-security-group-ingress --group-id sg-0cache1111 \
  --protocol tcp --port 6379 --source-group sg-0app2222

Client-এর দিক থেকে stickiness দেখতে চাইলে curl -i https://shop.example.com/ -c jar.txt -b jar.txt দিয়ে দুবার request পাঠাও, আর response header-এ AWSALB cookie খুঁজে দেখো।

#সারসংক্ষেপ

মূল কথা

  • Stateful app-এর আগের request-এর ডেটা লাগে। State server-এর RAM-এ থাকলে, request ভাগ করা load balancer প্রতিটা hop-এ cart হারায়।
  • Sticky session cookie দিয়ে ক্রেতাকে একটা server-এ আটকে রাখে। Code বদলাতে হয় না, কিন্তু instance-এর সাথে cart মরে (crash, scale in, deployment), আর load অসমান হয়। ঠিক উত্তর শুধু যে legacy app-এর code বদলানো যায় না তার জন্য। Session ID বনাম session data: লেসন ৬.৪।
  • Cookie-তে cart: server stateless, কিন্তু একটা cookie-তে প্রায় ৪ KB, যায় প্রতিটা request-এ, আর client বদলে দিতে পারে, তাই sign আর যাচাই করতে হয়, গোপন কিছু রাখা যায় না।
  • Server-side session: cookie-তে শুধু একটা random session ID; ডেটা থাকে ElastiCache-এ (microsecond, replica সহ Valkey/Redis OSS) বা DynamoDB-তে (টেকসই, serverless, TTL কয়েক দিনের মধ্যে মোছে)। TTL অলস session-এর মেয়াদ শেষ করে। Code বদলাতে হয়।
  • User-এর ডেটা (account, ঠিকানা, order) যায় RDS-এ; web tier কিছুই রাখে না।
  • Read scaling: read replica (সামান্য code বদল, যেকোনো query, replica lag) বা ElastiCache lazy loading (code বদল, microsecond, TTL বা invalidation পর্যন্ত পুরনো)। “Code বদল ছাড়া” → replica।
  • RDS-এর Multi-AZ (synchronous standby, ৬০–১২০ সেকেন্ডে failover) আর ElastiCache-এর (replica, কয়েক সেকেন্ডে failover) data tier-কে AZ নষ্ট হলেও টিকিয়ে রাখে; DynamoDB এমনিতেই তিনটা AZ জুড়ে।
  • Security group একে অন্যকে reference করে: internet → alb-sg (৮০/৪৪৩), alb-sg → app-sg, app-sg → cache-sg (৬৩৭৯) আর db-sg (৩৩০৬)। Data-র port কখনো 0.0.0.0/0-এর জন্য খুলবে না।
  • সব মিলিয়ে: classic three-tier web application।