Migrate
This commit is contained in:
751
microservices/docs/vi/architecture/data-consistency-patterns.md
Normal file
751
microservices/docs/vi/architecture/data-consistency-patterns.md
Normal file
@@ -0,0 +1,751 @@
|
||||
# Kiến trúc Patterns Đồng bộ Dữ liệu
|
||||
|
||||
> Các patterns để duy trì tính nhất quán dữ liệu trong kiến trúc microservices phân tán
|
||||
|
||||
## Sơ đồ Tổng quan
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Consistency Patterns"
|
||||
Saga[Saga Pattern<br/>Distributed Transactions]
|
||||
Outbox[Outbox Pattern<br/>Reliable Events]
|
||||
Idempotency[Idempotency<br/>Retry Safety]
|
||||
OptimisticLock[Optimistic Locking<br/>Concurrent Updates]
|
||||
CQRS[CQRS<br/>Read/Write Separation]
|
||||
end
|
||||
|
||||
Service1[Service A] --> Saga
|
||||
Service2[Service B] --> Outbox
|
||||
Service3[Service C] --> Idempotency
|
||||
|
||||
Saga --> EventualConsistency[Eventual Consistency]
|
||||
Outbox --> EventualConsistency
|
||||
Idempotency --> EventualConsistency
|
||||
OptimisticLock --> StrongConsistency[Strong Consistency]
|
||||
CQRS --> EventualConsistency
|
||||
|
||||
%% Dark color palette with white text
|
||||
style Saga fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style Outbox fill:#b45309,stroke:#f59e0b,color:#ffffff
|
||||
style Idempotency fill:#7e22ce,stroke:#a855f7,color:#ffffff
|
||||
style OptimisticLock fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style CQRS fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style EventualConsistency fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style StrongConsistency fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style Service1 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
style Service2 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
style Service3 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
```
|
||||
|
||||
## Mô tả Kiến trúc
|
||||
|
||||
### Tổng quan Kiến trúc
|
||||
|
||||
Nền tảng GoodGo sử dụng nhiều consistency patterns để xử lý dữ liệu phân tán:
|
||||
|
||||
**Thách thức Cốt lõi**:
|
||||
- Không có distributed transactions (2PC quá chậm)
|
||||
- Services sở hữu dữ liệu riêng (database per service)
|
||||
- Network failures có thể gây partial completion
|
||||
- Cần maintain data integrity giữa các services
|
||||
|
||||
**Lựa chọn Pattern**:
|
||||
- **Saga**: Cho workflows nhiều services
|
||||
- **Outbox**: Cho event publishing đảm bảo
|
||||
- **Idempotency**: Cho retries an toàn
|
||||
- **Optimistic Locking**: Cho concurrent updates
|
||||
- **CQRS**: Cho tối ưu read/write
|
||||
|
||||
## Bối cảnh Hệ thống
|
||||
|
||||
```mermaid
|
||||
C4Context
|
||||
title System Context for Data Consistency in GoodGo Platform
|
||||
|
||||
Person(user, "User", "End user performing actions")
|
||||
|
||||
System_Boundary(goodgo, "GoodGo Microservices") {
|
||||
System(order_service, "Order Service", "Manages orders with Saga")
|
||||
System(payment_service, "Payment Service", "Processes payments")
|
||||
System(inventory_service, "Inventory Service", "Manages stock")
|
||||
System(saga_orchestrator, "Saga Orchestrator", "Coordinates distributed transactions")
|
||||
System(outbox_processor, "Outbox Processor", "Publishes events reliably")
|
||||
}
|
||||
|
||||
System_Ext(db_order, "Order DB", "PostgreSQL with Outbox table")
|
||||
System_Ext(db_payment, "Payment DB", "PostgreSQL with version field")
|
||||
System_Ext(db_inventory, "Inventory DB", "PostgreSQL")
|
||||
System_Ext(kafka, "Event Bus", "Kafka - Event streaming")
|
||||
System_Ext(redis, "Cache", "Redis - Idempotency keys")
|
||||
|
||||
Rel(user, order_service, "Places order", "HTTPS")
|
||||
Rel(order_service, saga_orchestrator, "Starts saga", "Internal")
|
||||
Rel(saga_orchestrator, payment_service, "Process payment", "HTTP")
|
||||
Rel(saga_orchestrator, inventory_service, "Reserve stock", "HTTP")
|
||||
|
||||
Rel(order_service, db_order, "Writes + Outbox", "SQL")
|
||||
Rel(payment_service, db_payment, "Updates with version", "SQL")
|
||||
Rel(inventory_service, db_inventory, "Reads/Writes", "SQL")
|
||||
|
||||
Rel(outbox_processor, db_order, "Polls outbox", "SQL")
|
||||
Rel(outbox_processor, kafka, "Publishes events", "Kafka Protocol")
|
||||
Rel(order_service, redis, "Checks idempotency key", "Redis Protocol")
|
||||
|
||||
UpdateRelStyle(saga_orchestrator, payment_service, $lineColor="red", $textColor="red")
|
||||
UpdateRelStyle(saga_orchestrator, inventory_service, $lineColor="red", $textColor="red")
|
||||
```
|
||||
|
||||
Nền tảng GoodGo sử dụng kiến trúc database-per-service nơi mỗi service sở hữu dữ liệu riêng. Tính nhất quán dữ liệu giữa các services đạt được thông qua các patterns như Saga (cho workflows phối hợp), Outbox (cho event publishing đáng tin cậy), Idempotency (cho retries an toàn), và Optimistic Locking (cho concurrent updates). Các patterns này cho phép eventual consistency đồng thời duy trì data integrity.
|
||||
|
||||
## Pattern Saga
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Orchestrator
|
||||
participant OrderService
|
||||
participant PaymentService
|
||||
participant InventoryService
|
||||
|
||||
Orchestrator->>OrderService: 1. Create Order
|
||||
OrderService-->>Orchestrator: Order Created
|
||||
|
||||
Orchestrator->>PaymentService: 2. Process Payment
|
||||
PaymentService-->>Orchestrator: Payment Success
|
||||
|
||||
Orchestrator->>InventoryService: 3. Reserve Inventory
|
||||
|
||||
alt Inventory Reserved
|
||||
InventoryService-->>Orchestrator: Success
|
||||
Orchestrator->>Orchestrator: Complete Saga ✓
|
||||
else Inventory Failed
|
||||
InventoryService-->>Orchestrator: Failed ✗
|
||||
Orchestrator->>PaymentService: Compensate: Refund
|
||||
PaymentService-->>Orchestrator: Refunded
|
||||
Orchestrator->>OrderService: Compensate: Cancel Order
|
||||
OrderService-->>Orchestrator: Cancelled
|
||||
end
|
||||
```
|
||||
|
||||
**Mô tả**: Saga quản lý distributed transactions dưới dạng chuỗi local transactions với compensation.
|
||||
|
||||
**Triển khai**:
|
||||
```typescript
|
||||
// Saga orchestrator
|
||||
class OrderSaga {
|
||||
async execute(orderData: OrderData): Promise<void> {
|
||||
const sagaContext = {
|
||||
orderId: null,
|
||||
paymentId: null,
|
||||
inventoryId: null
|
||||
};
|
||||
|
||||
try {
|
||||
// Bước 1: Tạo đơn hàng
|
||||
sagaContext.orderId = await orderService.create(orderData);
|
||||
|
||||
// Bước 2: Xử lý thanh toán
|
||||
sagaContext.paymentId = await paymentService.process(orderData.payment);
|
||||
|
||||
// Bước 3: Đặt trước kho
|
||||
sagaContext.inventoryId = await inventoryService.reserve(orderData.items);
|
||||
|
||||
// Tất cả thành công - commit
|
||||
await this.completeSaga(sagaContext);
|
||||
} catch (error) {
|
||||
// Compensate theo thứ tự ngược lại
|
||||
await this.compensate(sagaContext, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async compensate(context: SagaContext, error: Error): Promise<void> {
|
||||
if (context.inventoryId) {
|
||||
await inventoryService.release(context.inventoryId);
|
||||
}
|
||||
if (context.paymentId) {
|
||||
await paymentService.refund(context.paymentId);
|
||||
}
|
||||
if (context.orderId) {
|
||||
await orderService.cancel(context.orderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pattern Outbox
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Service
|
||||
participant DB as Database
|
||||
participant OutboxTable as Outbox Table
|
||||
participant Processor as Outbox Processor
|
||||
participant Kafka
|
||||
|
||||
Service->>DB: Begin Transaction
|
||||
Service->>DB: Update Business Data
|
||||
Service->>OutboxTable: Insert Event
|
||||
Service->>DB: Commit Transaction
|
||||
|
||||
loop Every 5 seconds
|
||||
Processor->>OutboxTable: SELECT unpublished events
|
||||
OutboxTable-->>Processor: Events
|
||||
Processor->>Kafka: Publish Events
|
||||
Kafka-->>Processor: Ack
|
||||
Processor->>OutboxTable: Mark as published
|
||||
end
|
||||
```
|
||||
|
||||
**Mô tả**: Đảm bảo event publishing bằng cách lưu events trong database cùng transaction với business data.
|
||||
|
||||
**Triển khai (.NET với EF Core)**:
|
||||
```csharp
|
||||
// EN: Save event in outbox with business data
|
||||
// VI: Lưu event trong outbox cùng với business data
|
||||
public async Task<User> CreateUserAsync(CreateUserDto dto, CancellationToken ct)
|
||||
{
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync(ct);
|
||||
|
||||
try
|
||||
{
|
||||
// Business operation
|
||||
var user = new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Email = dto.Email,
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName
|
||||
};
|
||||
_context.Users.Add(user);
|
||||
|
||||
// Lưu event trong outbox (cùng transaction)
|
||||
var outboxEvent = new OutboxMessage
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
AggregateId = user.Id.ToString(),
|
||||
AggregateType = nameof(User),
|
||||
EventType = "user.created.v1",
|
||||
Payload = JsonSerializer.Serialize(user),
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
_context.OutboxMessages.Add(outboxEvent);
|
||||
|
||||
await _context.SaveChangesAsync(ct);
|
||||
await transaction.CommitAsync(ct);
|
||||
|
||||
return user;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pattern Idempotency
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Request1[Request with<br/>Idempotency Key]
|
||||
Request2[Retry with<br/>Same Key]
|
||||
|
||||
Request1 --> Check{Key Exists?}
|
||||
Check -->|No| Process[Process Request]
|
||||
Check -->|Yes| Return[Return Cached Result]
|
||||
|
||||
Process --> Store[Store Result<br/>with Key]
|
||||
Store --> Response1[Response]
|
||||
|
||||
Request2 --> Check
|
||||
Return --> Response2[Same Response]
|
||||
|
||||
%% Dark color palette with white text
|
||||
style Request1 fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style Request2 fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style Check fill:#b45309,stroke:#f59e0b,color:#ffffff
|
||||
style Process fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style Store fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style Return fill:#7e22ce,stroke:#a855f7,color:#ffffff
|
||||
style Response1 fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style Response2 fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
```
|
||||
|
||||
**Mô tả**: Đảm bảo operations có thể retry an toàn mà không có side effects bằng cách sử dụng idempotency keys.
|
||||
|
||||
**Triển khai**:
|
||||
```typescript
|
||||
// Idempotency middleware
|
||||
async function idempotentOperation<T>(
|
||||
key: string,
|
||||
operation: () => Promise<T>,
|
||||
ttl: number = 86400
|
||||
): Promise<T> {
|
||||
// Kiểm tra đã xử lý chưa
|
||||
const cached = await redis.get(`idempotency:${key}`);
|
||||
if (cached) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
|
||||
// Xử lý operation
|
||||
const result = await operation();
|
||||
|
||||
// Lưu kết quả
|
||||
await redis.setex(`idempotency:${key}`, ttl, JSON.stringify(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Sử dụng trong controller
|
||||
async createPayment(req: Request, res: Response): Promise<void> {
|
||||
const idempotencyKey = req.headers['idempotency-key'] as string;
|
||||
|
||||
if (!idempotencyKey) {
|
||||
return res.status(400).json({ error: 'Idempotency-Key header required' });
|
||||
}
|
||||
|
||||
const result = await idempotentOperation(
|
||||
idempotencyKey,
|
||||
() => paymentService.process(req.body)
|
||||
);
|
||||
|
||||
res.json({ success: true, data: result });
|
||||
}
|
||||
```
|
||||
|
||||
## Khóa Lạc quan (Optimistic Locking)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User1
|
||||
participant User2
|
||||
participant Service
|
||||
participant DB
|
||||
|
||||
User1->>Service: Read (version=1)
|
||||
User2->>Service: Read (version=1)
|
||||
|
||||
User1->>Service: Update (version=1)
|
||||
Service->>DB: UPDATE WHERE version=1
|
||||
DB-->>Service: Success, version→2
|
||||
Service-->>User1: Success
|
||||
|
||||
User2->>Service: Update (version=1)
|
||||
Service->>DB: UPDATE WHERE version=1
|
||||
DB-->>Service: No rows updated
|
||||
Service-->>User2: Conflict - version mismatch
|
||||
User2->>Service: Read (version=2)
|
||||
User2->>Service: Update (version=2)
|
||||
Service-->>User2: Success
|
||||
```
|
||||
|
||||
**Mô tả**: Ngăn chặn lost updates bằng cách kiểm tra version khi update.
|
||||
|
||||
**Triển khai (.NET với EF Core)**:
|
||||
```csharp
|
||||
// EN: Entity with concurrency token
|
||||
// VI: Entity với concurrency token
|
||||
public class User
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
[ConcurrencyCheck]
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
// Or use RowVersion for SQL Server
|
||||
// [Timestamp]
|
||||
// public byte[] RowVersion { get; set; }
|
||||
}
|
||||
|
||||
// EN: Update with optimistic locking
|
||||
// VI: Update với optimistic locking
|
||||
public async Task<User> UpdateUserAsync(
|
||||
Guid userId,
|
||||
UpdateUserDto dto,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var user = await _context.Users.FindAsync([userId], ct)
|
||||
?? throw new UserNotFoundException(userId);
|
||||
|
||||
user.Name = dto.Name;
|
||||
user.Version++; // Increment version
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync(ct);
|
||||
return user;
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
throw new ConcurrencyConflictException(
|
||||
"Data was modified by another user. Please refresh and try again.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## CQRS Pattern
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Write Side"
|
||||
Command[Command] --> WriteModel[Write Model<br/>Normalized]
|
||||
WriteModel --> Events[Domain Events]
|
||||
end
|
||||
|
||||
subgraph "Read Side"
|
||||
Events --> Projection[Event Projection]
|
||||
Projection --> ReadModel[Read Model<br/>Denormalized]
|
||||
Query[Query] --> ReadModel
|
||||
end
|
||||
|
||||
WriteModel --> DB1[(Write DB)]
|
||||
ReadModel --> DB2[(Read DB<br/>Optimized)]
|
||||
|
||||
%% Dark color palette with white text
|
||||
style Command fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style WriteModel fill:#7e22ce,stroke:#a855f7,color:#ffffff
|
||||
style Events fill:#b45309,stroke:#f59e0b,color:#ffffff
|
||||
style Projection fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style ReadModel fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style Query fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style DB1 fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style DB2 fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
```
|
||||
|
||||
**Mô tả**: Tách biệt read và write models để tối ưu hiệu suất.
|
||||
|
||||
## Đặc điểm Hiệu suất
|
||||
|
||||
Chỉ số hiệu suất và chiến lược tối ưu cho patterns đồng bộ dữ liệu.
|
||||
|
||||
| Pattern | Tác động Độ trễ | Thông lượng | Ghi chú |
|
||||
|---------|-----------------|-------------|---------|
|
||||
| **Thực thi Saga** | 500ms - 2s | 100-500 sagas/s | Phụ thuộc số bước và compensation |
|
||||
| **Xử lý Outbox** | < 100ms | 10,000 events/s | Xử lý bất đồng bộ, tác động tối thiểu |
|
||||
| **Kiểm tra Idempotency** | < 10ms | 50,000 checks/s | Redis lookup, rất nhanh |
|
||||
| **Cập nhật Optimistic Lock** | < 50ms | 5,000 updates/s | Single DB operation với version check |
|
||||
| **CQRS Projection** | 100ms - 1s | 1,000 events/s | Xử lý event sang read model |
|
||||
| **Thực thi Compensation** | 200ms - 1s | Varies | Rollback operations trong saga |
|
||||
|
||||
### Chiến lược Tối ưu Hiệu suất
|
||||
|
||||
**Saga Pattern**:
|
||||
- Giảm thiểu số bước (< 5 bước lý tưởng)
|
||||
- Thực thi song song khi có thể
|
||||
- Cache service responses
|
||||
- Đặt timeouts phù hợp (30s mặc định)
|
||||
|
||||
**Outbox Pattern**:
|
||||
- Batch process outbox events (100-500 mỗi batch)
|
||||
- Index cột `publishedAt` cho hiệu suất
|
||||
- Archive processed events định kỳ
|
||||
- Sử dụng connection pooling cho Kafka
|
||||
|
||||
**Idempotency**:
|
||||
- Sử dụng Redis cho fast key lookups
|
||||
- Đặt TTL 24-48 giờ
|
||||
- Hash long idempotency keys
|
||||
- Clean expired keys thường xuyên
|
||||
|
||||
**Optimistic Locking**:
|
||||
- Hoạt động tốt nhất cho low-contention scenarios
|
||||
- Triển khai retry với exponential backoff
|
||||
- Giám sát conflict rates (nên < 5%)
|
||||
- Cân nhắc pessimistic locking nếu conflicts > 10%
|
||||
|
||||
## Cân nhắc Bảo mật
|
||||
|
||||
Biện pháp bảo mật để bảo vệ các operations đồng bộ dữ liệu.
|
||||
|
||||
### Bảo mật Saga
|
||||
|
||||
**Bảo vệ Compensation**:
|
||||
- Xác thực saga execution permissions ở mỗi bước
|
||||
- Mã hóa sensitive data trong saga context
|
||||
- Log tất cả saga executions cho audit
|
||||
- Triển khai timeout để ngăn hanging sagas
|
||||
|
||||
```typescript
|
||||
// Saga context bảo mật
|
||||
interface SecureSagaContext {
|
||||
sagaId: string;
|
||||
userId: string;
|
||||
permissions: string[];
|
||||
encryptedData: string;
|
||||
auditLog: AuditEntry[];
|
||||
}
|
||||
```
|
||||
|
||||
### Bảo mật Outbox
|
||||
|
||||
**Mã hóa Event Payload**:
|
||||
- Mã hóa PII (Personally Identifiable Information) trước khi lưu trong outbox
|
||||
- Sử dụng AES-256-GCM cho event payload encryption
|
||||
- Giải mã chỉ khi publishing sang Kafka
|
||||
- Rotate encryption keys hàng quý
|
||||
|
||||
**Kiểm soát Truy cập**:
|
||||
- Hạn chế truy cập outbox table chỉ cho outbox processor
|
||||
- Sử dụng database roles và permissions
|
||||
- Giám sát outbox table access patterns
|
||||
|
||||
### Bảo mật Idempotency
|
||||
|
||||
**Bảo mật Key**:
|
||||
- Sử dụng cryptographic hashing cho idempotency keys (SHA-256)
|
||||
- Bao gồm user context trong key generation
|
||||
- Xác thực key ownership trước khi xử lý
|
||||
- Clear keys khi user logout cho sensitive operations
|
||||
|
||||
```typescript
|
||||
// Tạo idempotency key bảo mật
|
||||
function generateIdempotencyKey(
|
||||
operation: string,
|
||||
userId: string,
|
||||
data: any
|
||||
): string {
|
||||
const payload = JSON.stringify({ operation, userId, data });
|
||||
return crypto.createHash('sha256').update(payload).digest('hex');
|
||||
}
|
||||
```
|
||||
|
||||
### Bảo mật Optimistic Lock
|
||||
|
||||
**Ngăn chặn Giả mạo Version**:
|
||||
- Xác thực version field chỉ ở server-side
|
||||
- Không bao giờ chấp nhận version từ client trực tiếp
|
||||
- Log version conflicts cho security monitoring
|
||||
- Rate limit update attempts per user
|
||||
|
||||
## Triển khai
|
||||
|
||||
Cách các patterns đồng bộ dữ liệu được triển khai và mở rộng.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Production Deployment"
|
||||
subgraph "Order Service Cluster"
|
||||
OS1[Order Service\nPod 1]
|
||||
OS2[Order Service\nPod 2]
|
||||
OS3[Order Service\nPod 3]
|
||||
end
|
||||
|
||||
subgraph "Saga Orchestrator"
|
||||
SO1[Saga Orchestrator\nPod 1]
|
||||
SO2[Saga Orchestrator\nPod 2]
|
||||
end
|
||||
|
||||
subgraph "Outbox Processor"
|
||||
OP1[Outbox Processor\nPod 1]
|
||||
OP2[Outbox Processor\nPod 2]
|
||||
end
|
||||
|
||||
OS1 & OS2 & OS3 --> DB[(Order DB\nwith Outbox)]
|
||||
OS1 & OS2 & OS3 --> Redis[(Redis\nIdempotency Keys)]
|
||||
|
||||
SO1 & SO2 --> PS[Payment Service]
|
||||
SO1 & SO2 --> IS[Inventory Service]
|
||||
|
||||
OP1 & OP2 --> DB
|
||||
OP1 & OP2 --> Kafka[Kafka Cluster\n5 brokers]
|
||||
end
|
||||
|
||||
%% Dark color palette with white text
|
||||
style OS1 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
style OS2 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
style OS3 fill:#4527a0,stroke:#7c4dff,color:#ffffff
|
||||
style SO1 fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style SO2 fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
style OP1 fill:#b45309,stroke:#f59e0b,color:#ffffff
|
||||
style OP2 fill:#b45309,stroke:#f59e0b,color:#ffffff
|
||||
style DB fill:#15803d,stroke:#22c55e,color:#ffffff
|
||||
style Redis fill:#7e22ce,stroke:#a855f7,color:#ffffff
|
||||
style Kafka fill:#b91c1c,stroke:#ef4444,color:#ffffff
|
||||
style PS fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
style IS fill:#374151,stroke:#6b7280,color:#ffffff
|
||||
```
|
||||
|
||||
### Cấu hình Triển khai
|
||||
|
||||
| Thành phần | Replicas | Resources | HA Strategy |
|
||||
|------------|----------|-----------|-------------|
|
||||
| **Saga Orchestrator** | 2-3 | 512Mi RAM, 500m CPU | Leader election với etcd |
|
||||
| **Outbox Processor** | 2-5 | 256Mi RAM, 250m CPU | Distributed lock per event batch |
|
||||
| **Services với Outbox** | 3+ | Varies | Standard service scaling |
|
||||
| **Redis (Idempotency)** | 3 nodes | 1Gi RAM each | Redis Cluster với replication |
|
||||
|
||||
### Chiến lược Mở rộng
|
||||
|
||||
**Saga Orchestrator**:
|
||||
- Scale dựa trên pending saga count
|
||||
- Sử dụng queue-based load distribution
|
||||
- Giám sát saga execution duration
|
||||
|
||||
**Outbox Processor**:
|
||||
- Scale với database sharding (1 processor per shard)
|
||||
- Tăng batch size trước khi thêm replicas
|
||||
- Giám sát outbox table size và age
|
||||
|
||||
**Idempotency Store (Redis)**:
|
||||
- Scale Redis cluster horizontally
|
||||
- Sử dụng consistent hashing cho key distribution
|
||||
- Giám sát memory usage (nên < 70%)
|
||||
|
||||
## Giám sát & Khả năng quan sát
|
||||
|
||||
Chiến lược giám sát cho patterns đồng bộ dữ liệu.
|
||||
|
||||
### Chỉ số Chính
|
||||
|
||||
**Saga Metrics**:
|
||||
- `saga_executions_total` - Tổng saga executions (success/failure)
|
||||
- `saga_duration_seconds` - Saga execution time histogram
|
||||
- `saga_compensations_total` - Tổng compensation executions
|
||||
- `saga_timeout_total` - Sagas timeout
|
||||
- `saga_pending_count` - Sagas đang thực thi
|
||||
|
||||
**Outbox Metrics**:
|
||||
- `outbox_events_total` - Events ghi vào outbox
|
||||
- `outbox_published_total` - Events published sang Kafka
|
||||
- `outbox_processing_lag_seconds` - Thời gian từ write đến publish
|
||||
- `outbox_table_size` - Số dòng outbox table
|
||||
- `outbox_failed_events_total` - Failed event publications
|
||||
|
||||
**Idempotency Metrics**:
|
||||
- `idempotency_checks_total` - Tổng idempotency checks
|
||||
- `idempotency_hits_total` - Duplicate requests prevented
|
||||
- `idempotency_key_ttl_seconds` - Average key TTL
|
||||
- `idempotency_redis_errors_total` - Redis failures
|
||||
|
||||
**Optimistic Lock Metrics**:
|
||||
- `optimistic_lock_conflicts_total` - Version conflicts detected
|
||||
- `optimistic_lock_retries_total` - Retry attempts sau conflict
|
||||
- `optimistic_lock_success_rate` - Update success percentage
|
||||
|
||||
### Cảnh báo
|
||||
|
||||
**Critical Alerts**:
|
||||
```yaml
|
||||
# Saga timeout rate quá cao
|
||||
alert: HighSagaTimeoutRate
|
||||
expr: rate(saga_timeout_total[5m]) > 0.05
|
||||
for: 5m
|
||||
severity: critical
|
||||
|
||||
# Outbox processing lag
|
||||
alert: OutboxProcessingLag
|
||||
expr: outbox_processing_lag_seconds > 300
|
||||
for: 10m
|
||||
severity: critical
|
||||
|
||||
# High optimistic lock conflict rate
|
||||
alert: HighOptimisticLockConflicts
|
||||
expr: rate(optimistic_lock_conflicts_total[5m]) / rate(optimistic_lock_attempts_total[5m]) > 0.1
|
||||
for: 5m
|
||||
severity: warning
|
||||
```
|
||||
|
||||
### Dashboard Giám sát
|
||||
|
||||
**Grafana Panels**:
|
||||
|
||||
1. **Tổng quan Saga Orchestration**:
|
||||
- Saga execution rate (success/failure)
|
||||
- Average saga duration
|
||||
- Compensation rate
|
||||
- Pending saga count
|
||||
|
||||
2. **Sức khỏe Outbox Processing**:
|
||||
- Outbox publishing rate
|
||||
- Processing lag (P95, P99)
|
||||
- Failed events
|
||||
- Table size trend
|
||||
|
||||
3. **Hiệu quả Idempotency**:
|
||||
- Duplicate prevention rate
|
||||
- Redis hit rate
|
||||
- Key distribution
|
||||
|
||||
4. **Data Consistency SLA**:
|
||||
- Overall consistency rate (target: 99.9%)
|
||||
- Mean time to consistency (MTTC)
|
||||
- Conflict resolution success rate
|
||||
|
||||
### Tracing Phân tán
|
||||
|
||||
**Trace Saga Execution**:
|
||||
```typescript
|
||||
// Saga step được trace
|
||||
async function executeStepWithTracing(
|
||||
step: SagaStep,
|
||||
context: SagaContext
|
||||
): Promise<void> {
|
||||
const tracer = trace.getTracer('saga-orchestrator');
|
||||
const span = tracer.startSpan(`saga.step.${step.name}`, {
|
||||
attributes: {
|
||||
'saga.id': context.sagaId,
|
||||
'saga.step': step.name,
|
||||
'saga.attempt': context.currentAttempt
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await step.execute(context);
|
||||
span.setStatus({ code: SpanStatusCode.OK });
|
||||
} catch (error) {
|
||||
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
||||
span.recordException(error);
|
||||
throw error;
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tài liệu Liên quan
|
||||
|
||||
- [Event-Driven Architecture](./event-driven-architecture.md) - Event sourcing và Kafka
|
||||
- [System Design](./system-design.md) - Kiến trúc tổng thể
|
||||
- [Microservices Communication](./microservices-communication.md) - Patterns giao tiếp service
|
||||
- [Resilience Patterns](../skills/resilience-patterns.md) - Circuit breaker, retry cho saga steps
|
||||
- [Caching Patterns](../skills/caching-patterns.md) - Caching cho idempotency keys
|
||||
- [Database Prisma](../skills/database-prisma.md) - Prisma transactions cho outbox pattern
|
||||
|
||||
---
|
||||
|
||||
**Cập nhật Lần cuối**: 2026-01-14
|
||||
**Tác giả**: GoodGo Architecture Team
|
||||
|
||||
## Quick Tips
|
||||
|
||||
### Mermaid Common Issues
|
||||
- ⚠️ **Syntax Error**: Kiểm tra dấu `(` `)` `[` `]` `{` `}`
|
||||
- ⚠️ **Render Error**: Kiểm tra `graph` vs `flowchart`, sử dụng `graph` cho compatibility
|
||||
- ⚠️ **Arrow Direction**: Sử dụng `-->` (solid) hoặc `-.->` (dashed)
|
||||
- ✅ **Color**: Luôn sử dụng dark palette với white text
|
||||
|
||||
### Color Palette Reference
|
||||
|
||||
| Color | Fill | Stroke | Use Case |
|
||||
|-------|------|--------|----------|
|
||||
| **Blue** | `#1d4ed8` | `#3b82f6` | Primary Components, Saga |
|
||||
| **Green** | `#15803d` | `#22c55e` | Success, DB, Stable States |
|
||||
| **Purple** | `#7e22ce` | `#a855f7` | Feature, Logic, Idempotency |
|
||||
| **Orange** | `#b45309` | `#f59e0b` | Warning, External, Outbox |
|
||||
| **Red** | `#b91c1c` | `#ef4444` | Error, Failure, Critical |
|
||||
| **Gray** | `#374151` | `#6b7280` | Background, Secondary |
|
||||
|
||||
**Pattern áp dụng**:
|
||||
```
|
||||
style NodeName fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
|
||||
```
|
||||
|
||||
### Visual Indicators
|
||||
|
||||
- ✅ **Recommended**: Best practices, khuyến nghị sử dụng
|
||||
- ⚠️ **Warning**: Cần chú ý, có điều kiện
|
||||
- ❌ **Avoid**: Anti-patterns, tránh sử dụng
|
||||
- 🔒 **Security**: Liên quan đến bảo mật
|
||||
- ⚡ **Performance**: Liên quan đến hiệu suất
|
||||
Reference in New Issue
Block a user