All threads / `fresh_when` usage for dynamic queries

Ask A Question


You’re not receiving notifications from this thread.

`fresh_when` usage for dynamic queries

Nicolas Brousse asked in Rails


I'm recently looking to use fresh_when method on my PostController.

class PostsController < ApplicationController
  def index
    @posts = Post.published.order(published_at: :desc)
    fresh_when @posts

  def show
    @post = Post.published.find(params[:id])
    fresh_when @post
class Post < ApplicationRecord
  scope :published, -> { where("published_at IS NOT NULL AND published_at <= ?", }
  scope :unpublished, -> { where("published_at IS NULL OR published_at > ?", }

All is good for the show method, ETag and Last-Modified headers looks good.

But it's not the case for the index method. Each request the ETag is not the same.

I took a look at Rails code and I discover that the ETag generation is done by generate_strong_etag who call a bit later retrieve_cache_key.
In index action @posts return an ActiveRecord::Relation object who reponds to cache_key method. And this method return a key based on the sql request.
So because of the usage, all requests are different ...

I resolve the problem by doing this:

fresh_when @posts, etag: @posts.to_a

But I'm not sure if it is the best way to do this.
That's why I asking you here :)

Hi Nicolas,

What version of Rails are you using? It appears that in Rails 5 you can now start using fresh_when or stale? for collections, see:

Hi Jacob,

I'm using Rails 5.0.2. Yes, I saw in their doc that they speak about using fresh_when for collections:

With the example of your link, it works because Post.all will always generate the same query. And by checking how the Rails code works, I saw they call cache_key method who generated a string key in function of the SQL query.

In my case, because of, the SQL query is always different. So, the ETag too...

That's why I don't know if it is a problem of usage or a problem onto Rails code.

OH! I'm tracking now - one way you could find out for sure is to set a fixed time on your scopes. So instead of using, just use a fixed time and see if you're getting the proper cached response or a fresh query. If the same, then you know that your use of is causing a new key to be generated.

I tried and yes if I use a static Time the query stay the so ETag too. That's how I discovered why my ETag was always different.

But anyway if I have a query without something variable like my it mean that if I update a Post the ETag will stay the same. And that's not logic.

The etag wouldn't stay the same. If you remove and modify one of the records, it will not return a 304 Not Modified status which is signifying that a record has changed and so you need to refresh the page.

I'm not sure what your intended use is, but I think a little bit of restructuring would give you the results you want. If you were to add a new boolean for published, you could let that be your query instead. So your published scope could be published: true instead of published_at <=

Outside of that, I'm not sure what kind of edge cases may arise by using the array as your etag - it may be a perfectly viable solution, I'm jut not sure though.

@Nicolas Brousse I think your solution it correct, that's how I do it, too. You even don't need to pass the record, if you pass the etag option. I would write it like so:

fresh_when etag: @posts.to_a
Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 35,699+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.