#Saga Pattern คืออะไร

Saga Pattern เป็นรูปแบบการออกแบบสำหรับจัดการธุรกรรมทางธุรกิจที่ครอบคลุมหลาย Microservices โดยแบ่งธุรกรรมใหญ่เป็น Local Transactions แต่ละบริการบันทึกข้อมูลในฐานข้อมูลของตนเอง และประสานขั้นตอนถัดไปผ่านเหตุการณ์ (Events) หรือคำสั่ง (Commands) หากขั้นตอนใดล้มเหลว ระบบจะเรียก Compensating Transactions เพื่อชดเชยผลของขั้นตอนที่ทำสำเร็จไปแล้ว แทนการใช้ Rollback แบบฐานข้อมูลเดียว

การชดเชยไม่จำเป็นต้องคืนสภาพได้อย่างสมบูรณ์เสมอไป เช่น การส่งอีเมลไปแล้วไม่สามารถยกเลิกการรับรู้ของผู้รับได้

#ตัวอย่างระบบสั่งซื้อสินค้า

ประกอบด้วย Order Service, Inventory Service และ Payment Service ซึ่งต่างมีฐานข้อมูลของตนเอง

#เส้นทางสำเร็จ

  1. Order Service สร้างคำสั่งซื้อสถานะ PENDING
  2. Inventory Service จองสินค้า
  3. Payment Service ตัดชำระเงิน
  4. Order Service เปลี่ยนสถานะเป็น CONFIRMED

#เส้นทางล้มเหลว

หากการชำระเงินล้มเหลวหลังจากจองสินค้าแล้ว ระบบต้องยกเลิกการจองสินค้า และเปลี่ยนสถานะคำสั่งซื้อเป็น CANCELLED โดยบันทึกเหตุผลไว้

sequenceDiagram
    participant O as Order Service
    participant I as Inventory Service
    participant P as Payment Service
    O->>O: Create order (PENDING)
    O->>I: Reserve inventory
    I-->>O: Reserved
    O->>P: Charge payment
    alt Payment succeeded
        P-->>O: Paid
        O->>O: Confirm order
    else Payment failed
        P-->>O: Failed
        O->>I: Release inventory (compensation)
        I-->>O: Released
        O->>O: Cancel order
    end

#สองรูปแบบหลัก

หัวข้อ Choreography Orchestration
วิธีประสานงาน บริการตอบสนองต่อ Events ตัวกลาง Saga Orchestrator สั่งงาน
การเชื่อมโยง Event-driven กระจาย ขั้นตอนธุรกิจอยู่ในตัวประสาน
เหมาะกับ กระบวนการสั้นและไม่ซับซ้อน กระบวนการหลายขั้นและมีเงื่อนไข
ความท้าทาย ติดตามลำดับและ dependencies ยาก ต้องดูแล orchestrator และสถานะ

#Choreography

OrderCreated -> InventoryReserved -> PaymentSucceeded -> OrderConfirmed
                              |
                              +-> PaymentFailed -> InventoryReleased -> OrderCancelled

#Orchestration

Order Service -> Saga Orchestrator
                     |-> Reserve Inventory
                     |-> Charge Payment
                     |-> Confirm Order
                     +-> On failure: Release Inventory -> Cancel Order

#ตัวอย่าง Pseudocode ของ Orchestration

public void processOrder(String orderId) {
    boolean inventoryReserved = false;
    try {
        inventoryService.reserve(orderId); // idempotent
        inventoryReserved = true;
        paymentService.charge(orderId);     // idempotent
        orderService.confirm(orderId);
    } catch (Exception error) {
        if (inventoryReserved) {
            inventoryService.release(orderId); // retry safely
        }
        orderService.cancel(orderId);
    }
}

หมายเหตุ: ตัวอย่างนี้แสดงแนวคิดเท่านั้น ไม่ใช่ production-ready implementation การเรียกบริการผ่านเครือข่ายอาจ timeout หลังจากฝั่งปลายทางทำสำเร็จแล้ว จึงต้องตรวจสอบผลลัพธ์ที่ไม่แน่นอนก่อนชดเชย และต้องบันทึกสถานะ Saga แบบ durable

#แนวทางสำหรับ Production

  1. Idempotency: ทุกคำสั่งและ Compensation ต้องรองรับการส่งซ้ำโดยไม่เกิดผลซ้ำ เช่น ใช้ orderId หรือ operationId เป็น idempotency key
  2. Transactional Outbox: บันทึกข้อมูลธุรกิจและ Outbox Event ใน Local Transaction เดียวกัน เพื่อหลีกเลี่ยงปัญหาบันทึก DB สำเร็จแต่ส่ง Event ไม่สำเร็จ
  3. Inbox / Deduplication: ผู้รับตรวจสอบ Event ID เพื่อป้องกันการประมวลผลซ้ำ
  4. Retry และ Backoff: ใช้ retry แบบจำกัดจำนวน พร้อม exponential backoff และ dead-letter queue สำหรับงานที่ต้องตรวจสอบ
  5. Durable Saga State: บันทึกสถานะ ขั้นตอนที่เสร็จ และขั้นตอนที่ต้องชดเชย เพื่อกลับมาทำงานได้หลังระบบล่ม
  6. Observability: ติดตาม sagaId, orderId, trace ID, ระยะเวลา และจำนวน compensation
  7. Business Invariants: กำหนดข้อจำกัด เช่น ห้ามยืนยันคำสั่งซื้อหากยังไม่ได้รับผลยืนยันการชำระเงิน
  8. Isolation: Saga ไม่ได้ให้ ACID isolation ระหว่างบริการ ต้องออกแบบรับ concurrent updates และความไม่สอดคล้องชั่วคราว

#Workshop ที่แนะนำ

สร้างระบบ order-service, inventory-service, payment-service, saga-orchestrator พร้อม PostgreSQL แยกฐานข้อมูลตามบริการ และใช้ RabbitMQ หรือ Kafka สำหรับการสื่อสารแบบ asynchronous ทดสอบกรณีชำระเงินสำเร็จ ล้มเหลว timeout, duplicate event, orchestrator restart และ compensation ล้มเหลว

เกณฑ์ยอมรับ: คำสั่งซื้อไม่ถูก CONFIRMED หากยังชำระเงินไม่สำเร็จ; สินค้าถูกคืนเมื่อชำระเงินล้มเหลว; ข้อความซ้ำไม่ทำให้ตัดเงินหรือคืนสินค้าซ้ำ; ระบบกู้คืนงานที่ค้างได้หลังรีสตาร์ต

#สรุป

Saga Pattern ช่วยให้ Microservices ดำเนินกระบวนการธุรกิจข้ามฐานข้อมูลได้โดยไม่ต้องพึ่ง Distributed ACID Transaction แต่ต้องยอมรับ Eventual Consistency และออกแบบ Compensation, Idempotency, Durable State และ Monitoring ให้รัดกุม เลือก Choreography สำหรับงานที่ไม่ซับซ้อน และ Orchestration เมื่อต้องควบคุมลำดับงานอย่างชัดเจน