feat(docs): Update architecture and caching documentation
- Added detailed sections on distributed caching architecture, including caching overview, ICacheService interface, and caching strategies for tokens and sessions. - Enhanced documentation with mermaid diagrams to illustrate caching flows and key patterns. - Updated Redis configuration instructions in the README, including environment variable setup and usage examples for caching operations. - Removed outdated notes from the NOTE.MD file to streamline content and focus on relevant tasks and proposals.
This commit is contained in:
9
NOTE.MD
9
NOTE.MD
@@ -8,15 +8,6 @@
|
||||
|
||||
Có Cached chưa
|
||||
|
||||
⚠️ Vấn đề phát hiện:
|
||||
/connect/token endpoint không phản hồi - OAuth2 token endpoint bị treo khi gửi request. Có vẻ OpenIddict Server middleware không xử lý request đúng cách.
|
||||
|
||||
Bạn muốn tôi làm gì tiếp:
|
||||
Fix OAuth2 /connect/token endpoint - Debug và sửa lỗi OpenIddict configuration
|
||||
Chỉ cần database connection - Nếu chỉ cần verify database connection thì đã hoàn thành
|
||||
Tiếp tục với task khác - Cần hướng dẫn cụ thể
|
||||
|
||||
|
||||
Đề xuất cần implement:
|
||||
Redis Connection - Đăng ký IConnectionMultiplexer trong DI
|
||||
Distributed Caching Service - Sử dụng IDistributedCache
|
||||
|
||||
@@ -367,12 +367,163 @@ All errors return Problem Details format:
|
||||
}
|
||||
```
|
||||
|
||||
## Distributed Caching Architecture
|
||||
|
||||
### Caching Overview
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Application"
|
||||
SVC[Services]
|
||||
CACHE[ICacheService]
|
||||
end
|
||||
|
||||
subgraph "Caching Layer"
|
||||
REDIS[(Redis Server)]
|
||||
TOKEN[Token Cache]
|
||||
SESSION[Session Cache]
|
||||
DATA[Data Cache]
|
||||
end
|
||||
|
||||
SVC --> CACHE
|
||||
CACHE --> REDIS
|
||||
REDIS --> TOKEN
|
||||
REDIS --> SESSION
|
||||
REDIS --> DATA
|
||||
|
||||
style CACHE fill:#e74c3c,stroke:#c0392b,color:#fff
|
||||
style REDIS fill:#d35400,stroke:#a04000,color:#fff
|
||||
style TOKEN fill:#9b59b6,stroke:#7d3c98,color:#fff
|
||||
style SESSION fill:#3498db,stroke:#2980b9,color:#fff
|
||||
style DATA fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||
```
|
||||
|
||||
### ICacheService Interface
|
||||
|
||||
The service implements a generic `ICacheService` interface for distributed caching:
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `GetAsync<T>` | Retrieve cached item by key |
|
||||
| `SetAsync<T>` | Store item with optional TTL |
|
||||
| `RemoveAsync` | Delete cached item |
|
||||
| `ExistsAsync` | Check if key exists |
|
||||
| `GetOrSetAsync<T>` | Cache-aside pattern |
|
||||
| `BlacklistAsync` | Add to token blacklist |
|
||||
| `IsBlacklistedAsync` | Check token blacklist |
|
||||
|
||||
### Token Caching Strategy
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant AuthController
|
||||
participant CacheService
|
||||
participant Redis
|
||||
participant Database
|
||||
|
||||
Note over Client,Database: Token Validation with Cache
|
||||
|
||||
Client->>AuthController: Request with JWT
|
||||
AuthController->>CacheService: IsBlacklistedAsync(tokenId)
|
||||
CacheService->>Redis: GET blacklist:token:{id}
|
||||
Redis-->>CacheService: null (not blacklisted)
|
||||
CacheService-->>AuthController: false
|
||||
AuthController->>AuthController: Validate JWT Claims
|
||||
AuthController-->>Client: Response
|
||||
|
||||
Note over Client,Database: Token Revocation (Logout)
|
||||
|
||||
Client->>AuthController: POST /logout
|
||||
AuthController->>CacheService: BlacklistAsync(tokenId, 7 days)
|
||||
CacheService->>Redis: SETEX blacklist:token:{id} 604800 "1"
|
||||
Redis-->>CacheService: OK
|
||||
AuthController-->>Client: 200 OK
|
||||
```
|
||||
|
||||
### Token Cache Keys
|
||||
|
||||
| Key Pattern | Purpose | TTL |
|
||||
|-------------|---------|-----|
|
||||
| `blacklist:token:{tokenId}` | Revoked tokens | Token remaining lifetime |
|
||||
| `blacklist:refresh:{tokenId}` | Revoked refresh tokens | 7 days |
|
||||
| `user:{userId}:tokens` | User's active tokens | 15 minutes |
|
||||
|
||||
### Session Caching Strategy
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Session Data"
|
||||
UID[User ID]
|
||||
ROLES[User Roles]
|
||||
PERMS[Permissions]
|
||||
CLAIMS[Claims]
|
||||
end
|
||||
|
||||
subgraph "Cache Keys"
|
||||
K1[session:{userId}]
|
||||
K2[user:{userId}:roles]
|
||||
K3[user:{userId}:permissions]
|
||||
end
|
||||
|
||||
UID --> K1
|
||||
ROLES --> K2
|
||||
PERMS --> K3
|
||||
|
||||
style K1 fill:#3498db,stroke:#2980b9,color:#fff
|
||||
style K2 fill:#9b59b6,stroke:#7d3c98,color:#fff
|
||||
style K3 fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||
```
|
||||
|
||||
### Session Cache Keys
|
||||
|
||||
| Key Pattern | Purpose | TTL |
|
||||
|-------------|---------|-----|
|
||||
| `session:{userId}` | User session data | 30 minutes |
|
||||
| `user:{userId}:roles` | Cached user roles | 15 minutes |
|
||||
| `user:{userId}:permissions` | Computed permissions | 15 minutes |
|
||||
| `user:{userId}:profile` | User profile data | 10 minutes |
|
||||
|
||||
### Cache-Aside Pattern
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Service
|
||||
participant CacheService
|
||||
participant Redis
|
||||
participant Database
|
||||
|
||||
Service->>CacheService: GetOrSetAsync(key, factory)
|
||||
CacheService->>Redis: GET key
|
||||
|
||||
alt Cache Hit
|
||||
Redis-->>CacheService: cached data
|
||||
CacheService-->>Service: return data
|
||||
else Cache Miss
|
||||
Redis-->>CacheService: null
|
||||
CacheService->>Database: factory() - fetch data
|
||||
Database-->>CacheService: data
|
||||
CacheService->>Redis: SETEX key ttl data
|
||||
CacheService-->>Service: return data
|
||||
end
|
||||
```
|
||||
|
||||
### Cache Invalidation Strategies
|
||||
|
||||
| Strategy | When to Use | Implementation |
|
||||
|----------|-------------|----------------|
|
||||
| **Time-based (TTL)** | General data | `SetAsync(..., TimeSpan.FromMinutes(15))` |
|
||||
| **Event-based** | User updates | Remove cache on update event |
|
||||
| **Pattern-based** | Bulk invalidation | `RemoveByPatternAsync("user:*")` |
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Connection Pooling**: EF Core with Npgsql resilience
|
||||
2. **Token Caching**: Redis for token validation
|
||||
3. **Async Operations**: All I/O operations are async
|
||||
4. **Database Indexes**: Configured in EntityConfigurations
|
||||
2. **Token Caching**: Redis for token validation and blacklist
|
||||
3. **Session Caching**: User sessions and permissions cached
|
||||
4. **Async Operations**: All I/O operations are async
|
||||
5. **Database Indexes**: Configured in EntityConfigurations
|
||||
6. **Cache-Aside Pattern**: Reduce database load for frequent reads
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -367,12 +367,163 @@ Tất cả lỗi trả về định dạng Problem Details:
|
||||
}
|
||||
```
|
||||
|
||||
## Kiến Trúc Distributed Caching
|
||||
|
||||
### Tổng Quan Caching
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Application"
|
||||
SVC[Services]
|
||||
CACHE[ICacheService]
|
||||
end
|
||||
|
||||
subgraph "Tầng Caching"
|
||||
REDIS[(Redis Server)]
|
||||
TOKEN[Token Cache]
|
||||
SESSION[Session Cache]
|
||||
DATA[Data Cache]
|
||||
end
|
||||
|
||||
SVC --> CACHE
|
||||
CACHE --> REDIS
|
||||
REDIS --> TOKEN
|
||||
REDIS --> SESSION
|
||||
REDIS --> DATA
|
||||
|
||||
style CACHE fill:#e74c3c,stroke:#c0392b,color:#fff
|
||||
style REDIS fill:#d35400,stroke:#a04000,color:#fff
|
||||
style TOKEN fill:#9b59b6,stroke:#7d3c98,color:#fff
|
||||
style SESSION fill:#3498db,stroke:#2980b9,color:#fff
|
||||
style DATA fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||
```
|
||||
|
||||
### ICacheService Interface
|
||||
|
||||
Service implement interface `ICacheService` cho distributed caching:
|
||||
|
||||
| Method | Mục Đích |
|
||||
|--------|----------|
|
||||
| `GetAsync<T>` | Lấy item từ cache theo key |
|
||||
| `SetAsync<T>` | Lưu item với TTL tùy chọn |
|
||||
| `RemoveAsync` | Xóa item từ cache |
|
||||
| `ExistsAsync` | Kiểm tra key tồn tại |
|
||||
| `GetOrSetAsync<T>` | Pattern cache-aside |
|
||||
| `BlacklistAsync` | Thêm vào blacklist token |
|
||||
| `IsBlacklistedAsync` | Kiểm tra blacklist token |
|
||||
|
||||
### Chiến Lược Token Caching
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant AuthController
|
||||
participant CacheService
|
||||
participant Redis
|
||||
participant Database
|
||||
|
||||
Note over Client,Database: Validate Token với Cache
|
||||
|
||||
Client->>AuthController: Request với JWT
|
||||
AuthController->>CacheService: IsBlacklistedAsync(tokenId)
|
||||
CacheService->>Redis: GET blacklist:token:{id}
|
||||
Redis-->>CacheService: null (không bị blacklist)
|
||||
CacheService-->>AuthController: false
|
||||
AuthController->>AuthController: Validate JWT Claims
|
||||
AuthController-->>Client: Response
|
||||
|
||||
Note over Client,Database: Thu Hồi Token (Logout)
|
||||
|
||||
Client->>AuthController: POST /logout
|
||||
AuthController->>CacheService: BlacklistAsync(tokenId, 7 ngày)
|
||||
CacheService->>Redis: SETEX blacklist:token:{id} 604800 "1"
|
||||
Redis-->>CacheService: OK
|
||||
AuthController-->>Client: 200 OK
|
||||
```
|
||||
|
||||
### Token Cache Keys
|
||||
|
||||
| Mẫu Key | Mục Đích | TTL |
|
||||
|---------|----------|-----|
|
||||
| `blacklist:token:{tokenId}` | Tokens đã thu hồi | Thời gian còn lại của token |
|
||||
| `blacklist:refresh:{tokenId}` | Refresh tokens đã thu hồi | 7 ngày |
|
||||
| `user:{userId}:tokens` | Tokens đang hoạt động của user | 15 phút |
|
||||
|
||||
### Chiến Lược Session Caching
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Session Data"
|
||||
UID[User ID]
|
||||
ROLES[User Roles]
|
||||
PERMS[Permissions]
|
||||
CLAIMS[Claims]
|
||||
end
|
||||
|
||||
subgraph "Cache Keys"
|
||||
K1[session:{userId}]
|
||||
K2[user:{userId}:roles]
|
||||
K3[user:{userId}:permissions]
|
||||
end
|
||||
|
||||
UID --> K1
|
||||
ROLES --> K2
|
||||
PERMS --> K3
|
||||
|
||||
style K1 fill:#3498db,stroke:#2980b9,color:#fff
|
||||
style K2 fill:#9b59b6,stroke:#7d3c98,color:#fff
|
||||
style K3 fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||
```
|
||||
|
||||
### Session Cache Keys
|
||||
|
||||
| Mẫu Key | Mục Đích | TTL |
|
||||
|---------|----------|-----|
|
||||
| `session:{userId}` | Dữ liệu session user | 30 phút |
|
||||
| `user:{userId}:roles` | Cached user roles | 15 phút |
|
||||
| `user:{userId}:permissions` | Computed permissions | 15 phút |
|
||||
| `user:{userId}:profile` | Dữ liệu profile user | 10 phút |
|
||||
|
||||
### Pattern Cache-Aside
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Service
|
||||
participant CacheService
|
||||
participant Redis
|
||||
participant Database
|
||||
|
||||
Service->>CacheService: GetOrSetAsync(key, factory)
|
||||
CacheService->>Redis: GET key
|
||||
|
||||
alt Cache Hit
|
||||
Redis-->>CacheService: dữ liệu cached
|
||||
CacheService-->>Service: trả về dữ liệu
|
||||
else Cache Miss
|
||||
Redis-->>CacheService: null
|
||||
CacheService->>Database: factory() - lấy dữ liệu
|
||||
Database-->>CacheService: dữ liệu
|
||||
CacheService->>Redis: SETEX key ttl data
|
||||
CacheService-->>Service: trả về dữ liệu
|
||||
end
|
||||
```
|
||||
|
||||
### Chiến Lược Invalidation Cache
|
||||
|
||||
| Chiến Lược | Khi Nào Sử Dụng | Implementation |
|
||||
|------------|-----------------|----------------|
|
||||
| **Time-based (TTL)** | Dữ liệu chung | `SetAsync(..., TimeSpan.FromMinutes(15))` |
|
||||
| **Event-based** | Cập nhật user | Xóa cache khi có event cập nhật |
|
||||
| **Pattern-based** | Invalidation hàng loạt | `RemoveByPatternAsync("user:*")` |
|
||||
|
||||
## Cân Nhắc Hiệu Năng
|
||||
|
||||
1. **Connection Pooling**: EF Core với Npgsql resilience
|
||||
2. **Token Caching**: Redis cho token validation
|
||||
3. **Async Operations**: Tất cả I/O operations đều async
|
||||
4. **Database Indexes**: Cấu hình trong EntityConfigurations
|
||||
2. **Token Caching**: Redis cho token validation và blacklist
|
||||
3. **Session Caching**: User sessions và permissions được cache
|
||||
4. **Async Operations**: Tất cả I/O operations đều async
|
||||
5. **Database Indexes**: Cấu hình trong EntityConfigurations
|
||||
6. **Cache-Aside Pattern**: Giảm tải database cho các truy vấn thường xuyên
|
||||
|
||||
## Tài Liệu Tham Khảo
|
||||
|
||||
|
||||
@@ -228,6 +228,108 @@ Sau khi chạy service, truy cập Swagger UI tại:
|
||||
| `ASPNETCORE_ENVIRONMENT` | Môi trường | `Development` |
|
||||
| `DATABASE_URL` | PostgreSQL connection | - |
|
||||
| `JWT_SECRET` | Secret ký JWT (32+ ký tự) | - |
|
||||
| `REDIS_HOST` | Redis server host | `localhost` |
|
||||
| `REDIS_PORT` | Redis server port | `6379` |
|
||||
| `REDIS_PASSWORD` | Redis password | - |
|
||||
| `REDIS_DATABASE` | Redis database number | `0` |
|
||||
|
||||
## Redis Caching
|
||||
|
||||
Service sử dụng Redis cho distributed caching thông qua interface `ICacheService`.
|
||||
|
||||
### Cấu Hình Redis
|
||||
|
||||
Thêm cấu hình Redis trong `appsettings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Redis": {
|
||||
"Host": "localhost",
|
||||
"Port": 6379,
|
||||
"Password": "",
|
||||
"Database": 0,
|
||||
"ConnectTimeout": 5000,
|
||||
"SyncTimeout": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hoặc sử dụng biến môi trường:
|
||||
|
||||
```bash
|
||||
REDIS_HOST=your-redis-host
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=your-password
|
||||
REDIS_DATABASE=0
|
||||
```
|
||||
|
||||
### ICacheService Interface
|
||||
|
||||
```csharp
|
||||
public interface ICacheService
|
||||
{
|
||||
// Các thao tác cơ bản
|
||||
Task<T?> GetAsync<T>(string key);
|
||||
Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
|
||||
Task RemoveAsync(string key);
|
||||
Task<bool> ExistsAsync(string key);
|
||||
|
||||
// Get hoặc tạo mới pattern
|
||||
Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null);
|
||||
|
||||
// Hỗ trợ blacklist token
|
||||
Task BlacklistAsync(string key, TimeSpan expiration);
|
||||
Task<bool> IsBlacklistedAsync(string key);
|
||||
}
|
||||
```
|
||||
|
||||
### Ví Dụ Sử Dụng
|
||||
|
||||
**Get/Set cơ bản:**
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly ICacheService _cache;
|
||||
|
||||
public MyService(ICacheService cache) => _cache = cache;
|
||||
|
||||
public async Task<User?> GetUser(string userId)
|
||||
{
|
||||
return await _cache.GetAsync<User>($"user:{userId}");
|
||||
}
|
||||
|
||||
public async Task CacheUser(User user)
|
||||
{
|
||||
await _cache.SetAsync($"user:{user.Id}", user, TimeSpan.FromMinutes(15));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Get or Set Pattern (Cache-Aside):**
|
||||
```csharp
|
||||
public async Task<User> GetUserById(string userId)
|
||||
{
|
||||
return await _cache.GetOrSetAsync(
|
||||
$"user:{userId}",
|
||||
async () => await _repository.GetByIdAsync(userId),
|
||||
TimeSpan.FromMinutes(15)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Blacklist Token (cho Logout):**
|
||||
```csharp
|
||||
public async Task Logout(string tokenId)
|
||||
{
|
||||
// Blacklist refresh token trong thời gian còn lại của token
|
||||
await _cache.BlacklistAsync($"token:{tokenId}", TimeSpan.FromDays(7));
|
||||
}
|
||||
|
||||
public async Task<bool> IsTokenRevoked(string tokenId)
|
||||
{
|
||||
return await _cache.IsBlacklistedAsync($"token:{tokenId}");
|
||||
}
|
||||
```
|
||||
|
||||
## Kiểm Thử
|
||||
|
||||
|
||||
Reference in New Issue
Block a user