When I make the following call in my controller’s action, I can see the first time I load the page I see the output “posts hitting the db” in console output.
cache_key = "posts"
@posts = Rails.cache.fetch(cache_key, expires_in: 5.minutes) do
puts "posts hitting the db"
Post.include(:tags).where("post_status = 1").order("id desc")
end
If I reload the page, I don’t see the “posts hitting the db” message but I can still see the queries like:
Processing by PostsController#index as HTML Rendering
posts/index.html.erb within layouts/main Post Load (0.5ms) SELECT
“posts”.* FROM “posts” WHERE (post_status = 1) ORDER BY id desc ↳
app/views/posts/index.html.erb:6 Label Load (0.3ms) SELECT “tags”.*
FROM “tags” WHERE “tags”.”id” = $1 [[“id”, 7]] ↳
app/views/posts/index.html.erb:6 Rendered posts/index.html.erb
within layouts/posts (31.4ms) Completed 200 OK in 87ms (Views: 62.6ms
| ActiveRecord: 8.2ms)
Is this because it is caching the @posts but since the @posts object wasn’t actually used it didn’t even make the db call? So when the view page does the @posts.each then it hits the db?
For example, if I remove all my html in my view page then it doesn’t hit the db at all.
2
Answers
When you do
You aren’t actually calling the database. You are creating a ActiveRecord scope — so that is what you are caching. If you want to cache the results of the database call, you need to do something like:
Instead, which makes ActiveRecord actually populate @posts with table data.
As @bkimble said you need to add
.all
to turn your query into a database call. However, since Rails 4,.all
is executed lazily. As I explained in this Memcache guide for Rails, you need to force its execution, e.g., by adding.to_a
which converts the result into an array.Your code would then look like: