Một cache nhanh không nhất thiết phải trả lỗi ngay khi bản lưu vừa hết hạn hoặc origin tạm thời gặp sự cố. Hai chỉ thị stale-while-revalidate và stale-if-error cho phép hệ thống tiếp tục dùng phản hồi cũ trong những cửa sổ được kiểm soát: một chỉ thị che độ trễ làm mới, chỉ thị còn lại ưu tiên tính sẵn sàng khi origin lỗi.
Đây không phải công tắc “cache mọi thứ”. Muốn dùng an toàn, đội phát triển phải xác định dữ liệu nào được phép cũ, cũ tối đa bao lâu, tầng cache nào thực thi chính sách và bằng chứng nào chứng minh người dùng thật nhận đúng hành vi.
1. Fresh, stale và validation khác nhau thế nào?
Theo RFC 9111 về HTTP Caching, phản hồi còn fresh khi tuổi của nó chưa vượt freshness lifetime. Khi vượt ngưỡng đó, phản hồi trở thành stale. Stale không có nghĩa nội dung đã sai; nó chỉ có nghĩa cache không còn được mặc định tái sử dụng phản hồi nếu chưa validation hoặc chưa có quyền phục vụ stale.
max-age đặt freshness lifetime tính bằng giây. Age là ước lượng tuổi của phản hồi kể từ khi origin tạo hoặc validation thành công. Cache có thể validation bằng conditional request với ETag/If-None-Match hoặc Last-Modified/If-Modified-Since. Origin trả 304 Not Modified khi bản cũ vẫn dùng được, tránh truyền lại toàn bộ body.
| Trạng thái | Cache thường làm gì? | Người dùng thấy gì? |
|---|---|---|
| Fresh | Phục vụ bản lưu mà không gọi origin | Phản hồi nhanh từ cache |
| Stale, cần validation | Hỏi origin trước khi tái sử dụng | Có thể phải chờ origin |
| Stale được SWR cho phép | Trả bản cũ và làm mới nền | Không chờ validation trong cửa sổ cho phép |
| Stale được SIE cho phép | Trả bản cũ nếu gặp lỗi phù hợp | Nhận nội dung cũ thay vì hard error |
2. stale-while-revalidate che độ trễ như thế nào?
RFC 5861 định nghĩa stale-while-revalidate để cache có thể phục vụ phản hồi stale trong số giây được chỉ định, đồng thời cố gắng validation mà không chặn request đang chờ. Ví dụ:
Cache-Control: public, max-age=60, stale-while-revalidate=30
- Từ giây 0 đến 60: phản hồi fresh.
- Sau giây 60 và trong 30 giây tiếp theo: cache có thể trả bản stale ngay, rồi revalidate nền.
- Nếu làm mới thành công: bản mới thay thế bản cũ và tuổi được tính lại.
- Nếu cửa sổ 30 giây kết thúc mà chưa có bản hợp lệ: request tiếp theo không còn mặc nhiên hưởng SWR.
Quá trình nền thường chỉ được kích hoạt khi có request trong cửa sổ stale. Với URL ít truy cập, không nên giả định cache sẽ tự làm mới đúng thời điểm. Cách triển khai cũng khác giữa trình duyệt, reverse proxy và CDN; phải kiểm tra tầng thực tế thay vì suy luận từ header origin.
3. stale-if-error cải thiện khả năng chịu lỗi
stale-if-error cho phép cache dùng bản stale khi gặp lỗi, trong giới hạn thời gian xác định. Ví dụ:
Cache-Control: public, max-age=60,
stale-while-revalidate=30, stale-if-error=600
Sau 60 giây, bản lưu đã stale. Nếu origin lỗi trong cửa sổ stale-if-error=600, cache có thể tiếp tục trả bản thành công cũ thay vì truyền hard error tới người dùng. RFC nêu các trường hợp như lỗi server, DNS hoặc kết nối; nhà cung cấp CDN có thể chỉ hỗ trợ một tập điều kiện cụ thể. Tài liệu Origin Cache Control của Cloudflare, chẳng hạn, mô tả các mã 500, 502, 503 và 504 cho hành vi này và nêu một số giới hạn theo sản phẩm.
SWR tối ưu độ trễ; SIE tối ưu tính sẵn sàng. Chúng có thể xuất hiện trong cùng header nhưng giải quyết hai thời điểm khác nhau.
4. Chọn TTL bằng ngân sách độ cũ, không bằng cảm tính
Ba con số trong header tạo ra một hợp đồng về mức cũ tối đa. Hãy bắt đầu từ hậu quả nghiệp vụ nếu người dùng nhìn thấy dữ liệu cũ:
- Tài sản tĩnh có tên phiên bản: có thể cache dài; khi nội dung đổi thì URL đổi.
- Trang bài viết hoặc danh mục công khai: thường chấp nhận vài chục giây làm mới nền và một khoảng fallback có giới hạn.
- Giá, tồn kho, trạng thái đơn hàng: cửa sổ phải bám sát cam kết nghiệp vụ; có trường hợp không nên dùng stale.
- Quyền truy cập, số dư, mã dùng một lần: không phục vụ bản cũ chỉ để tránh lỗi. Tính đúng quan trọng hơn availability giả tạo.
Nếu đặt max-age=300 và stale-while-revalidate=300, hệ thống phải chấp nhận khả năng một người dùng nhận nội dung cũ gần 10 phút tính từ lần validation trước. stale-if-error dài một ngày là một quyết định sản phẩm và vận hành, không chỉ là tối ưu kỹ thuật.
5. Đừng làm rò dữ liệu cá nhân qua shared cache
public cho phép shared cache lưu phản hồi trong những điều kiện phù hợp. Không gắn nó vào response cá nhân hóa nếu cache key chưa bao phủ đúng biến thể. Cookie, Authorization, ngôn ngữ, tenant và feature flag đều có thể làm hai người dùng cần hai body khác nhau.
- Dùng
privatekhi phản hồi chỉ dành cho cache riêng của người dùng. - Dùng
no-storecho dữ liệu không được phép lưu. - Kiểm tra
Varyvà cache key của CDN, nhưng tránh tạo biến thể không giới hạn. - Không coi cache header là cơ chế authorization.
- Không cache response có
Set-Cookieở shared cache nếu chưa hiểu rõ chính sách nhà cung cấp.
RFC 9111 cũng quy định các chỉ thị có thể cấm phục vụ stale, như no-cache hoặc must-revalidate trong ngữ cảnh áp dụng. Khi ghép các chỉ thị mâu thuẫn, cache có thể chọn hành vi bảo thủ. Đừng thêm header bằng nhiều lớp cấu hình độc lập mà không kiểm tra phản hồi cuối.
6. Ví dụ cấu hình ở ứng dụng và reverse proxy
Ví dụ PHP minh họa cho một trang nội dung công khai:
header('Cache-Control: public, max-age=60, stale-while-revalidate=30, stale-if-error=600');
header('ETag: "'.$contentHash.'"');
Với Nginx, có thể đặt header tại location phù hợp, nhưng cần xác nhận ứng dụng hoặc CDN không ghi đè:
location /public-content/ {
add_header Cache-Control "public, max-age=60, stale-while-revalidate=30, stale-if-error=600" always;
proxy_pass http://app_backend;
}
Đây chỉ là cấu hình minh họa. Việc Nginx có tự cache hay không phụ thuộc cấu hình proxy cache riêng; thêm header cho client/CDN không tự biến Nginx thành cache. Tương tự, CDN có thể có Edge TTL hoặc Cache Rule khác với TTL trình duyệt.
7. Quy trình kiểm thử không dựa vào một lần curl
- Chọn URL công khai, dữ liệu thử và một cửa sổ TTL ngắn trên môi trường kiểm thử.
- Gửi request đầu để tạo cache; ghi
Date,Age,ETagvà header trạng thái cache của nhà cung cấp. - Request lại khi còn fresh và xác nhận origin không bị gọi ngoài dự kiến.
- Chờ quá
max-agenhưng còn trong SWR; đo latency và quan sát revalidation. - Thay nội dung ở origin, kiểm tra request sau nhận bản mới trong thời gian chấp nhận.
- Mô phỏng lỗi origin có kiểm soát, kiểm tra SIE trả bản cũ đúng cửa sổ rồi ngừng sau khi hết hạn.
- Purge cache và xác nhận runbook phục hồi hoạt động; không thử gây lỗi trực tiếp trên production.
curl -sS -D - -o /dev/null https://example.test/public-content/article
# Quan sát các header liên quan nếu có:
# Cache-Control, Age, ETag, Cache-Status hoặc header riêng của CDN
RFC 9211 định nghĩa Cache-Status, nhưng không phải mọi nhà cung cấp đều dùng header chuẩn này. Một chuỗi HIT/MISS riêng của CDN chỉ là bằng chứng cho tầng đó; hãy kết hợp log origin, request ID và metric cache-hit để hiểu toàn bộ đường đi.
8. Checklist đưa stale caching vào production
- Đã phân loại URL công khai, riêng tư và không được lưu.
- Đã ghi rõ freshness lifetime và mức cũ tối đa được nghiệp vụ chấp nhận.
- Đã có validator như ETag hoặc Last-Modified khi phù hợp.
- Đã kiểm tra header cuối sau reverse proxy/CDN, không chỉ header từ ứng dụng.
- Đã thử revalidation, origin timeout, 5xx, DNS/network failure theo khả năng nền tảng.
- Đã quan sát Age, hit ratio, origin request rate và số lần phục vụ stale.
- Đã có purge, rollback và cách tắt stale nhanh khi nội dung sai.
Stale caching hiệu quả khi “cũ” là một trạng thái có ngân sách và có thể quan sát. Hãy bắt đầu với nội dung công khai rủi ro thấp, đo hành vi ở đúng CDN đang dùng, rồi mới mở rộng. Tốc độ và khả năng chịu lỗi chỉ có giá trị khi không đánh đổi tính đúng và quyền riêng tư.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.