အခန်း ၁၆ :: Performance and Scalability

Software ၏ functional requirement များ မှန်ကန်စွာ အလုပ်လုပ်ရုံသာမက၊ သုံးစွဲသူများအတွက် လျင်မြန်သော တုံ့ပြန်မှု (fast response) နှင့် တည်ငြိမ်သော အတွေ့အကြုံ (stable experience) ကို ပေးစွမ်းနိုင်ရန်မှာလည်း အလွန်အရေးကြီးသည်။ Performance Engineering ဆိုသည်မှာ software ၏ performance ကို စနစ်တကျ တိုင်းတာခြင်း၊ သုံးသပ်ခြင်း၊ နှင့် မြှင့်တင်ခြင်းတို့ကို ပြုလုပ်သည့် discipline ဖြစ်သည်။

Scalability ဆိုသည်မှာ user load သို့မဟုတ် data ပမာဏ တိုးလာသည့်အခါ system က ထို workload ကို ကောင်းမွန်စွာ ကိုင်တွယ်နိုင်စွမ်း ဖြစ်သည်။

၁၆.၁ Performance Requirements and Benchmarking

Performance ကိစ္စ ပြောရင် "မြန်ရမယ်" လို့ ယေဘုယျ ပြောလို့ မရပါဘူး။ ဘယ်လောက် မြန်ရမှာလဲ။ ဘယ်နေရာမှာ မြန်ရမှာလဲ ဆိုတာ တိတိကျကျ သတ်မှတ်ထားဖို့ လိုပါတယ်။ ဒါကို Non-functional Requirement အနေနဲ့ တိုင်းတာလို့ရတဲ့ ဂဏန်းတွေနဲ့ သတ်မှတ်ရပါမယ်။

ဘာတွေ တိုင်းတာမလဲ (Key Metrics)

Latency (Response Time)

Request တစ်ခု ပို့လိုက်ရင် ဘယ်လောက်ကြာမှ အဖြေ ပြန်ရသလဲ ဆိုတာပါ။ ဒါပေမယ့် Average ပဲ ကြည့်လို့ မရပါဘူး။

ဥပမာ - "p95 Latency < 200ms" လို့ သတ်မှတ်လေ့ ရှိပါတယ်။ ဆိုလိုတာက Request ၁၀၀ မှာ ၉၅ ခုက 200ms အောက် (≤ 200ms) ဖြစ်ရမယ်လို့ ဆိုလိုတာပါ။ (p95 = 95th Percentile)။ Average က နည်းနေပေမယ့် တချို့ User တွေမှာ အရမ်းကြာနေတာမျိုး မဖြစ်အောင် p95, p99 တွေကို ကြည့်ရတာပါ။

တိုင်းတာတဲ့ အခါမှာလည်း Server မှာ ကြာတဲ့ အချိန် လား၊ User ဆီ အရောက်ပြန်ပို့တဲ့ အချိန် (End-to-End) လား ဆိုတာ တိတိကျကျ ခွဲခြား သိဖို့ လိုပါတယ်။

Throughput

အချိန်ယူနစ်တစ်ခု (ဥပမာ - ၁ စက္ကန့်) အတွင်းမှာ System က အလုပ် ဘယ်လောက် ပြီးနိုင်သလဲ ဆိုတာပါ။

Unit တွေ အနေနဲ့ RPS (Requests Per Second) သို့မဟုတ် TPS (Transactions Per Second) နဲ့ တိုင်းတာလေ့ ရှိပါတယ်။

ဥပမာ - "Server က တစ်စက္ကန့်ကို Request ၁၀၀၀ (1000 RPS) လက်ခံနိုင်ရမယ်" ဆိုတာမျိုးပါ။

Error Rate

Request ၁၀၀ လာရင် ဘယ်နှစ်ခါ Fail ဖြစ်လဲ ဆိုတာပါ။

Fail ဖြစ်တယ် ဆိုရာမှာ 5xx Errors (Server ဘက်က မှားတာ)၊ Timeouts (ကြာလွန်းလို့ ပြတ်သွားတာ) နဲ့ Rate Limits (များလွန်းလို့ ပိတ်ချတာ) တွေ အကုန် ပါဝင်ပါတယ်။ User ဘက်က မှားတဲ့ 4xx Errors တွေကိုတော့ Error အဖြစ် ထည့်တွက်မလား၊ မတွက်ဘူးလား ဆိုတာ ကိုယ့်ရဲ့ SLO (Service Level Objective) ပေါ် မူတည်ပြီး ဆုံးဖြတ်ရပါမယ်။

ဘယ်လို စစ်ဆေးမလဲ

Benchmarking ဆိုတာကတော့ ကိုယ့် System ရဲ့ ပုံမှန် အခြေအနေမှာ ဘယ်လောက် နိုင်သလဲ ဆိုတာ Baseline တစ်ခု သတ်မှတ်ဖို့ မှတ်တမ်းတင်ထားတာပါ။ Environment တူညီဖို့ လိုသလို၊ System က စစချင်း (Cold start) မှာ နှေးတတ်တဲ့ အတွက် Warm-up လုပ်ပြီးမှ တိုင်းတာသင့်ပါတယ်။

Load Testing ကတော့ ပုံမှန် လာနေကျ User ပမာဏ (Expected Load) လောက် ဝင်လာရင် ခံနိုင်ရည် ရှိမရှိ စမ်းသပ်တာပါ။

Stress Testing ကတော့ System ပျက်သွားတဲ့အထိ တမင် ဖိအားပေးပြီး Break Point ကို ရှာတာပါ။ ဘယ်လောက်ထိ ခံနိုင်လဲ သိချင်လို့ပါ။

Soak Testing ဆိုတာလည်း ရှိပါသေးတယ်။ သူကတော့ အကြာကြီး (ဥပမာ - ၂၄ နာရီ) Run ထားပြီးတော့ Memory Leak ဖြစ်မဖြစ်၊ Resource တွေ ပြည့်မလာဘူးလား ဆိုတာ စစ်ဆေးတာပါ။ Apache JMeter လို Tool မျိုးနဲ့ စမ်းသပ်နိုင်ပါတယ်။

၁၆.၂ Performance Analysis and Profiling

Performance ပြဿနာ ရှင်းမယ် ဆိုရင် အရင်ဆုံး ဘယ်နေရာမှာ ကြာနေလဲ ဆိုတာ အရင် ရှာရပါမယ်။ ဒါကို Bottleneck (ပုလင်းလည်ပင်း) ရှာတယ်လို့ ခေါ်ပါတယ်။ ပုလင်းလည်ပင်း ကျဉ်းနေရင် ရေတွေ ဘယ်လောက် များများ ထွက်လို့ မရသလို၊ System မှာလည်း တစ်နေရာက ကြာနေရင် ကျန်တဲ့ နေရာတွေပါ လိုက်နှေးနေတတ်ပါတယ်။

Pareto Principle (80/20 Rule) အရ ပြောရရင် ကြာချိန်ရဲ့ ၈၀% ဟာ Code ရဲ့ ၂၀% လောက်ကပဲ ဖြစ်စေတာ များပါတယ်။ ဆိုလိုတာက Code အများကြီး ပြင်စရာ မလိုပါဘူး။ အဓိက ကြာစေတဲ့ အချက် (Critical Part) ကို ရှာတွေ့ဖို့ပဲ လိုပါတယ်။

ဒါကြောင့် ဘယ်နေရာ ကြာလဲ သိရအောင် Profiler Tools တွေ သုံးရပါတယ်။ Profiler က Code ကို Run နေရင်းနဲ့ Function တစ်ခုချင်းစီ က CPU Time ဘယ်လောက်ကြာလဲ၊ Memory ဘယ်လောက်စားလဲ ဆိုတာ အတိအကျ တိုင်းတာပေးနိုင်ပါတယ်။

Developer က ကိုယ့်ထင်မြင်ချက် (Guesswork) နဲ့ လျှောက်မပြင်သင့်ပါဘူး။ Profiler က ပြတဲ့ Data ကို ကြည့်ပြီးမှ အမှန်တကယ် ကြာနေတဲ့ နေရာကို ပြင်ဆင် (Optimize) လုပ်သင့်ပါတယ်။

၁၆.၃ Performance Optimization Techniques

Code ရေးတဲ့ အခါ ဘာတွေ သတိထားရမလဲ။

Algorithmic Optimization

Algorithm ရွေးချယ်မှု မှားယွင်းခြင်းက Performance ကို အထိခိုက်ဆုံးပါပဲ။

ဥပမာ - Data အလုံး ၁ သန်းကို စီချင်တယ် (Sort လုပ်ချင်တယ်) ဆိုပါစို့။

ဒါကြောင့် ကိုယ်ရေးတဲ့ Code က Loop တွေ ဘယ်နှစ်ထပ် ဖြစ်နေလဲ (Nested Loops)၊ Big O Notation အရ ဘယ်လောက် Complexity ရှိလဲ ဆိုတာ Developer တိုင်း သိထားသင့်ပါတယ်။

Asynchronous Operations

I/O Operation တွေ ဖြစ်တဲ့ File ဖတ်တာ၊ Network (API) ခေါ်တာ တွေက CPU ကို မလိုအပ်ပဲ စောင့်ခိုင်းထားသလို ဖြစ်စေပါတယ်။

Code ကို Asynchronous (Non-blocking I/O) ပုံစံ ပြောင်းလိုက်မယ်ဆိုရင်၊ I/O စောင့်နေတဲ့ အချိန်မှာ CPU က အလကား မနေပဲ တခြား Request တွေကို လုပ်ပေးနိုင်တဲ့ အတွက် Throughput တက်လာပါလိမ့်မယ်။

သတိပြုရန်: Async လုပ်လိုက်လို့ Task တစ်ခုချင်းစီရဲ့ ကြာချိန် (Latency) လျော့သွားတာ မဟုတ်ပါဘူး။ တစ်ပြိုင်နက် လုပ်နိုင်တဲ့ အရည်အသွေး (Concurrency) ကောင်းလာတာသာ ဖြစ်ပါတယ်။ Memory ပေါ်မှာ တွက်ချက်ရတဲ့ (CPU Bound) အလုပ်တွေ ဆိုရင်တော့ Async လုပ်လည်း မထူးပါဘူး။

Resource Pooling

Database Connection ဖွင့်တာတွေက "Expensive Operation" (စရိတ်ကြီးတဲ့ အလုပ်) တွေ ဖြစ်ပါတယ်။ TCP Handshake လုပ်ရတာတို့၊ Authentication လုပ်ရတာတို့က အချိန်ကြာပါတယ်။

ဒါကြောင့် Connection တွေကို ခဏခဏ ဖွင့်လိုက် ပိတ်လိုက် မလုပ်ပဲ ဖွင့်ပြီးသား Connection တွေကို Connection Pool တစ်ခုထဲမှာ သိမ်းထားပြီး လိုအပ်တဲ့ အချိန် ယူသုံး၊ ပြီးရင် ပြန်ထည့်ထားတာမျိုး လုပ်သင့်ပါတယ်။

၁၆.၄ Scalability Patterns (Horizontal vs. Vertical Scaling)

User တွေ များလာရင် Server နိုင်တော့မှာ မဟုတ်ပါဘူး။ အဲဒီအခါ ဘယ်လို ဖြေရှင်းမလဲ။

Vertical Scaling (Scaling Up)

လက်ရှိ Server ရဲ့ Hardware (CPU, RAM, SSD) ကို အဆင့်မြှင့်တာပါ။

Horizontal Scaling (Scaling Out)

Server အလုံးရေ ကို တိုးပြီး Load မျှ သုံးတဲ့ နည်းလမ်းပါ။ Server တစ်လုံးတည်းက လုပ်မယ့်အစား ၃ လုံးလောက် ခွဲပြီး လုပ်လိုက်တာပါ။


graph TD

    subgraph "Vertical Scaling (Scaling Up)"

        A["Server (Small)"] --> B["Server (Big)"]

    end

    subgraph "Horizontal Scaling (Scaling Out)"

        Client --> F(Load Balancer)

        C[Server 1]

        D[Server 2]

        E[Server 3]

        F --> C

        F --> D

        F --> E

    end

၁၆.၅ Caching Strategies

Caching ဆိုတာ မကြာခဏ သုံးနေရတဲ့ Data တွေကို ယူရခက်တဲ့ နေရာ (Database) ကနေ ယူမယ့်အစား၊ ယူရလွယ်တဲ့ နေရာ (RAM) မှာ ခဏ သိမ်းထားတာပါ။ ဒါဆိုရင် Data လိုချင်တိုင်း Database ကို သွားသွား ခေါက်နေစရာ မလိုတော့လို့ ပိုမြန်ပါတယ်။

Caching Flow (Cache-Aside Pattern)

အောက်ပါ Diagram တွင် Application သည် data လိုချင်သည့်အခါ Cache ကို အရင်စစ်ဆေးပုံ (Cache Hit vs Cache Miss) ကို ပြသထားသည်။


sequenceDiagram

    autonumber

    participant Client

    participant App as Application

    participant Cache

    participant DB as Database

    Client->>App: Request Data (ID: 101)

    App->>Cache: Get Data (ID: 101)

    

    alt Cache Hit (Data exists)

        Cache-->>App: Return Data

    else Cache Miss (Data not found)

        Cache-->>App: Not Found

        App->>DB: Query Data (ID: 101)

        DB-->>App: Return Result

        App->>Cache: Set Data (ID: 101)

    end

    

    App-->>Client: Return Response

၁၆.၅.၁ Cache Write Strategies

Data အသစ်ဝင်လာရင် Cache ထဲကို ဘယ်လို ထည့်မလဲ ဆိုတာ မူဝါဒ (Policy) ထားရှိဖို့ လိုပါတယ်။

1. Write-through

Data လာရင် Cache ထဲကိုလည်း ထည့်တယ်၊ Database ထဲကိုလည်း တစ်ခါတည်း (Synchronous) ထည့်ပါတယ်။


sequenceDiagram

    autonumber

    participant Client

    participant App

    participant Cache

    participant DB

    Client->>App: Write Data

    Note right of App: Update both Cache & DB

    App->>Cache: Save Data

    App->>DB: Save Data

    

    Cache-->>App: Success

    DB-->>App: Success

    

    App-->>Client: Acknowledge Write

2. Write-around

Database ထဲကိုပဲ တိုက်ရိုက် ထည့်လိုက်ပါတယ်။ Cache ကို ကျော်သွားတယ်။ ပြန်သုံးချင်တဲ့ အချိန်ကျမှ Database ကနေ ယူပြီး Cache ထဲ ထည့် (Lazy Load) ပါတယ်။


sequenceDiagram

    autonumber

    participant Client

    participant App

    participant Cache

    participant DB

    Client->>App: Write Data

    Note right of App: Update DB only (Bypass Cache)

    App->>DB: Save Data

    DB-->>App: Success

    App-->>Client: Acknowledge Write

    

    Note over Client, DB: Later, when reading data...

    

    Client->>App: Read Data

    App->>Cache: Get Data

    Cache-->>App: Miss (Not Found)

    App->>DB: Get Data

    DB-->>App: Return Data

    App->>Cache: Save Data (Lazy Load)

    App-->>Client: Return Data

3. Write-back (Write-behind)

ဒါကတော့ Cache ထဲကိုပဲ အရင် ထည့်လိုက်တာပါ။ User ကို ချက်ချင်း အိုကေ ပြန်ပြောလိုက်တယ်။ ပြီးမှ နောက်ကွယ်ကနေ (Asynchronous) Database ထဲကို လိုက်ထည့်တာပါ။


sequenceDiagram

    autonumber

    participant Client

    participant App

    participant Cache

    participant DB

    Client->>App: Write Data

    Note right of App: Update Cache only

    App->>Cache: Save Data (Dirty)

    Cache-->>App: Ack

    App-->>Client: Success (Fast Response)

    

    Note over Cache, DB: Asynchronously...

    

    loop Background Sync

        Cache->>DB: Flush Dirty Data

        DB-->>Cache: Success

    end

၁၆.၅.၂ Entity Caching vs. Query Caching

ဒါကလည်း အရေးကြီးပါတယ်။ ဘာကို Cache လုပ်မှာလဲ ပေါ့။

1. Entity Caching ( ID နဲ့ သိမ်းခြင်း)

User တစ်ယောက်ချင်းစီ ID နဲ့ သိမ်းတာမျိုးပါ။ user:101 ဆိုပြီး Key-Value ပုံစံနဲ့ သိမ်းလိုက်တယ်။

User Update လုပ်ရင် အဲဒီ Key တစ်ခုတည်း ဖျက်လိုက်ရင် ရပြီ။ ရှင်းတယ်။ Manage လုပ်ရတာ လွယ်ပါတယ်။

2. Query Caching (Complex Pattern)

SELECT * FROM users WHERE age > 10 ဆိုတဲ့ Query ရလဒ်ကြီး တစ်ခုလုံးကို Cache လုပ်လိုက်တာပါ။

ပြဿနာကတော့ Invalidation (Cache ဖျက်တာ) ပါပဲ။ User 101 ကို အသက် ၁၁ နှစ် (age: 11) လို့ ပြင်လိုက်ရင်၊ ဒီ User ပါဝင်နေတဲ့ Query တွေ အကုန်လုံး (ဥပမာ age > 5, age < 20 etc.) လိုက်မှားကုန်ပါပြီ။ ဘယ် Query တွေနဲ့ ငြိနေလဲ လိုက်ရှာဖို့က အရမ်း ခက်ခဲပါတယ်။

ဒါကြောင့် ဒီလို Complex Query တွေ Cache လုပ်မယ်ဆိုရင် Write-through တွေ မလုပ်ပဲ TTL (Time To Live) တိုတိုလေး ထားပြီး သက်တမ်းကုန်ရင် ပျက်သွားအောင် စောင့်တာ လက်တွေ့အကျဆုံးပါပဲ။


၁၆.၅.၃ Memory ပြည့်သွားရင် ဘယ်လိုလုပ်မလဲ (Cache Eviction Policies)

Cache ဆိုတာ RAM ပေါ်မှာ သိမ်းတာ ဆိုတော့ နေရာ အကန့်အသတ် ရှိပါတယ်။ ပြည့်သွားရင် အဟောင်းတွေ ဖျက်ထုတ်ရပါတယ်။ ဘယ်ဟာကို ရွေးဖျက်မလဲ ဆိုတဲ့ မူဝါဒတွေ ရှိပါတယ်။


၁၆.၅.၄ ဖြစ်တတ်တဲ့ ပြဿနာများ (Common Pitfalls)

1. Cache Penetration

ပြဿနာ: Hacker က Cache ထဲမှာ မရှိသလို၊ Database ထဲမှာလည်း မရှိတဲ့ Key တွေ (ဥပမာ - ID အတုတွေ) နဲ့ တမင် လာခေါ်တာပါ။

Fail ဖြစ်သွားတဲ့ Request တွေက Cache မှာ မရှိတော့ Database အထိ တောက်လျှောက် ရောက်လာပြီး Database ကို ဝန်ပိစေပါတယ်။

ဖြေရှင်းနည်း:

  1. Bloom Filter သုံးပြီး မရှိနိုင်တဲ့ Key တွေကို အရင် စစ်ထုတ်လိုက်တာ။

  2. Database မှာ မတွေ့ရင် Null Value (မရှိကြောင်း) ကိုပါ Cache ထဲမှာ ခဏ (TTL တိုတိုလေးနဲ့) သိမ်းထားလိုက်တာမျိုး လုပ်နိုင်ပါတယ်။

2. Cache Stampede (Dog-piling)

ပြဿနာ: လူသုံးများတဲ့ Data (Hot Key) တစ်ခု သက်တမ်းကုန်သွားတဲ့ အချိန်မှာ၊ User အများကြီး တစ်ပြိုင်နက် ဝင်လာတာပါ။ Cache မရှိတော့ အားလုံး Database ကို ဝိုင်းခေါ်သလို ဖြစ်သွားပြီး Database Down သွားနိုင်ပါတယ်။

ဖြေရှင်းနည်း:

  1. Mutex Locking: တစ်ယောက်ကိုပဲ Database သွားယူခိုင်းပြီး ကျန်တဲ့ လူတွေကို ခဏ စောင့်ခိုင်းတာမျိုး။

  2. Early Expiration: TTL မကုန်ခင် ကြိုပြီး Background ကနေ Refresh လုပ်ထားတာမျိုး။

3. Cache Avalanche

ပြဿနာ: Cache Key အများကြီးက တိုင်ပင်ထားသလို တချိန်တည်း သက်တမ်းကုန် (Expire) သွားတာပါ။ ဒါဆို Request တွေ အကုန်လုံး Database ပေါ် ပြုံကျလာပါလိမ့်မယ်။

ဖြေရှင်းနည်း:

Randomize TTL (Jitter): သက်တမ်းကုန်မယ့် အချိန် (TTL) ကို တစ်ခုနဲ့ တစ်ခု မတူအောင် နည်းနည်း စီ လွှဲထားလိုက်ပါ။ ဥပမာ - ၁၀ မိနစ် အတိ မထားပဲ ၉ မိနစ် နဲ့ ၁၁ မိနစ် ကြား Random ထားလိုက်တာမျိုးပါ။

Cache အမျိုးအစားများ

In-Memory Cache

Application ရဲ့ RAM ထဲမှာပဲ သိမ်းတာပါ။ ဒါက အမြန်ဆုံးပါပဲ။

Distributed Cache

Redis, Memcached လိုမျိုး သီးသန့် Server နဲ့ သိမ်းတာပါ။ Server တွေ အများကြီး က ဝိုင်းသုံးလို့ ရတဲ့ အားသာချက် ရှိပါတယ်။

Content Delivery Network (CDN)

Static file တွေ (ပုံတွေ၊ CSS တွေ) ကို ကမ္ဘာအနှံ့က Server တွေပေါ် ဖြန့်သိမ်းထားတာပါ။ User နဲ့ အနီးဆုံး Server ကနေ ပို့ပေးတော့ အရမ်း မြန်ပါတယ်။

၁၆.၆ Database Performance and Optimization

System တော်တော်များများ နှေးရတဲ့ အဓိက တရားခံက Database မှာ ဖြစ်လေ့ရှိပါတယ်။

Database Indexing

စာအုပ်မှာ မာတိကာ ပါသလိုပါပဲ။ Database မှာ Index ခံတယ်ဆိုတာ B-Tree (သို့မဟုတ် Hash) Data Structure နဲ့ သီးသန့် စီထားတာပါ။

Database Pagination

Data အများကြီးကို တစ်ခါတည်း ယူလိုက်ရင် Memory Pressure တက်ပြီး Database Crash ဖြစ်နိုင်ပါတယ်။

Database Partitioning vs. Sharding

N+1 Query Problem

Loop ပတ်ပြီး Query ခေါ်မိတဲ့ ပြဿနာပါ။

ဥပမာ - Blog Post ၁၀ ခု ယူမယ် (၁ ခါ)။ ပြီးမှ Loop ပတ်ပြီး Post တစ်ခုချင်းရဲ့ Author ကို လိုက်ယူမယ် (၁၀ ခါ)။ စုစုပေါင်း Query ၁၁ ခါ (1 + N) ဖြစ်သွားပါတယ်။

ဖြေရှင်းနည်း:

ORM တွေမှာ Eager Loading (with('author')) သုံးပြီး တစ်ခါတည်း JOIN လုပ်ယူလိုက်ရင် Query တစ်ခေါက်တည်းနဲ့ ပြီးပါတယ်။

Query Optimization

Database Query တွေ နှေးနေရင် EXPLAIN ANALYZE လို command မျိုး သုံးပြီး Execution Plan ကို စစ်ဆေးရပါတယ်။

"Full Table Scan" ဖြစ်နေလား၊ Index မထိပဲ ဖြစ်နေလား၊ Join တွေက မှားနေလား ဆိုတာ Database Engine ရဲ့ လုပ်ဆောင်ချက်ကို အသေးစိတ် ကြည့်ပြီး ပြင်ဆင်ရပါမယ်။

Connection Pooling

(ဒါက ၁၆.၃ မှာ ပြောခဲ့တဲ့ Resource Pooling ပါပဲ) Database Connection တွေကို အရှင် မွေးထားပြီး ပြန်သုံးတဲ့ နည်းလမ်းပါ။

၁၆.၇ Load Balancing and High Availability

Load Balancer

Server တွေ အများကြီး သုံးတဲ့အခါ Traffic ကို မျှပေးမယ့် (Traffic Distribution) ကောင် လိုပါတယ်။ ဒါက Load Balancer ပါ။

Load Balancing Algorithm အမျိုးမျိုး ရှိပါတယ်:


graph TD

    Client1(Client) --> LB[Load Balancer]

    Client2(Client) --> LB

    LB --> Server1[Server 1]

    LB --> Server2[Server 2]

    LB --> Server3[Server 3]

High Availability (HA)

System တစ်ခုလုံး ဘယ်တော့မှ မရပ်သွားအောင် (No Single Point of Failure) လုပ်ဆောင်ခြင်းပါ။


graph TD

    C[Clients] --> LB[(Load Balancer - Multi-AZ / HA)]

    subgraph "Availability Zone A"

      S1[App Server A]

      DB1[(Primary DB)]

    end

    subgraph "Availability Zone B"

      S2[App Server B]

      DB2[(Standby/Replica DB)]

    end

    LB --> S1

    LB --> S2

    S1 --> DBE[(DB Endpoint/Proxy)]

    S2 --> DBE

    DBE --> DB1

    DBE -. failover switch .-> DB2

    DB1 -- Replication --> DB2

    DB2 -. Promote on failover .-> DB1