12/08/2018, 14:17

Tăng tốc Rails với data model cache sử dụng gem IdentityCache

1. Giới thiệu Khi model ngày càng trở nên phức tạp, và yêu cầu cho response data từ controller hay API vẫn phải đảm bảo trong 1 giới hạn thời gian cho phép, giải pháp dễ dàng nhất là thực hiện preload, sử dụng includes để giảm bớt một số lượng lớn câu SQL. Tuy nhiên, model lại tiếp tục phức ...

1. Giới thiệu

Khi model ngày càng trở nên phức tạp, và yêu cầu cho response data từ controller hay API vẫn phải đảm bảo trong 1 giới hạn thời gian cho phép, giải pháp dễ dàng nhất là thực hiện preload, sử dụng includes để giảm bớt một số lượng lớn câu SQL.

Tuy nhiên, model lại tiếp tục phức tạp hơn, preload không còn đáp ứng được, tới đây ta nghĩ đến cache response cho mỗi request. Nhưng phương pháp cache này sẽ làm mất các object includes.

Từ đó, với mong muốn vẫn cache model, vẫn giữ các includes và cung cấp khả năng xử lý các object thông qua cache, gem IdentityCache sẽ đáp ứng các khả năng trên.

2. Model cache với IdentityCache

1. Cài đặt và cấu hình

Thêm vào trong gem file và chạy bundle

gem 'identity_cache'

Thêm config trong file môi trường:

config.identity_cache_store = :mem_cache_store, Memcached::Rails.new(:servers => ["mem1.server.com"])

2. Cách dùng

IdentityCache thêm hàm fetch_* cho class mà ta dùng để cache với các index đã được định nghĩa trong class.

  • sử dụng fetch_by_(index) với index của 1 field
  • sử dụng fetch_(association) với index của association

Ví dụ: ta muốn lấy các object image của product đã được cache

# định nghĩa model product.rb
# include IdentityCache trong model cần cache
class Product < ActiveRecord::Base
  include IdentityCache

  has_many :images

  cache_has_many :images, :embed => true
end

# get product theo id
@product = Product.fetch(id)

# lấy image từ product, image đã được gắn kèm nên khi product được load thì image cũng được load theo
@images = @product.fetch_images

IdentityCache cho phép tìm kiếm theo các trường khác ngoài id. Bạn có thể tạo nhiều index bằng cách kết hợp nhiều trường:

class Product < ActiveRecord::Base
  include IdentityCache
  cache_index :handle, :unique => true
  cache_index :vendor, :product_type
end

# Lấy product từ cache với index
# Nếu object không nằm trong cache, nó sẽ được tìm kiếm trong db và sẽ tự lưu vào trong cache
product = Product.fetch_by_handle(handle)

products = Product.fetch_by_vendor_and_product_type(vendor, product_type)

Với cache_has_many và cache_has_one, ta có thể dễ dàng thực hiện includes trong cache, khi load parent object thì sẽ thực hiện load toàn bộ object quan hệ với nó với cú pháp fetch_(association) _ vd : @product.fetch_images

Trong trường hợp object không được cache, product và nhưng object quan hệ sẽ được load từ trong db. Sau đó data sẽ được tự động lưu thành 1 key của product trong cache, và những lần request sau thì ta chỉ cần gọi @product.fetch_images để lấy product và image sẽ được load cùng từ cache, không cần phải request đến db.

3. Kết luận

Với IdentityCache, việc sử dụng cache cho model nhưng vẫn có thể dùng preload cho object cache đã trở nên rất đơn giản.

Để có thể tìm hiểu rõ hơn về IdentityCache, bạn có thể dựa theo các nguồn tham khảo bên dưới:

https://github.com/Shopify/identity_cache

https://engineering.shopify.com/17489020-identitycache-improving-performance-one-cached-model-at-a-time

0