Ngoài Russian doll caching, có more techniques speed up performance Rails apps. Lần này xem Rails built-in conditional GET support, cho phép store rendered pages trong user browser cache.
👋 Nếu thích đọc thêm performance ngoài caching, chúng tôi viết nhiều về Ruby (on Rails) performance, check Ruby performance monitoring checklist.
Etag và Last-Modified headers
Khi browser execute HTTP GET request cho page trong Rails app, router link đến controller action. Controller request required data từ database và render view. HTTP response (với 200 OK response code) sent back browser với rendered HTML từ view trong response body cho browser parse và display.
Khi resource requested again, go through same pipeline. Some situations, unnecessary vì page không change meantime. HTTP offer ETag và Last-Modified headers. Dùng these, browser store response body, dùng headers invalidate khi stale.
Etags, hoặc Entity tags, dùng cho client-side cache validation, think of them as cache keys cho HTTP responses. Chúng passed back browser trong HTTP response header every request.
~ $ curl -I https://localhost:3000/products/1
HTTP/1.1 200 OK
...
ETag: W/"9462d76cc55aeb6249fa990e39231c7c"
Last-Modified: Wed, 25 Apr 2018 08:27:04 GMT
...
Nếu response repeated later, browser find existing response in cache và dùng stored Etag từ last request as If-None-Match header. Header này tell Rails app đã có version này in cache.
Nếu Etag từ request match current one, Rails send 304 Not Modified response without response body. Tell browser dùng version từ local cache instead.
~ $ curl -i -H 'If-None-Match: W/"9462d76cc55aeb6249fa990e39231c7c"' https://localhost:3000/products/1
HTTP/1.1 304 Not Modified
...
ETag: W/"9462d76cc55aeb6249fa990e39231c7c"
Last-Modified: Wed, 25 Apr 2018 08:27:04 GMT
...
Conditional GET requests trong Rails
Request page từ local Rails app, thấy Rails add Etag cho each request automatically. Request same page few times in row, thấy Etag change mỗi request.
Mặc dù Rails generate Etag cho each request by default, dùng digest của whole response body generate it. Nghĩa là <%= csrf_meta_tags %> trong layout throw Etag off, vì csrf-token meta tag change each request. Vì change body each request, Etag invalidated và local cache marked stale.
Bên cạnh đó, Rails never return 304 Not Modified by default, vì local cache never explicitly marked fresh trong controller.
fresh_when và stale?
Dùng Etags từ request header cho conditional GETs, cần explicitly mark object in local cache as "fresh". Ví dụ, page show product, keep cache fresh miễn là product và view templates không change. Làm work, làm 2 things.
- Explicitly set values make up Etag, vì dùng whole response body require render whole body check cached response valid, negate speedup từ caching page locally.
- Compare Etag từ request header với predicted one before render view, omit rendering nếu match.
Rails có helpers làm everything cho chúng ta. Explicitly base Etag và Last-Modified date trên product dùng fresh_when.
# app/views/products/show.html.erb
def show
@product = Product.find(params[:id])
fresh_when @product
end
Nếu có explicit respond_to block, dùng stale? thay fresh_when.
# app/views/products/show.html.erb
def show
@product = Product.find(params[:id])
if stale?(@product)
respond_to do |format|
format.html
end
end
end
Giờ request product page sẽ cache response locally. Subsequent requests same page include Etag tell Rails có cached response, compared với new Etag. Nếu match, Rails skip rendering page, return 304 Not Modified immediately.
Note: Refresh page luôn request uncached version. Test conditional GETs work, navigate away dùng link hoặc back button thay vì refresh.
Bạn like bài này và previous articles AppSignal Academy series như thế nào? Chúng tôi có more articles caching Rails lined up, nhưng đừng ngần ngại let us know what you'd like us to write about (caching-related hoặc otherwise) next!