- Updated the architecture documentation to enhance clarity with detailed diagrams and descriptions for the GoodGo Microservices Platform. - Revised the .NET and Node.js template documentation to reflect new naming conventions, project structures, and setup instructions for local development. - Improved the guide documentation with verification checklists, troubleshooting steps, and real-world examples to assist developers in deploying and managing services effectively. - Ensured bilingual support in documentation to enhance accessibility for a wider audience.
637 lines
19 KiB
Markdown
637 lines
19 KiB
Markdown
# Kiến trúc Caching
|
|
|
|
> Chiến lược caching nhiều tầng để tối ưu hiệu suất
|
|
|
|
## Sơ đồ Tổng quan
|
|
|
|
```mermaid
|
|
graph TD
|
|
Request[API Request] --> L1{L1 Cache<br/>Memory}
|
|
|
|
L1 -->|Hit| Return1[Return<br/>< 1ms]
|
|
L1 -->|Miss| L2{L2 Cache<br/>Redis}
|
|
|
|
L2 -->|Hit| WarmL1[Warm L1]
|
|
WarmL1 --> Return2[Return<br/>< 5ms]
|
|
|
|
L2 -->|Miss| DB[(Database)]
|
|
DB --> StoreL2[Store L2 + L1]
|
|
StoreL2 --> Return3[Return<br/>< 50ms]
|
|
|
|
classDef memory fill:#1b5e20,stroke:#2e7d32,color:#fff
|
|
classDef redis fill:#e65100,stroke:#ef6c00,color:#fff
|
|
classDef db fill:#212121,stroke:#424242,color:#fff
|
|
classDef default fill:#202020,stroke:#505050,color:#fff
|
|
|
|
class L1,Return1,WarmL1 memory
|
|
class L2,Return2,StoreL2 redis
|
|
class DB,Return3 db
|
|
```
|
|
|
|
## Bối cảnh Hệ thống
|
|
|
|
```mermaid
|
|
C4Context
|
|
title Sơ đồ Bối cảnh Hệ thống Caching
|
|
|
|
System(service, "Microservice", "Client service using cache")
|
|
System_Ext(db, "Neon PostgreSQL", "Primary database")
|
|
|
|
Boundary(caching, "Caching Layer") {
|
|
System(l1, "L1 Cache", "In-memory NodeCache")
|
|
System(l2, "L2 Cache", "Redis Cluster")
|
|
}
|
|
|
|
Rel(service, l1, "Reads/Writes", "In-process")
|
|
Rel(service, l2, "Reads/Writes", "Redis Protocol")
|
|
Rel(l1, l2, "Fills from", "On miss")
|
|
Rel(l2, db, "Cache aside", "On miss")
|
|
|
|
UpdateElementStyle(service, $fontColor="white", $bgColor="#1a237e", $borderColor="#3949ab")
|
|
UpdateElementStyle(db, $fontColor="white", $bgColor="#212121", $borderColor="#424242")
|
|
UpdateElementStyle(l1, $fontColor="white", $bgColor="#1b5e20", $borderColor="#2e7d32")
|
|
UpdateElementStyle(l2, $fontColor="white", $bgColor="#e65100", $borderColor="#ef6c00")
|
|
```
|
|
|
|
### Mô tả Bối cảnh
|
|
- **Service**: Giao tiếp trực tiếp với L1 Cache (in-memory) để đạt độ trễ thấp nhất.
|
|
- **L1 Cache**: Cache cục bộ, không chia sẻ, tự động hết hạn (TTL ngắn).
|
|
- **L2 Cache**: Redis cluster chia sẻ, giữ dữ liệu lâu dài hơn và đồng bộ giữa các instances.
|
|
- **Database**: Nguồn dữ liệu gốc (source of truth), chỉ được truy cập khi cache miss.
|
|
|
|
## Mô tả Kiến trúc
|
|
|
|
### Caching Nhiều Tầng
|
|
|
|
Nền tảng GoodGo sử dụng caching 2 tầng để tối ưu hiệu suất:
|
|
|
|
**L1 Cache (Memory)**:
|
|
- In-memory cache trên mỗi service instance
|
|
- Truy cập rất nhanh (< 1ms)
|
|
- Dung lượng giới hạn (10k keys mặc định)
|
|
- TTL ngắn (60 giây mặc định, tối đa 5 phút)
|
|
- Không share giữa instances
|
|
|
|
**L2 Cache (Redis)**:
|
|
- Shared distributed cache
|
|
- Truy cập nhanh (< 5ms)
|
|
- Dung lượng lớn
|
|
- TTL dài hơn (configurable, thường 5-15 phút)
|
|
- Share giữa tất cả service instances
|
|
|
|
**Cache Flow**:
|
|
```
|
|
Request → L1 → L2 → Database
|
|
↓ ↓ ↓ ↓
|
|
40-50% 80-90% 10-20% Cache miss
|
|
hit rate hit rate rate
|
|
```
|
|
|
|
## Triển khai Cache
|
|
|
|
### Multi-Layer Cache Service (.NET)
|
|
|
|
```csharp
|
|
// EN: Multi-layer cache implementation
|
|
// VI: Triển khai cache đa lớp
|
|
public class MultiLayerCacheService : ICacheService
|
|
{
|
|
private readonly IMemoryCache _l1Cache;
|
|
private readonly IConnectionMultiplexer _redis;
|
|
private readonly IDatabase _l2Cache;
|
|
private readonly ILogger<MultiLayerCacheService> _logger;
|
|
|
|
public MultiLayerCacheService(
|
|
IMemoryCache l1Cache,
|
|
IConnectionMultiplexer redis,
|
|
ILogger<MultiLayerCacheService> logger)
|
|
{
|
|
_l1Cache = l1Cache;
|
|
_redis = redis;
|
|
_l2Cache = redis.GetDatabase();
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<T?> GetAsync<T>(string key, CancellationToken ct = default)
|
|
{
|
|
// L1: Memory cache check
|
|
if (_l1Cache.TryGetValue(key, out T? l1Value))
|
|
{
|
|
_logger.LogDebug("L1 cache hit for key: {Key}", key);
|
|
return l1Value;
|
|
}
|
|
|
|
// L2: Redis cache check
|
|
var l2Value = await _l2Cache.StringGetAsync(key);
|
|
if (!l2Value.IsNullOrEmpty)
|
|
{
|
|
_logger.LogDebug("L2 cache hit for key: {Key}", key);
|
|
var parsed = JsonSerializer.Deserialize<T>(l2Value!);
|
|
|
|
// Warm L1 cache
|
|
_l1Cache.Set(key, parsed, TimeSpan.FromMinutes(1));
|
|
return parsed;
|
|
}
|
|
|
|
_logger.LogDebug("Cache miss for key: {Key}", key);
|
|
return default;
|
|
}
|
|
|
|
public async Task SetAsync<T>(string key, T value, TimeSpan? ttl = null, CancellationToken ct = default)
|
|
{
|
|
var expiry = ttl ?? TimeSpan.FromMinutes(5);
|
|
var l1Expiry = TimeSpan.FromMinutes(Math.Min(expiry.TotalMinutes, 5));
|
|
|
|
// L1: Memory cache (max 5 min)
|
|
_l1Cache.Set(key, value, l1Expiry);
|
|
|
|
// L2: Redis cache
|
|
var json = JsonSerializer.Serialize(value);
|
|
await _l2Cache.StringSetAsync(key, json, expiry);
|
|
}
|
|
|
|
public async Task RemoveAsync(string key, CancellationToken ct = default)
|
|
{
|
|
_l1Cache.Remove(key);
|
|
await _l2Cache.KeyDeleteAsync(key);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Quy ước Đặt tên Key
|
|
|
|
**Pattern**: `{service}:{entity}:{identifier}:{sub-resource}`
|
|
|
|
**Ví dụ (C#)**:
|
|
```csharp
|
|
// Cache key constants
|
|
public static class CacheKeys
|
|
{
|
|
public static string User(string userId) => $"iam:user:{userId}";
|
|
public static string UserPermissions(string userId) => $"iam:user:{userId}:permissions";
|
|
public static string UserRoles(string userId) => $"iam:user:{userId}:roles";
|
|
public static string Session(string sessionId) => $"iam:session:{sessionId}";
|
|
public static string UserQuota(string userId) => $"storage:quota:{userId}";
|
|
}
|
|
|
|
// Sử dụng
|
|
var user = await _cache.GetAsync<UserDto>(CacheKeys.User("user_123"));
|
|
var permissions = await _cache.GetAsync<List<string>>(CacheKeys.UserPermissions("user_123"));
|
|
```
|
|
|
|
## Chiến lược TTL
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph "TTL Tiers"
|
|
Short[Short TTL<br/>60-300s<br/>Frequently changing]
|
|
Medium[Medium TTL<br/>300-1800s<br/>Moderately changing]
|
|
Long[Long TTL<br/>1800-3600s<br/>Rarely changing]
|
|
end
|
|
|
|
Short --> Permissions[User Permissions]
|
|
Short --> Sessions[Session Data]
|
|
|
|
Medium --> UserProfiles[User Profiles]
|
|
Medium --> OrgData[Organization Data]
|
|
|
|
Long --> Config[Static Config]
|
|
Long --> RefData[Reference Data]
|
|
|
|
classDef tier fill:#202020,stroke:#505050,color:#fff
|
|
classDef short fill:#b71c1c,stroke:#f44336,color:#fff
|
|
classDef medium fill:#e65100,stroke:#ef6c00,color:#fff
|
|
classDef long fill:#1b5e20,stroke:#2e7d32,color:#fff
|
|
|
|
class Short short
|
|
class Medium medium
|
|
class Long long
|
|
class Permissions,Sessions,UserProfiles,OrgData,Config,RefData tier
|
|
```
|
|
|
|
**Hướng dẫn TTL**:
|
|
| Loại Dữ liệu | TTL | Lý do |
|
|
|---------------------------|-----|----------------|
|
|
| User permissions | 5 min | Nhạy cảm bảo mật |
|
|
| Session data | Varies | Dựa trên độ dài session |
|
|
| User profiles | 10 min | Tần suất cập nhật vừa phải |
|
|
| Organization data | 15 min | Cập nhật không thường xuyên |
|
|
| Static config | 30-60 min | Rất ổn định |
|
|
| Reference data | 1-2 hours | Hầu như không thay đổi |
|
|
|
|
## Vô hiệu hóa Cache
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant API
|
|
participant Service
|
|
participant Cache
|
|
participant DB
|
|
|
|
API->>Service: Update User
|
|
Service->>DB: UPDATE user
|
|
DB-->>Service: Success
|
|
|
|
Service->>Cache: Invalidate user:123
|
|
Service->>Cache: Invalidate user:123:permissions
|
|
Service->>Cache: Invalidate user:123:roles
|
|
Cache-->>Service: Cleared
|
|
|
|
Service-->>API: Success
|
|
|
|
Note over Service,Cache: Next request will fetch fresh data
|
|
```
|
|
|
|
**Chiến lược Invalidation**:
|
|
|
|
```typescript
|
|
// 1. Invalidation single key
|
|
async updateUser(userId: string, data: UpdateUserDto): Promise<User> {
|
|
const user = await userRepository.update(userId, data);
|
|
|
|
// Vô hiệu hóa user cache
|
|
await cache.del(cacheKeys.user(userId));
|
|
|
|
return user;
|
|
}
|
|
|
|
// 2. Invalidation theo pattern
|
|
async updateUserRole(userId: string, roleId: string): Promise<void> {
|
|
await userRoleRepository.assign(userId, roleId);
|
|
|
|
// Vô hiệu hóa tất cả cache liên quan đến user
|
|
await cache.invalidatePattern(`iam:user:${userId}:*`);
|
|
}
|
|
|
|
// 3. Invalidation theo thời gian (TTL expiry)
|
|
// Tự động xử lý bởi cache
|
|
```
|
|
|
|
## Làm ấm Cache
|
|
|
|
```typescript
|
|
// Preload dữ liệu thường xuyên truy cập
|
|
async warmCache(): Promise<void> {
|
|
logger.info('Starting cache warming');
|
|
|
|
// Làm ấm user permissions cho active users
|
|
const activeUsers = await userRepository.findActive({ limit: 1000 });
|
|
|
|
for (const user of activeUsers) {
|
|
const permissions = await rbacService.getUserPermissions(user.id);
|
|
|
|
await cache.set(
|
|
cacheKeys.userPermissions(user.id),
|
|
permissions,
|
|
300 // 5 phút
|
|
);
|
|
}
|
|
|
|
logger.info('Cache warming completed', { count: activeUsers.length });
|
|
}
|
|
|
|
// Chạy khi service khởi động
|
|
warmCache().catch(err => logger.error('Cache warming failed', { err }));
|
|
```
|
|
|
|
## Quyết định Thiết kế
|
|
|
|
### Quyết định 1: Multi-layer Caching (L1 + L2)
|
|
|
|
**Bối cảnh**: Cần giảm tải cho Redis và đạt độ trễ cực thấp cho dữ liệu hot.
|
|
**Quyết định**: Sử dụng kết hợp L1 (NodeCache) và L2 (Redis).
|
|
**Hậu quả**:
|
|
- ✅ Độ trễ < 1ms cho 40-50% requests.
|
|
- ✅ Giảm network traffic tới Redis.
|
|
- ❌ Phức tạp trong đồng bộ (L1 có thể stale trong thời gian ngắn).
|
|
|
|
## Đặc điểm Hiệu suất
|
|
|
|
### Mục tiêu Hiệu suất
|
|
| Chỉ số | Mục tiêu | Ghi chú |
|
|
|-----------------|-------------------|-----------------|
|
|
| **L1 Hit Latency** | < 0.5ms | In-memory lookup |
|
|
| **L2 Hit Latency** | < 5ms | Network RTT + Redis processing |
|
|
| **Combine Hit Rate** | > 90% | L1 + L2 combined |
|
|
| **L1 Capacity** | 10k items | Per instance limit to protect heap |
|
|
| **Cache Warmup Time** | < 30s | At service startup |
|
|
|
|
## Cân nhắc Bảo mật
|
|
|
|
### Bảo mật Cache
|
|
- **Encryption**: Dữ liệu nhạy cảm (PII) PHẢI được mã hóa trước khi lưu vào L2 Redis (AES-256). L1 có thể lưuplaintext vì nằm trong memory process (trừ khi memory dump).
|
|
- **Isolation**: Redis instance được bảo vệ bằng mật khẩu và Network Policy (chỉ allow traffic từ nội bộ K8s).
|
|
- **TLS**: Kết nối tới Redis qua TLS 1.2+.
|
|
- **Data Sanitization**: Không cache toàn bộ user object nếu chứa password hash hoặc secrets.
|
|
|
|
## Triển khai
|
|
|
|
```mermaid
|
|
graph TD
|
|
subgraph "Redis Cluster"
|
|
subgraph "Masters"
|
|
M1[Redis Master 1<br/>Slots: 0-5460]
|
|
M2[Redis Master 2<br/>Slots: 5461-10922]
|
|
M3[Redis Master 3<br/>Slots: 10923-16383]
|
|
end
|
|
|
|
subgraph "Slaves"
|
|
S1[Redis Slave 1<br/>Replica of M1]
|
|
S2[Redis Slave 2<br/>Replica of M2]
|
|
S3[Redis Slave 3<br/>Replica of M3]
|
|
end
|
|
|
|
M1 --> S1
|
|
M2 --> S2
|
|
M3 --> S3
|
|
|
|
Sentinel[Redis Sentinel<br/>3 nodes]
|
|
|
|
Sentinel -.->|Monitor| M1
|
|
Sentinel -.->|Monitor| M2
|
|
Sentinel -.->|Monitor| M3
|
|
end
|
|
|
|
subgraph "Services"
|
|
Service1[Service A]
|
|
Service2[Service B]
|
|
Service3[Service C]
|
|
end
|
|
|
|
Service1 --> M1
|
|
Service1 --> M2
|
|
Service1 --> M3
|
|
|
|
Service2 --> M1
|
|
Service2 --> M2
|
|
Service2 --> M3
|
|
|
|
Service3 --> M1
|
|
Service3 --> M2
|
|
Service3 --> M3
|
|
|
|
classDef master fill:#e65100,stroke:#ef6c00,color:#fff
|
|
classDef slave fill:#f57c00,stroke:#e65100,color:#fff
|
|
classDef sentinel fill:#4a148c,stroke:#7b1fa2,color:#fff
|
|
classDef service fill:#1a237e,stroke:#3949ab,color:#fff
|
|
classDef default fill:#202020,stroke:#505050,color:#fff
|
|
|
|
class M1,M2,M3 master
|
|
class S1,S2,S3 slave
|
|
class Sentinel sentinel
|
|
class Service1,Service2,Service3 service
|
|
```
|
|
|
|
### Chiến lược Triển khai
|
|
|
|
**Redis Cluster Configuration**:
|
|
- **Mode**: Cluster mode với 3 masters + 3 slaves
|
|
- **Replication**: Mỗi master có 1 slave cho high availability
|
|
- **Sentinel**: 3-node Sentinel ensemble cho automatic failover
|
|
- **Sharding**: 16384 hash slots phân chia đều giữa 3 masters
|
|
- **Persistence**: RDB snapshots mỗi 5 phút, AOF disabled (performance)
|
|
|
|
**Resource Allocation**:
|
|
| Component | CPU | Memory | Disk | Replicas |
|
|
|-----------|-----|--------|------|----------|
|
|
| **Redis Master** | 1 core | 2GB | 10GB SSD | 3 |
|
|
| **Redis Slave** | 1 core | 2GB | 10GB SSD | 3 |
|
|
| **Sentinel** | 500m | 512MB | 5GB | 3 |
|
|
|
|
**Redis Configuration**:
|
|
```yaml
|
|
# redis.conf
|
|
maxmemory 2gb
|
|
maxmemory-policy allkeys-lru # Evict least recently used keys
|
|
timeout 300 # Close idle connections after 5min
|
|
tcp-keepalive 60
|
|
save 300 10 # RDB snapshot every 5min if 10+ keys changed
|
|
appendonly no # Disable AOF for performance
|
|
|
|
# Cluster config
|
|
cluster-enabled yes
|
|
cluster-node-timeout 5000
|
|
cluster-replica-validity-factor 0
|
|
```
|
|
|
|
**High Availability**:
|
|
- Automatic failover với Redis Sentinel
|
|
- Slave promotion khi master fails
|
|
- Client-side retry logic
|
|
- Connection pooling (max 50 connections per service)
|
|
|
|
**Scaling Strategy**:
|
|
- **Vertical**: Tăng memory per node (2GB → 4GB → 8GB)
|
|
- **Horizontal**: Thêm master nodes (3 → 5 → 7)
|
|
- **Read Scaling**: Route reads to slaves
|
|
- **Monitoring**: Auto-alert khi memory usage > 80%
|
|
|
|
## Giám sát & Khả năng quan sát
|
|
|
|
### Chỉ số Chính
|
|
|
|
**Cache Performance Metrics**:
|
|
```typescript
|
|
// Custom metrics cho cache performance
|
|
import { Counter, Histogram, Gauge } from 'prom-client';
|
|
|
|
export const cacheHits = new Counter({
|
|
name: 'cache_hits_total',
|
|
labelNames: ['layer', 'key_prefix'] // layer: l1/l2, key_prefix: user/session/etc
|
|
});
|
|
|
|
export const cacheMisses = new Counter({
|
|
name: 'cache_misses_total',
|
|
help: 'Tổng số cache misses',
|
|
labelNames: ['key_prefix']
|
|
});
|
|
|
|
export const cacheLatency = new Histogram({
|
|
name: 'cache_operation_duration_seconds',
|
|
help: 'Thời gian thực hiện cache operation',
|
|
labelNames: ['operation', 'layer'], // operation: get/set/del
|
|
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1]
|
|
});
|
|
|
|
export const cacheSize = new Gauge({
|
|
name: 'cache_size_bytes',
|
|
help: 'Kích thước cache (bytes)',
|
|
labelNames: ['layer']
|
|
});
|
|
|
|
export const cacheEvictions = new Counter({
|
|
name: 'cache_evictions_total',
|
|
help: 'Tổng số cache evictions',
|
|
labelNames: ['layer', 'reason'] // reason: ttl_expired/memory_full
|
|
});
|
|
```
|
|
|
|
**Redis Metrics**:
|
|
- `redis_connected_clients` - Connected clients
|
|
- `redis_used_memory_bytes` - Memory usage
|
|
- `redis_memory_fragmentation_ratio` - Memory fragmentation
|
|
- `redis_keyspace_hits_total` - Cache hits
|
|
- `redis_keyspace_misses_total` - Cache misses
|
|
- `redis_evicted_keys_total` - Evicted keys
|
|
- `redis_expired_keys_total` - Expired keys
|
|
- `redis_commands_processed_total` - Commands processed
|
|
|
|
**Calculated Metrics**:
|
|
```promql
|
|
# Cache hit rate
|
|
rate(cache_hits_total[5m]) / (rate(cache_hits_total[5m]) + rate(cache_misses_total[5m]))
|
|
|
|
# L1 hit rate
|
|
rate(cache_hits_total{layer="l1"}[5m]) / rate(cache_hits_total[5m])
|
|
|
|
# L2 hit rate
|
|
rate(cache_hits_total{layer="l2"}[5m]) / rate(cache_hits_total[5m])
|
|
|
|
# Average cache latency
|
|
histogram_quantile(0.95, cache_operation_duration_seconds_bucket)
|
|
|
|
# Memory usage percentage
|
|
redis_used_memory_bytes / redis_maxmemory_bytes * 100
|
|
```
|
|
|
|
**Alerting Rules**:
|
|
```yaml
|
|
# Quy tắc cảnh báo cho cache
|
|
groups:
|
|
- name: cache_alerts
|
|
interval: 30s
|
|
rules:
|
|
# Low cache hit rate
|
|
- alert: LowCacheHitRate
|
|
expr: |
|
|
rate(cache_hits_total[5m]) /
|
|
(rate(cache_hits_total[5m]) + rate(cache_misses_total[5m])) < 0.5
|
|
for: 10m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "Tỷ lệ cache hit thấp"
|
|
description: "Tỷ lệ cache hit là {{ $value | humanizePercentage }}"
|
|
|
|
# High memory usage
|
|
- alert: HighRedisMemoryUsage
|
|
expr: redis_used_memory_bytes / redis_maxmemory_bytes > 0.8
|
|
for: 5m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "Sử dụng bộ nhớ Redis cao"
|
|
description: "Bộ nhớ Redis sử dụng là {{ $value | humanizePercentage }}"
|
|
|
|
# High eviction rate
|
|
- alert: HighEvictionRate
|
|
expr: rate(redis_evicted_keys_total[5m]) > 100
|
|
for: 5m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "Tỷ lệ cache eviction cao"
|
|
description: "Tỷ lệ eviction là {{ $value }}/giây"
|
|
|
|
# Redis down
|
|
- alert: RedisDown
|
|
expr: redis_up == 0
|
|
for: 1m
|
|
labels:
|
|
severity: critical
|
|
annotations:
|
|
summary: "Redis bị down"
|
|
|
|
# High replication lag
|
|
- alert: HighReplicationLag
|
|
expr: redis_replication_lag_seconds > 5
|
|
for: 2m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "Độ trễ replication cao"
|
|
description: "Độ trễ replication là {{ $value }}s"
|
|
```
|
|
|
|
**Dashboards**:
|
|
- **Cache Overview**: Hit rate, miss rate, latency, size
|
|
- **Redis Cluster**: Memory usage, connections, commands/sec
|
|
- **Performance**: L1 vs L2 hit rates, operation latency
|
|
- **Evictions**: Eviction rate, reasons, trends
|
|
|
|
**Logging**:
|
|
```typescript
|
|
// Structured logging cho cache operations
|
|
logger.debug('Cache operation', {
|
|
operation: 'get',
|
|
layer: 'l1',
|
|
key: cacheKey,
|
|
hit: true,
|
|
latency: duration,
|
|
correlationId: req.correlationId
|
|
});
|
|
|
|
logger.warn('Cache eviction', {
|
|
layer: 'l2',
|
|
reason: 'memory_full',
|
|
evictedKeys: count,
|
|
memoryUsage: usagePercent
|
|
});
|
|
|
|
logger.error('Cache error', {
|
|
operation: 'set',
|
|
layer: 'l2',
|
|
error: error.message,
|
|
key: cacheKey
|
|
});
|
|
```
|
|
|
|
**Health Checks**:
|
|
```typescript
|
|
// Health check cho Redis
|
|
async function checkRedisHealth(): Promise<boolean> {
|
|
try {
|
|
await redis.ping();
|
|
const info = await redis.info('memory');
|
|
const memoryUsage = parseMemoryUsage(info);
|
|
|
|
return memoryUsage < 0.9; // Healthy if < 90% memory
|
|
} catch (error) {
|
|
logger.error('Redis health check failed', { error });
|
|
return false;
|
|
}
|
|
}
|
|
```
|
|
|
|
|
|
## Tài liệu Liên quan
|
|
|
|
- [System Design](./system-design.md) - Kiến trúc tổng thể với caching
|
|
- [Data Consistency Patterns](./data-consistency-patterns.md) - Cache invalidation patterns
|
|
|
|
---
|
|
|
|
**Cập nhật Lần cuối**: 2026-01-14
|
|
**Tác giả**: GoodGo Architecture Team
|
|
|
|
## Quick Tips
|
|
|
|
### Mermaid Common Issues
|
|
- **Arrow Syntax**: Use `-->` for solid arrows, `-.->` for dotted arrows.
|
|
- **Node IDs**: Avoid spaces/special chars in IDs (e.g., `Node-A` not `Node A`).
|
|
- **Subgraphs**: Ensure `subgraph` names are unique and descriptive.
|
|
|
|
### Color Pattern Quick Reference
|
|
| Element | Dark Color | Text Color |
|
|
|---------|------------|------------|
|
|
| **Service (Blue)** | `#1a237e` | `#ffffff` |
|
|
| **Storage (Gray)** | `#212121` | `#ffffff` |
|
|
| **Cache L2 (Orange)** | `#e65100` | `#ffffff` |
|
|
| **Cache L1 (Green)** | `#1b5e20` | `#ffffff` |
|
|
| **Monitoring (Purple)** | `#4a148c` | `#ffffff` |
|
|
|
|
### Visual Indicators
|
|
- ✅ **Recommended / Khuyên dùng**
|
|
- ❌ **Not Recommended / Không khuyên dùng**
|
|
- ⚠️ **Warning / Cảnh báo**
|