T3 — Choreography based Saga Pattern - congsinhv/fluxion GitHub Wiki
Choreography-based Saga Pattern trong Hệ Thống MDM
Issue: #13 — Nghiên cứu Choreography-based Saga Pattern Tuần: 3 | 07/04 – 13/04/2026 Numbering chính thức: Mục 2.5 theo Master TOC (Chương 2)
2.5.1 Vấn Đề Giao Dịch Phân Tán trong Microservices
Trong các hệ thống monolithic truyền thống, các giao dịch ACID (Atomicity, Consistency, Isolation, Durability) được quản lý bởi một cơ sở dữ liệu duy nhất, đảm bảo tính toàn vẹn thông qua cơ chế khóa và rollback tự động. Tuy nhiên, khi chia tách ứng dụng thành microservices, mỗi dịch vụ quản lý cơ sở dữ liệu độc lập, làm cho giao dịch ACID xuyên dịch vụ trở nên không khả thi [3].
Không có cơ chế "rollback toàn cầu" trong hệ thống phân tán. Đây là vấn đề mà Saga Pattern giải quyết — cơ chế quản lý giao dịch dài, phân tán bằng chuỗi giao dịch cục bộ với compensating transactions [3].
Áp dụng trong Fluxion:
action-triggerLambda tạo command record trong RDS →ios-process-actionLambda gửi APNS push đến thiết bị → Nếu thiết bị không phản hồi, cần hoàn nguyên — không có cơ chế "rollback toàn cầu" giữa các Lambda và ECS service.
2.5.2 Định Nghĩa Saga Pattern
2.5.2.1 Khái Niệm Ban Đầu (Garcia-Molina & Salem, 1987)
Saga Pattern được đề xuất bởi Hector Garcia-Molina và Kenneth Salem tại ACM SIGMOD 1987 [1]. Các tác giả định nghĩa Long Lived Transaction (LLT) — giao dịch kéo dài giữ tài nguyên lâu, làm chậm các giao dịch khác.
Giải pháp: một saga là chuỗi giao dịch T₁, T₂, ..., Tₙ với đảm bảo:
- Hoặc tất cả Tᵢ hoàn thành thành công
- Hoặc chuỗi compensating transactions C₁...Cₖ được thực thi để hoàn nguyên
2.5.2.2 Định Nghĩa Hiện Đại (Richardson, 2018)
Chris Richardson [3] định nghĩa lại Saga cho microservices cloud-native: chuỗi giao dịch cục bộ trong đó mỗi giao dịch cập nhật dữ liệu của dịch vụ và xuất bản sự kiện để kích hoạt bước tiếp theo. Nếu một bước thất bại, saga thực thi compensating transactions để hoàn nguyên.
Điểm khác biệt so với 1987:
- Tập trung vào eventual consistency thay vì strong consistency
- Sử dụng messaging/events làm cơ chế điều phối
- Thích ứng với môi trường bất đồng bộ và cloud-native
2.5.3 So Sánh Orchestration vs Choreography
Saga có hai mô hình điều phối [3]:
| Khía cạnh | Orchestration | Choreography |
|---|---|---|
| Điều phối bởi | Orchestrator trung tâm | Sự kiện giữa các dịch vụ |
| Mô hình giao tiếp | Synchronous (request-reply) | Asynchronous (pub-sub) |
| Coupling | Tight (orchestrator biết tất cả) | Loose (mỗi service biết input/output của mình) |
| Debugging | Dễ (flow trong orchestrator) | Khó hơn (flow phân tán) |
| Khả năng mở rộng | Bị giới hạn bởi orchestrator | Tốt (scale độc lập) |
| Single Point of Failure | Orchestrator là SPoF | Không có SPoF rõ ràng |
| Phù hợp serverless | Không (cần coordinator dài hạn) | Có (event-driven, stateless) |
Bảng 2.5.1: So sánh Orchestration và Choreography-based Saga
2.5.4 Tại Sao Choreography Phù Hợp với Fluxion
2.5.4.1 Kiến Trúc Bất Đồng Bộ
Fluxion dùng SQS làm backbone pipeline lệnh — bản chất là bất đồng bộ. Không có orchestrator trung tâm; các dịch vụ trao đổi qua sự kiện, tương thích tự nhiên với choreography [3].
2.5.4.2 Không Có Orchestrator Tập Trung
Mỗi Lambda chỉ biết phần việc của mình:
action-triggerLambda: nhận từ SQS, tạo record RDS, publish SNSios-process-actionLambda: nhận từ SQS, gửi APNScheckin-ecs: nhận callback từ device, cập nhật FSM
Không có dịch vụ nào "biết" toàn bộ flow.
2.5.4.3 Loose Coupling và Độc Lập Triển Khai
Choreography cho phép các dịch vụ phát triển và triển khai độc lập [3]. Thêm subscriber mới (ví dụ: audit-logger) vào device-event-sns không cần thay đổi bất kỳ dịch vụ hiện có nào.
2.5.4.4 Phù Hợp với EDA
Fluxion đã dùng SNS/SQS theo EDA pattern (T2). Choreography là mô hình giao tiếp tự nhiên cho EDA [2] — mỗi service là event consumer + event producer.
2.5.5 Command Pipeline Fluxion Như Một Choreography Saga
2.5.5.1 Ánh Xạ Các Bước Sang Saga Participants
| Bước | Layer | Service | Giao dịch cục bộ | State sau bước | Sự kiện |
|---|---|---|---|---|---|
| 1 | BE | action-resolver λ |
Validate (busy + transition) + enqueue SQS | — | — |
| 2 | BE | action-trigger λ |
INSERT action_executions (ACTION_PENDING), UPDATE device |
ACTION_PENDING |
→ command-sns |
| 3 | OEM | apns-sender λ |
Cache command + send APNS silent push | — | — |
| 4 | OEM | mdm-handler λ |
Device polls → serve command from cache | — | — |
| 5 | — | iOS Device | Execute command, send result to OEM | — | — |
| 6 | OEM | mdm-handler λ |
Receive result, publish event | — | → checkin-sns |
| 7 | BE | checkin-handler λ |
UPDATE action_executions + devices + INSERT milestones | ACTION_COMPLETED |
— |
Architecture note: BE chỉ biết DB, OEM chỉ biết 3rd party (APNS + device). Giao tiếp qua SNS/SQS events. Xem chi tiết tại T5 — Command Pipeline và T4 — Kiến trúc tổng thể.
Bảng 2.5.2: Ánh xạ command pipeline sang Choreography Saga participants
2.5.5.2 Schema: action_executions
CREATE TABLE action_executions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
device_id UUID NOT NULL,
assigned_action_id UUID NOT NULL,
state VARCHAR(20) NOT NULL DEFAULT 'ACTION_PENDING',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
ext_fields JSONB
-- ext_fields: { "failed_reason": "...", "retry_count": 2, "apns_message_id": "..." }
);
CREATE INDEX idx_ae_device_id ON action_executions(device_id);
CREATE INDEX idx_ae_assigned_action_id ON action_executions(assigned_action_id);
CREATE INDEX idx_ae_active_state ON action_executions(state)
WHERE state NOT IN ('ACTION_COMPLETED', 'ACTION_FAILED');
2.5.5.3 Idempotency
Vì SQS cung cấp at-least-once delivery, các giao dịch phải idempotent [3]:
-- Safe (idempotent): chuyển ACTION_SENT → ACTION_FAILED
UPDATE action_executions
SET state = 'ACTION_FAILED',
updated_at = NOW(),
ext_fields = jsonb_set(COALESCE(ext_fields, '{}'), '{failed_reason}', '"device_timeout"')
WHERE id = 'exec-456' AND state = 'ACTION_SENT';
-- Lần thứ hai: state đã là 'ACTION_FAILED' → WHERE không match → no-op
2.5.6 Compensating Transactions và Chiến Lược Phục Hồi
2.5.6.1 Định Nghĩa
Giao dịch bù trừ là giao dịch đảo ngược hiệu quả của giao dịch đã commit. Richardson [3] yêu cầu: idempotent và retryable.
Ví dụ:
- Giao dịch ban đầu:
INSERT INTO action_executions (state='ACTION_PENDING') - Compensating:
UPDATE action_executions SET state='ACTION_FAILED' WHERE id=... AND state IN ('ACTION_PENDING','ACTION_SENT')
2.5.6.2 Kịch Bản Thất Bại
Kịch bản 1 — Device không phản hồi (timeout):
T=0s: action-trigger: INSERT action_executions (ACTION_PENDING)
T=1s: ios-process-action: gửi APNS → UPDATE ACTION_SENT
T=31s: Timeout — checkin-ecs không nhận callback
→ Compensating: UPDATE action_executions SET state='ACTION_FAILED',
ext_fields='{"failed_reason":"device_timeout"}'
WHERE id=... AND state='ACTION_SENT'
→ User notification: "Lock command failed — device offline"
Kịch bản 2 — APNS delivery failure:
T=0s: action-trigger: INSERT action_executions (ACTION_PENDING)
T=1s: ios-process-action: APNS returns 400 (BadDeviceToken)
→ Compensating: UPDATE action_executions SET state='ACTION_FAILED',
ext_fields='{"failed_reason":"apns_bad_token"}'
WHERE id=... AND state='ACTION_PENDING'
→ UPDATE devices SET ext_fields=jsonb_set(ext_fields, '{invalid_token}', 'true')
2.5.6.3 DLQ Retry Strategy
action-trigger-sqs
→ action-trigger Lambda
├─ Success → INSERT action_executions (ACTION_PENDING) + publish SNS
└─ Exception → after maxReceiveCount → DLQ
action-trigger-dlq
→ DLQ handler Lambda
├─ Recoverable? → retry với exponential backoff → push back to primary queue
└─ Non-recoverable? → UPDATE action_executions SET state=ACTION_FAILED + alert ops team
2.5.7 Finite State Machine (FSM) Cho Trạng Thái Lệnh
2.5.7.1 Định Nghĩa Trạng Thái
| Trạng thái | Owner | Mô tả | Chuyển tiếp sang |
|---|---|---|---|
| ACTION_PENDING | action-trigger Lambda |
Action khởi tạo trong RDS, message enqueued tới ios-process-action | ACTION_SENT, ACTION_FAILED |
| ACTION_SENT | checkin-ecs |
Nhận event từ ios-process-action — APNS gửi thành công, chờ device apply | ACTION_COMPLETED, ACTION_FAILED |
| ACTION_COMPLETED | checkin-ecs |
Device đã apply action, callback received | (terminal) |
| ACTION_FAILED | Bất kỳ service | Lỗi ở bất kỳ bước — APNS error, timeout, device error | (terminal) |
Bảng 2.5.3: action_executions FSM states trong Fluxion
2.5.7.2 FSM State Diagram
stateDiagram-v2
[*] --> ACTION_PENDING: action-trigger\nINSERT action_executions
ACTION_PENDING --> ACTION_SENT: checkin-ecs nhận event\nAPNS gửi thành công
ACTION_PENDING --> ACTION_FAILED: ios-process-action\nAPNS error (BadToken, v.v.)
ACTION_SENT --> ACTION_COMPLETED: checkin-ecs\nnhận device callback
ACTION_SENT --> ACTION_FAILED: timeout — device\nkhông gửi callback
ACTION_COMPLETED --> [*]
ACTION_FAILED --> [*]
Hình 2.2: Finite State Machine cho action_executions lifecycle trong Fluxion
2.5.8 Sequence Diagram: Choreography Saga Flow
sequenceDiagram
participant User as Người Dùng
participant API as AppSync
participant AR as action-resolver λ
participant AT as action-trigger λ
participant RDS as RDS PostgreSQL
participant SNS1 as command-sns
participant APNS_L as apns-sender λ (OEM)
participant CACHE as Command Cache
participant APNS as Apple APNS
participant Device as iOS Device
participant APIGW as API Gateway mTLS
participant MDM as mdm-handler λ (OEM)
participant SNS2 as checkin-sns
participant CK as checkin-handler λ
User->>API: mutation assignAction(deviceId, actionId)
API->>AR: resolve
Note over AR,AT: BE Layer — Validate + Create
AR->>RDS: SELECT device, action (validate)
AR->>AT: enqueue via SQS
AT->>RDS: INSERT action_executions (ACTION_PENDING)
AT->>RDS: UPDATE devices SET assigned_action_id
AT->>SNS1: publish command {command_uuid, tokens, payload}
Note over APNS_L,MDM: OEM Layer — MDM Protocol
SNS1->>APNS_L: via SQS
APNS_L->>CACHE: cache command
APNS_L->>APNS: silent push {"mdm":"PushMagic"}
APNS-->>Device: wake up
Device->>APIGW: PUT /mdm (Idle)
APIGW->>MDM: mTLS verified
MDM->>CACHE: read + delete command
MDM-->>Device: 200 {CommandUUID, Command}
Device->>Device: execute command
Device->>APIGW: PUT /mdm (Acknowledged)
APIGW->>MDM: trigger
MDM->>SNS2: publish {ACTION_COMPLETED, command_uuid}
MDM-->>Device: 200 (empty)
Note over CK: BE Layer — Update DB
SNS2->>CK: via SQS
alt ACTION_COMPLETED
CK->>RDS: UPDATE action_executions SET status=ACTION_COMPLETED
CK->>RDS: UPDATE devices SET current_policy_id, assigned_action_id=NULL
CK->>RDS: INSERT milestones
Note over RDS: ✓ Saga hoàn thành
else ACTION_FAILED
CK->>RDS: UPDATE action_executions SET status=ACTION_FAILED
CK->>RDS: UPDATE devices SET assigned_action_id=NULL
Note over RDS: ✗ Compensating — device state giữ nguyên
end
opt APNS failure
APNS_L->>SNS2: publish {ACTION_FAILED, reason: apns_error}
SNS2->>CK: via SQS
CK->>RDS: UPDATE action_executions → ACTION_FAILED
end
opt Timeout
Note over RDS: timeout-handler λ periodic check
Note over RDS: mark stale ACTION_PENDING → ACTION_FAILED
end
Note over AT,ECS: Choreography Saga Ends
Hình 2.3: Sequence diagram Choreography Saga flow trong Fluxion
2.5.9 So Sánh Saga vs Two-Phase Commit (2PC)
| Khía cạnh | Saga (Choreography) | Two-Phase Commit (2PC) |
|---|---|---|
| Tính nhất quán | Eventual Consistency | Strong Consistency (ACID) |
| Khóa tài nguyên | Không có | Giữ lâu dài (bottleneck) |
| Độ trễ | Cao (async) | Thấp (synchronous) |
| Khả năng mở rộng | Cao (dịch vụ độc lập) | Thấp (coordinator là SPoF) |
| Tolerant failure | Tốt (DLQ retry) | Yếu (timeout → blocked) |
| Rollback | Compensating transactions | Tự động (DB-level) |
| Phù hợp serverless | ✓ Rất tốt | ✗ Không phù hợp |
| Phù hợp async MDM | ✓ Tối ưu | ✗ Không phù hợp |
Bảng 2.5.4: So sánh Saga Pattern và Two-Phase Commit
Lý do 2PC không phù hợp cho Fluxion:
- Blocking nature: 2PC khóa tài nguyên cho đến khi phase 2 hoàn thành. Device offline 30s → toàn bộ transaction bị block.
- Single Point of Failure: Coordinator bị sập → participants giữ khóa vô thời hạn.
- Asynchronous incompatibility: SQS, SNS, iOS callback là bất đồng bộ — 2PC cần synchronous dialogue.
- Lambda timeout: 2PC coordinator cần trạng thái dài hạn — không phù hợp Lambda (max 15 phút).
Garcia-Molina & Salem đã nhận thấy vấn đề này từ 1987 — Saga sinh ra để giải quyết chính xác vấn đề này [1].
2.5.10 Lợi Ích và Thách Thức
2.5.10.1 Lợi Ích
- Loose coupling: Mỗi service chỉ biết sự kiện của mình, không biết toàn bộ saga [3]
- Horizontal scalability: Scale độc lập per service, không chờ orchestrator
- Natural EDA fit: SNS/SQS là AWS-managed, không cần quản lý orchestrator thủ công
- Resilience: DLQ, retry, timeout built-in trong AWS services [4]
- Eventual consistency: Phù hợp MDM — không cần strong consistency tức thì
2.5.10.2 Thách Thức
- Distributed tracing: Debug flow phân tán khó hơn — cần AWS X-Ray với correlation ID [15]
- Compensating transaction design: Phải thiết kế bù trừ cho từng bước; không tự động như ACID
- Eventual consistency anomalies: Trong thời gian giữa các bước, dữ liệu có thể không nhất quán
- Testing complexity: Phải mô phỏng failure scenarios và async behavior
2.5.11 Tổng Hợp: Ánh Xạ Lý Thuyết — Thực Tiễn Fluxion
| Khái niệm lý thuyết | Nguồn | Biểu hiện trong Fluxion |
|---|---|---|
| Long Lived Transaction | Garcia-Molina & Salem [1] | MDM command pipeline (multi-step, async) |
| Compensating Transaction | Garcia-Molina & Salem [1] | UPDATE action_executions SET state=ACTION_FAILED |
| Choreography Saga | Richardson [3] | SQS/SNS event chain, không có orchestrator |
| Eventual Consistency | Richardson [3] | Device status cập nhật sau callback qua checkin-ecs |
| DLQ Retry Pattern | Hohpe & Woolf [2] | action-trigger-dlq + retry Lambda |
| Idempotency | Richardson [3] | WHERE state='ACTION_SENT' guard — no-op nếu đã FAILED |
| Circuit Breaker | Nygard [15] | Max retry count + ACTION_FAILED terminal state |
| FSM | — | action_executions states: ACTION_PENDING→ACTION_SENT→ACTION_COMPLETED/ACTION_FAILED |
Bảng 2.5.5: Ánh xạ lý thuyết — thực tiễn Saga trong Fluxion
Kết Luận
Choreography-based Saga Pattern giải quyết một trong những thách thức căn bản nhất của kiến trúc microservices: quản lý tính nhất quán dữ liệu xuyên dịch vụ mà không cần giao dịch phân tán (Distributed Transaction) hay điều phối trung tâm (Orchestrator). Nền tảng lý thuyết được đặt ra từ năm 1987 bởi Garcia-Molina & Salem với khái niệm Long Lived Transaction và Compensating Transaction vẫn giữ nguyên giá trị trong bối cảnh kiến trúc serverless hiện đại — minh chứng cho tính bền vững của giải pháp này.
Trong Fluxion, Choreography-based Saga là pattern phù hợp tự nhiên với pipeline lệnh MDM: mỗi bước (tạo command record, gửi APNS push, nhận callback từ thiết bị) là một giao dịch cục bộ độc lập, liên kết với nhau qua sự kiện SNS/SQS thay vì lời gọi đồng bộ. Khi thiết bị không phản hồi hoặc Lambda thất bại, compensating transaction (UPDATE action_executions SET state='ACTION_FAILED') đảm bảo hệ thống hội tụ về trạng thái nhất quán. Cơ chế DLQ (Dead Letter Queue) của AWS SQS đóng vai trò lưới an toàn cuối cùng, ngăn chặn mất mát sự kiện và cung cấp khả năng kiểm tra thủ công khi tất cả các lần thử lại đều thất bại.
Việc lựa chọn Choreography thay vì Orchestration phản ánh nguyên tắc kiến trúc cốt lõi của Fluxion: ưu tiên tách biệt hoàn toàn (loose coupling) và tránh tạo ra điểm thất bại đơn lẻ (Single Point of Failure). Mỗi Lambda service chỉ cần biết sự kiện mà nó tiêu thụ và sự kiện nó phát ra — không phụ thuộc vào sự tồn tại hay trạng thái của các service khác. Đây là nền tảng kỹ thuật cho phép Fluxion mở rộng quy mô theo chiều ngang (horizontal scaling) và duy trì khả năng hoạt động ngay cả khi một thành phần trong pipeline gặp sự cố.
Tài Liệu Tham Khảo
[1] Garcia-Molina, H. & Salem, K. (1987). Sagas. Proceedings of the ACM SIGMOD International Conference on Management of Data, pp. 249–259. https://dl.acm.org/doi/10.1145/38713.38742
[2] Hohpe, G. & Woolf, B. (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. https://www.enterpriseintegrationpatterns.com/
[3] Richardson, C. (2018). Microservices Patterns: With Examples in Java, Ch. 4 (Saga). Manning Publications. https://microservices.io/patterns/data/saga.html
[4] AWS. (2024). Saga Pattern — AWS Prescriptive Guidance: Cloud Design Patterns. https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/saga-choreography.html
[15] Nygard, M. (2018). Release It! 2nd Edition: Design and Deploy Production-Ready Software. The Pragmatic Programmers. https://pragprog.com/titles/mnee2/release-it-second-edition/
Câu hỏi còn mở:
- SNS ordering: SNS không đảm bảo thứ tự sự kiện — nếu
CommandFailedđến trướcCommandCreated(race condition hiếm), FSM xử lý thế nào? - Manual compensation: Khi compensating transaction thất bại (DB down), cần quy trình phục hồi thủ công nào?
- Monitoring: Cần thiết kế dashboard theo dõi saga state qua nhiều services.