Cache placement, invalidation và hot keys
Cache đổi latency/load lấy staleness, memory và failure modes mới; khi thiết kế phải nói rõ source of truth nằm ở đâu và miss path sẽ đi qua những thành phần nào.
1. Cache layers
Browser/CDN phù hợp với public/static content; reverse proxy có thể cache HTTP response; distributed cache chia sẻ dữ liệu giữa nhiều application instance; in-process cache nhanh nhất nhưng chỉ sống trong từng instance và khó invalidate đồng bộ. Cache key phải bao gồm tenant, auth, locale, version hoặc bất kỳ dimension nào làm response thay đổi.
| Layer | Điểm mạnh | Rủi ro / trade-off cần nói |
|---|---|---|
| Browser cache | Gần user nhất, giảm network request | Khó thu hồi ngay dữ liệu đã phát hành; policy HTTP phải đúng. |
| CDN | Giảm latency theo vùng và origin bandwidth | Phải phân biệt public/private response, purge/versioning và regional miss. |
| Reverse proxy | Cache HTTP response trước application | Key phải phản ánh header/query/identity thật sự ảnh hưởng representation. |
| Distributed cache | Nhiều app instance cùng dùng một cache | Thêm network hop, cluster failure mode, eviction và hot key. |
| In-process cache | Không network hop, latency rất thấp | Mỗi instance có bản sao riêng; invalidation và consistency khó hơn. |
2. Cache patterns và invalidation
Cache-aside là pattern phổ biến: application đọc cache trước, miss thì đọc source of truth rồi populate cache. Read-through, write-through và write-behind phụ thuộc semantics mà provider thực sự cung cấp; không nên suy diễn rằng mọi cache product đều có cùng guarantee.
TTL giới hạn thời gian một entry có thể tồn tại nhưng không bảo đảm dữ liệu luôn fresh. Với dữ liệu thay đổi, invalidate hoặc update cache nên xảy ra sau khi transaction ở source of truth commit. Việc dual write vào cache và database không tự động atomic, vì vậy có thể xảy ra trạng thái một bên thành công còn bên kia thất bại.
| Pattern | Read path | Write path / semantics cần chú ý |
|---|---|---|
| Cache-aside | App → cache; miss → DB → cache | Application tự chịu trách nhiệm populate/invalidate; dễ hiểu nhưng có stale window. |
| Read-through | App đọc qua cache provider | Provider thực hiện load; behavior khi load fail phải kiểm tra theo implementation. |
| Write-through | Read từ cache | Write đi qua cache tới backing store; latency và atomic boundary là provider-specific. |
| Write-behind | Read từ cache | Cache flush về backing store sau; giảm write latency nhưng tăng risk khi cache fail trước khi persist. |
3. Cache stampede và hot key
Khi một entry phổ biến hết hạn hoặc bị evict, nhiều request đồng thời có thể cùng miss và cùng đánh source of truth. Các kỹ thuật thường dùng là single-flight/request coalescing, jittered TTL, stale-while-revalidate, early refresh và admission control.
Hot key là một key nhận lượng truy cập lớn bất thường. Có thể giảm áp lực bằng cách replicate dữ liệu, thêm local cache, hoặc shard phần work có thể tách. Scale-out cache cluster không tự động chia tải cho một key duy nhất nếu tất cả request vẫn map tới cùng owner/partition.
3.1 Stampede: vì sao nguy hiểm?
- Nhiều request cùng miss làm DB hoặc downstream tăng load đột ngột.
- Backend chậm hơn làm request giữ connection/thread lâu hơn, tiếp tục khuếch đại contention.
- Nếu entry mới lại có cùng TTL tuyệt đối, các key có thể hết hạn thành từng “wave”.
3.2 Mitigation matrix
| Kỹ thuật | Ý nghĩa | Trade-off |
|---|---|---|
| Single-flight / request coalescing | Một request refresh; các request khác chờ hoặc dùng kết quả chung | Cần timeout/bounded wait để tránh một loader chậm giữ mọi request. |
| Jittered TTL | Thêm ngẫu nhiên vào TTL để entry không cùng hết hạn | Không loại bỏ miss; chỉ làm phẳng burst. |
| Stale-while-revalidate | Cho phép trả stale trong khi background/one request refresh | Phải có staleness budget rõ ràng. |
| Early refresh | Refresh trước khi TTL hết khi entry đủ hot | Tăng refresh traffic, cần policy để không refresh mọi key. |
| Admission control | Giới hạn request được phép đi tiếp xuống miss path | Có thể reject/degrade một phần traffic để bảo vệ dependency. |
4. Failure behavior
Cache down có thể tạo thundering herd vào database nếu toàn bộ request chuyển thẳng sang source of truth. Thiết kế cần có circuit, concurrency limit và degraded response phù hợp để bảo vệ dependency quan trọng hơn.
Negative caching giúp giảm repeated miss cho dữ liệu không tồn tại, nhưng TTL nên ngắn để không che dữ liệu vừa được tạo sau đó. Eviction policy phải xét working set của workload; một cache đầy không có nghĩa hit ratio sẽ tốt nếu policy liên tục đẩy các key hữu ích ra ngoài.
| Sự cố | Failure mode | Phản ứng mong muốn |
|---|---|---|
| Cache unavailable | Mọi read trở thành miss, DB/downstream bị burst | Circuit, bounded concurrency, rate limit hoặc degraded response. |
| Cache latency tăng | Cache trở thành bottleneck thay vì optimization | Timeout ngắn, fallback có kiểm soát; không cho request treo vô hạn. |
| Negative cache quá lâu | Che item vừa được tạo | TTL ngắn và invalidate khi create nếu workflow cần thấy ngay. |
| Eviction churn | Hit ratio giảm, dependency load tăng | Đo working set/hit ratio/eviction; điều chỉnh capacity hoặc admission/policy. |
5. CDN
CDN sử dụng HTTP caching primitives như Cache-Control và validators như ETag. Thiết kế production cần xét origin shield, purge hoặc versioned URL, signed URL/cookie và hiện tượng regional miss. Personalized/private response không được cache public theo cách làm rò rỉ representation của user này sang user khác.
CDN giảm latency đọc và origin bandwidth cho content cacheable, nhưng không sửa write path: write vẫn phải đi về hệ thống sở hữu dữ liệu, xử lý consistency và invalidation theo thiết kế riêng.
5.1 Purge hay versioned URL?
- Purge/invalidation: giữ nguyên URL nhưng yêu cầu edge loại object cũ; thời gian propagate phụ thuộc CDN/provider.
- Versioned URL: asset mới có URL mới, ví dụ hash trong filename; tránh phụ thuộc vào việc object cũ phải biến mất ngay.
5.2 Public vs private
6. Review checklist
- Source of truth và miss path đã được vẽ rõ chưa?
- Cache layer nào được dùng: browser, CDN, reverse proxy, distributed hay in-process?
- Cache key có đủ tenant/auth/locale/version và các dimension ảnh hưởng response không?
- Pattern đọc/ghi là cache-aside, read-through, write-through hay write-behind? Guarantee nào thực sự có?
- Staleness budget là bao nhiêu? TTL, invalidate và update xảy ra khi nào?
- Stampede/hot key được giảm bằng single-flight, jitter, stale-while-revalidate, early refresh, local cache/replication hay admission control?
- Nếu cache down, backend nào chịu miss traffic? Có circuit/concurrency limit/degraded mode không?
- Negative cache TTL có đủ ngắn để không che newly-created data không?
- Eviction policy/capacity có phù hợp working set và hit-ratio target không?
- CDN policy có phân biệt public/private, purge/versioned URL, signed access và regional miss không?