Giao diện
@digiforce-nc/cache
| Package | @digiforce-nc/cache |
| Import | import { CacheManager, Cache } from '@digiforce-nc/cache' |
Tổng quan
@digiforce-nc/cache là lớp quản lý cache của hệ thống Digiforce, xây dựng trên nền cache-manager. Package cung cấp:
- CacheManager - tạo và quản lý nhiều cache store có tên (named caches).
- Cache - interface thống nhất cho các thao tác get/set/del với hỗ trợ TTL.
- Memory store - cache trong bộ nhớ (mặc định), phù hợp single instance.
- Redis store - cache phân tán dùng Redis, phù hợp multi-instance production.
- Bloom filter - kiểm tra membership xác suất cho tập dữ liệu lớn.
- Counter - bộ đếm thread-safe với hỗ trợ memory và Redis backend.
CacheManager API
CacheManager là entry point chính, quản lý lifecycle của tất cả cache store.
| Method | Mô tả |
|---|---|
createCache(options) | Tạo cache store mới. options: { name, store?, ttl?, max?, prefix? }. Trả về Cache. |
getCache(name) | Lấy cache store theo tên. Trả về undefined nếu chưa tạo. |
flushAll() | Xoá toàn bộ dữ liệu trong tất cả cache store. |
registerStore(name, factory) | Đăng ký store backend tuỳ chỉnh. |
Tạo cache store
typescript
const cacheManager = new CacheManager();
// Memory store (mặc định)
const memCache = await cacheManager.createCache({
name: 'myCache',
store: 'memory',
ttl: 60, // TTL mặc định 60 giây
max: 1000, // Tối đa 1000 entry
});
// Redis store
const redisCache = await cacheManager.createCache({
name: 'sharedCache',
store: 'redis',
ttl: 300,
prefix: 'app:',
});Cache API
Mỗi Cache instance cung cấp interface thao tác dữ liệu:
| Method | Mô tả |
|---|---|
get(key) | Lấy giá trị theo key. Trả về undefined nếu không tồn tại hoặc đã hết TTL. |
set(key, value, ttl?) | Ghi giá trị. ttl (giây) ghi đè TTL mặc định của store. |
del(key) | Xoá entry theo key. |
wrap(key, fn, ttl?) | Cache-aside pattern: lấy từ cache, nếu miss thì gọi fn(), lưu kết quả, trả về. |
mget(keys) | Lấy nhiều key cùng lúc. Trả về array giá trị (hoặc undefined cho key không tồn tại). |
mset(entries, ttl?) | Ghi nhiều entry. entries: Array<[key, value]>. |
mdel(keys) | Xoá nhiều key. |
reset() | Xoá toàn bộ dữ liệu trong store này. |
keys() | Liệt kê tất cả key trong store. |
has(key) | Kiểm tra key có tồn tại không. Trả về boolean. |
Wrap pattern - cache-aside
wrap() là pattern được khuyến khích sử dụng nhất, tránh cache stampede:
typescript
const cache = cacheManager.getCache('myCache');
// Nếu cache có → trả về ngay
// Nếu cache miss → gọi hàm, lưu kết quả, trả về
const user = await cache.wrap(
`user:${userId}`,
async () => {
return await db.getRepository('users').findOne({
filter: { id: userId },
appends: ['roles'],
});
},
600, // cache 10 phút
);Wrap vs Get/Set thủ công
Luôn ưu tiên wrap() thay vì get → check → set thủ công. wrap() xử lý race condition và đảm bảo hàm chỉ chạy một lần dù có nhiều request đồng thời (cache stampede protection).
Cấu hình store
Memory store
| Option | Type | Mặc định | Mô tả |
|---|---|---|---|
max | number | 500 | Số entry tối đa. Khi đầy, LRU eviction. |
ttl | number | 0 (vĩnh viễn) | TTL mặc định (giây). |
Redis store
| Option | Type | Mặc định | Mô tả |
|---|---|---|---|
url | string | - | Redis connection URL. |
host | string | 'localhost' | Redis host. |
port | number | 6379 | Redis port. |
password | string | - | Redis password. |
db | number | 0 | Redis database index. |
prefix | string | '' | Key prefix để tránh xung đột. |
ttl | number | 0 | TTL mặc định (giây). |
Biến môi trường cho Redis
| Biến | Mô tả | Ví dụ |
|---|---|---|
CACHE_DEFAULT_STORE | Store mặc định (memory hoặc redis) | memory |
CACHE_REDIS_URL | Redis connection string | redis://localhost:6379/0 |
CACHE_REDIS_HOST | Redis host | localhost |
CACHE_REDIS_PORT | Redis port | 6379 |
CACHE_REDIS_PASSWORD | Redis password | secret |
CACHE_REDIS_DB | Redis database index | 0 |
CACHE_DEFAULT_TTL | TTL mặc định (giây) | 3600 |
CACHE_MAX_ENTRIES | Số entry tối đa (memory) | 1000 |
Bloom filter
Bloom filter là cấu trúc dữ liệu xác suất, cho phép kiểm tra nhanh xem một phần tử có thuộc tập hợp hay không - với khả năng false positive nhưng không bao giờ false negative.
Sử dụng
typescript
const bloom = cacheManager.createBloomFilter({
name: 'emailCheck',
expectedElements: 100000,
falsePositiveRate: 0.01,
});
// Thêm phần tử
bloom.add('user@example.com');
bloom.add('admin@example.com');
// Kiểm tra
bloom.has('user@example.com'); // true (chắc chắn có)
bloom.has('random@test.com'); // false (chắc chắn không có)
bloom.has('maybe@test.com'); // true hoặc false (false positive có thể xảy ra)Giới hạn Bloom filter
- Không thể xoá phần tử đã thêm.
- Tỉ lệ false positive tăng khi số phần tử vượt
expectedElements. - Gọi
reset()để xoá toàn bộ và build lại.
Use case phổ biến
| Trường hợp | Ý nghĩa |
|---|---|
| Kiểm tra email trùng | Trước khi query DB, check bloom filter để loại nhanh email chắc chắn chưa đăng ký |
| API rate limiting | Check nhanh IP/token đã bị block chưa |
| Cache miss optimization | Tránh query DB cho key chắc chắn không tồn tại |
Counter
Counter cung cấp bộ đếm thread-safe, hỗ trợ cả memory và Redis backend. Sử dụng LockManager để đảm bảo tính nhất quán trong môi trường concurrent.
| Method | Mô tả |
|---|---|
increment(key, delta?) | Tăng counter. delta mặc định là 1. Trả về giá trị mới. |
decrement(key, delta?) | Giảm counter. Trả về giá trị mới. |
get(key) | Lấy giá trị hiện tại. |
reset(key) | Reset counter về 0. |
typescript
const counter = cacheManager.createCounter({
name: 'apiCalls',
store: 'redis', // dùng Redis cho multi-instance
});
// Đếm API call
await counter.increment('user:123:calls');
await counter.increment('user:123:calls', 5); // tăng 5
const count = await counter.get('user:123:calls');
console.log(count); // 6
// Rate limiting
if (count > 1000) {
throw new Error('Rate limit exceeded');
}Multi-instance coordination
Khi chạy nhiều instance, phải dùng Redis backend cho Counter. Memory backend chỉ đếm trên instance local, dẫn đến sai lệch giữa các instance.
Ví dụ tổng hợp
typescript
import { CacheManager } from '@digiforce-nc/cache';
const cacheManager = new CacheManager();
// 1. Tạo cache cho user session
const sessionCache = await cacheManager.createCache({
name: 'sessions',
store: 'redis',
ttl: 3600, // 1 giờ
prefix: 'sess:',
});
// 2. Tạo cache cho query result
const queryCache = await cacheManager.createCache({
name: 'queries',
store: 'memory',
ttl: 300, // 5 phút
max: 500,
});
// 3. Cache-aside cho query nặng
const dashboardStats = await queryCache.wrap(
'dashboard:stats',
async () => {
const orders = await db.getRepository('orders').count({ filter: { status: 'completed' } });
const revenue = await db.getRepository('orders').aggregate('amount', 'sum');
return { orders, revenue };
},
600,
);
// 4. Session management
await sessionCache.set(`user:${userId}`, {
role: 'admin',
permissions: ['read', 'write'],
loginAt: Date.now(),
});
const session = await sessionCache.get(`user:${userId}`);
// 5. Batch operations
await queryCache.mset([
['key1', 'value1'],
['key2', 'value2'],
['key3', 'value3'],
], 120);
const values = await queryCache.mget(['key1', 'key2', 'key3']);
// 6. Flush khi deploy
await cacheManager.flushAll();Phụ thuộc
| Package | Vai trò |
|---|---|
@digiforce-nc/lock-manager | Lock cho Counter operations, đảm bảo atomic increment/decrement |
Đọc thêm
- Server Application - cách CacheManager tích hợp vào Application.
- Actions - action handler sử dụng Cache cho query optimization.
- Mã nguồn