Bỏ qua, đến nội dung

@digiforce-nc/cache

Package@digiforce-nc/cache
Importimport { 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.

MethodMô 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:

MethodMô 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

OptionTypeMặc địnhMô tả
maxnumber500Số entry tối đa. Khi đầy, LRU eviction.
ttlnumber0 (vĩnh viễn)TTL mặc định (giây).

Redis store

OptionTypeMặc địnhMô tả
urlstring-Redis connection URL.
hoststring'localhost'Redis host.
portnumber6379Redis port.
passwordstring-Redis password.
dbnumber0Redis database index.
prefixstring''Key prefix để tránh xung đột.
ttlnumber0TTL mặc định (giây).

Biến môi trường cho Redis

BiếnMô tảVí dụ
CACHE_DEFAULT_STOREStore mặc định (memory hoặc redis)memory
CACHE_REDIS_URLRedis connection stringredis://localhost:6379/0
CACHE_REDIS_HOSTRedis hostlocalhost
CACHE_REDIS_PORTRedis port6379
CACHE_REDIS_PASSWORDRedis passwordsecret
CACHE_REDIS_DBRedis database index0
CACHE_DEFAULT_TTLTTL mặc định (giây)3600
CACHE_MAX_ENTRIESSố 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ùngTrước khi query DB, check bloom filter để loại nhanh email chắc chắn chưa đăng ký
API rate limitingCheck nhanh IP/token đã bị block chưa
Cache miss optimizationTrá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.

MethodMô 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

PackageVai trò
@digiforce-nc/lock-managerLock cho Counter operations, đảm bảo atomic increment/decrement

Đọc thêm