Posted 27 days ago. Visible to the public. Repeats.

Careful: `fresh_when last_modified: ...` without an object does not generate an E-Tag

To allow HTTP 304 responses Archive , Rails offers the fresh_when Archive method for controllers.

The most common way is to pass an ActiveRecord instance or scope, and fresh_when will set fitting E-Tag and Last-Modified headers for you. For scopes, an extra query is sent to the database.

Copy
fresh_when @users

If you do not want that magic to happen, e.g. because your scope is expensive to resolve, you can not use fresh_when like above.

You might come up with some way of knowing the timestamp, like keeping the latest update timestamp in a separate place that is inexpensive to read from, and now just want to pass that timestamp to fresh_when.

The problem

It might seem simple enough to just say:

Copy
updated_at = Rails.cache.fetch('updated_at_of_expensive_scope') fresh_when last_modified: updated_at

The example above calls fresh_when without an object, so Rails will only set the Last-Modified header and not generate an E-Tag.
However, the Last-Modified header's resolution is using only seconds Archive . If your timestamp holds milliseconds, they are discarded.

While that is fine for humans, automated tests may fail because of that: If two tests run within the same second frame, your application's response will have the same Last-Modified header value, even when both tests actually have different data.

While test browsers usually clear all data (cookies, LocalStorage, etc.), at least Chrome seems to keep its HTTP cache and your tests will fail "randomly" (= when they happen within a second).

How to fix it

You can probably start your test browser in incognito mode to fix that.

However, the root cause is that your application claims that two different responses are the same.
The proper solution is to pass your high resolution timestamp as the object to fresh_when and omit the last_modified option.

Copy
updated_at = Rails.cache.fetch('updated_at_of_expensive_scope') fresh_when updated_at

Rails will generate an E-Tag header from that and all will be well.

Your development team has a full backlog of feature requests, chores and refactoring coupled with deadlines? We are familiar with that. With our "DevOps as a Service" offering, we support developer teams with infrastructure and operations expertise.

Owner of this card:

Avatar
Arne Hartherz
Last edit:
21 days ago
by Arne Hartherz
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Arne Hartherz to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more