Active Model Serializer 10 缓存。它似乎没有用。为什么?
Active Model Serializer 10 caching. It doesn't seem to be working. Why?
我的玩具应用程序有几个模型,rental_units 和用户。你可以找到 repo here
我是 运行 Rails 5 和 AMS 10。
active_model_serializers (0.10.0.rc4)
...
rails (5.0.0.beta3)
actioncable (= 5.0.0.beta3)
actionmailer (= 5.0.0.beta3)
actionpack (= 5.0.0.beta3)
...
我有一个如下所示的 RentalUnitSerializer:
class RentalUnitSerializer < ActiveModel::Serializer
cache key: 'rental_unit', expires_in: 3.hours
attributes :id, :rooms, :bathrooms, :price, :price_cents
belongs_to :user
end
这是我的 UserSerializer:
class UserSerializer < ActiveModel::Serializer
cache key: 'user'
attributes :id, :name, :email
has_many :rental_units
def name
names = object.name.split(" ")
"#{names[0].first}. #{names[1]}"
end
end
这是我的 Gemfile 的一部分:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '>= 5.0.0.beta3', '< 5.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma'
gem 'active_model_serializers', '~> 0.10.0.rc1'
gem "dalli"
gem "memcachier"
这是我的 config/environments/development.rb 文件:
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=172800'
}
else
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
end
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
这是奇怪的行为
当我访问 http://localhost:3000/users 时,这是我没有缓存迹象的日志:
Started GET "/users" for ::1 at 2016-03-04 15:18:12 -0500
Processing by UsersController#index as HTML
User Load (0.1ms) SELECT "users".* FROM "users"
[active_model_serializers] RentalUnit Load (0.2ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 1]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 2]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 3]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (11.0ms)
Completed 200 OK in 13ms (Views: 12.5ms | ActiveRecord: 0.5ms)
当我访问 http://localhost:3000/rental_units 时,有一些缓存?
Started GET "/rental_units" for ::1 at 2016-03-04 15:18:37 -0500
Processing by RentalUnitsController#index as HTML
RentalUnit Load (0.4ms) SELECT "rental_units".* FROM "rental_units"
[active_model_serializers] User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (16.1ms)
Completed 200 OK in 21ms (Views: 19.1ms | ActiveRecord: 1.4ms)
这是怎么回事?看起来在后面的日志中,用户正在被缓存?我相信,由于 3 个不同的出租单位属于用户 1,AMS 有一些默认缓存正在进行,其中相同的 SQL 查询只是命中某种缓存。因此,对 User1 的第一个查询会命中服务器的数据库,但对 User1 的后续查询不会。这个缓存在哪里?它在服务器上吗?或者一些网络服务器?
但我认为我的序列化程序中的缓存策略根本不起作用。为什么?
为了帮助调试,请尝试为您的缓存设置 logger
。 Rails缓存支持设置记录器,但MemoryStore
默认不设置。
在初始化程序中,尝试以下操作:
# config/initializers/cache.rb
Rails.cache.logger = Logger.new(STDOUT)
# or Rails.cache.logger = Rails.logger
重新启动您的 Rails 服务器,您应该会看到缓存记录 hits/misses。我怀疑它正在工作,但可能不是您期望的那样。
既然你问了,你的问题实际上涉及到几个缓存层:片段(或视图)缓存和查询缓存。
您已启用 :memory_store
缓存,这将创建一个 ActiveSupport::Cache::MemoryStore
实例,可在您的应用程序中访问,并在 Rails 控制台中作为 Rails.cache
访问。
您还为序列化程序设置了 cache
选项。从您的版本的 ActiveModelSerializer guides 来看,您似乎已正确设置。到目前为止一切顺利。
通常,在 Rails 控制器中,Rails 缓存在您渲染视图之前不会被调用。在您的情况下,您正在渲染 json 视图(通过 AMS);它与 HTML 模板的工作方式相同。
第一次发出此请求时,缓存是冷的,进行了数据库查询,Rails 使用缓存键向缓存请求 JSON 字符串的表示(或钥匙)。由于这组资源的缓存是空的,所以必须生成JSON字符串,然后缓存在Rails.cache
中,然后返回响应。
下次发出此请求时(在到期 window 内),将进行 DB 查询 - Rails 需要知道什么是什么请问资源缓存对吗? - 这一次,缓存命中,字符串从缓存中返回。不需要生成新的 JSON。请注意,这仍然会导致数据库命中以请求原始资源。这些请求对于 AMS 查找您的 JSON.
的缓存键是必要的
您还会在日志中看到 CACHE select ...
查询。正如在另一个答案中提到的,这可能表明您正在一遍又一遍地进行相同的数据库查询(通常表示 N+1 query),因此建议 "eager load" 这些关系。您的 RentalUnit
属于 :user
,因此对于每个出租单元,每个用户都会发出一个单独的请求 - 您可以通过预先加载在一个查询中获取所有这些用户。
Rails 提供了 cache the results of SQL queries 的一些能力。这与我们一直在讨论的您为 AMS 启用的视图缓存是分开的。
我试过以上所有答案都无济于事。最后,我在项目的根目录中结束了 rm -rf ./tmp/cache。
我的玩具应用程序有几个模型,rental_units 和用户。你可以找到 repo here
我是 运行 Rails 5 和 AMS 10。
active_model_serializers (0.10.0.rc4)
...
rails (5.0.0.beta3)
actioncable (= 5.0.0.beta3)
actionmailer (= 5.0.0.beta3)
actionpack (= 5.0.0.beta3)
...
我有一个如下所示的 RentalUnitSerializer:
class RentalUnitSerializer < ActiveModel::Serializer
cache key: 'rental_unit', expires_in: 3.hours
attributes :id, :rooms, :bathrooms, :price, :price_cents
belongs_to :user
end
这是我的 UserSerializer:
class UserSerializer < ActiveModel::Serializer
cache key: 'user'
attributes :id, :name, :email
has_many :rental_units
def name
names = object.name.split(" ")
"#{names[0].first}. #{names[1]}"
end
end
这是我的 Gemfile 的一部分:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '>= 5.0.0.beta3', '< 5.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma'
gem 'active_model_serializers', '~> 0.10.0.rc1'
gem "dalli"
gem "memcachier"
这是我的 config/environments/development.rb 文件:
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=172800'
}
else
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
end
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
这是奇怪的行为
当我访问 http://localhost:3000/users 时,这是我没有缓存迹象的日志:
Started GET "/users" for ::1 at 2016-03-04 15:18:12 -0500
Processing by UsersController#index as HTML
User Load (0.1ms) SELECT "users".* FROM "users"
[active_model_serializers] RentalUnit Load (0.2ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 1]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 2]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 3]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (11.0ms)
Completed 200 OK in 13ms (Views: 12.5ms | ActiveRecord: 0.5ms)
当我访问 http://localhost:3000/rental_units 时,有一些缓存?
Started GET "/rental_units" for ::1 at 2016-03-04 15:18:37 -0500
Processing by RentalUnitsController#index as HTML
RentalUnit Load (0.4ms) SELECT "rental_units".* FROM "rental_units"
[active_model_serializers] User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (16.1ms)
Completed 200 OK in 21ms (Views: 19.1ms | ActiveRecord: 1.4ms)
这是怎么回事?看起来在后面的日志中,用户正在被缓存?我相信,由于 3 个不同的出租单位属于用户 1,AMS 有一些默认缓存正在进行,其中相同的 SQL 查询只是命中某种缓存。因此,对 User1 的第一个查询会命中服务器的数据库,但对 User1 的后续查询不会。这个缓存在哪里?它在服务器上吗?或者一些网络服务器?
但我认为我的序列化程序中的缓存策略根本不起作用。为什么?
为了帮助调试,请尝试为您的缓存设置 logger
。 Rails缓存支持设置记录器,但MemoryStore
默认不设置。
在初始化程序中,尝试以下操作:
# config/initializers/cache.rb
Rails.cache.logger = Logger.new(STDOUT)
# or Rails.cache.logger = Rails.logger
重新启动您的 Rails 服务器,您应该会看到缓存记录 hits/misses。我怀疑它正在工作,但可能不是您期望的那样。
既然你问了,你的问题实际上涉及到几个缓存层:片段(或视图)缓存和查询缓存。
您已启用 :memory_store
缓存,这将创建一个 ActiveSupport::Cache::MemoryStore
实例,可在您的应用程序中访问,并在 Rails 控制台中作为 Rails.cache
访问。
您还为序列化程序设置了 cache
选项。从您的版本的 ActiveModelSerializer guides 来看,您似乎已正确设置。到目前为止一切顺利。
通常,在 Rails 控制器中,Rails 缓存在您渲染视图之前不会被调用。在您的情况下,您正在渲染 json 视图(通过 AMS);它与 HTML 模板的工作方式相同。
第一次发出此请求时,缓存是冷的,进行了数据库查询,Rails 使用缓存键向缓存请求 JSON 字符串的表示(或钥匙)。由于这组资源的缓存是空的,所以必须生成JSON字符串,然后缓存在Rails.cache
中,然后返回响应。
下次发出此请求时(在到期 window 内),将进行 DB 查询 - Rails 需要知道什么是什么请问资源缓存对吗? - 这一次,缓存命中,字符串从缓存中返回。不需要生成新的 JSON。请注意,这仍然会导致数据库命中以请求原始资源。这些请求对于 AMS 查找您的 JSON.
的缓存键是必要的您还会在日志中看到 CACHE select ...
查询。正如在另一个答案中提到的,这可能表明您正在一遍又一遍地进行相同的数据库查询(通常表示 N+1 query),因此建议 "eager load" 这些关系。您的 RentalUnit
属于 :user
,因此对于每个出租单元,每个用户都会发出一个单独的请求 - 您可以通过预先加载在一个查询中获取所有这些用户。
Rails 提供了 cache the results of SQL queries 的一些能力。这与我们一直在讨论的您为 AMS 启用的视图缓存是分开的。
我试过以上所有答案都无济于事。最后,我在项目的根目录中结束了 rm -rf ./tmp/cache。