skip to Main Content

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


  1. When you do

    Post.include(:tags).where("post_status = 1").order("id desc")
    

    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:

    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").all.to_a
    end
    

    Instead, which makes ActiveRecord actually populate @posts with table data.

    Login or Signup to reply.
  2. 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:

    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").all.to_a
    end
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search