Skip to content

Fix regression, enforce fresh ETag header after collection contents change#38119

Merged
rafaelfranca merged 1 commit intorails:masterfrom
alipman88:enforce_fresh_etag_after_collection_changes
Jan 3, 2020
Merged

Fix regression, enforce fresh ETag header after collection contents change#38119
rafaelfranca merged 1 commit intorails:masterfrom
alipman88:enforce_fresh_etag_after_collection_changes

Conversation

@alipman88
Copy link
Contributor

@alipman88 alipman88 commented Dec 29, 2019

Add ActiveRecord::Relation#cache_key_with_version. This method will be used by ActionController::ConditionalGet to ensure that when collection cache versioning is enabled, requests using ConditionalGet don't return the same ETag header after a collection is modified.

Prior to the introduction of collection cache versioning in 4f2ac80, ActiveRecord::Relation#cache_key was suitable for the purposes of ConditionalGet as all cache keys included a version. However, with collection cache versioning enabled, keys remain constant. In turn, ETag headers remain constant, rendering them ineffective.

This commit takes the cache_key_with_version logic used for individual Active Record objects (from aa8749e), abstracts it into a module, and applies it to both individual Active Record objects and collections.

Fixes #38078

@alipman88 alipman88 force-pushed the enforce_fresh_etag_after_collection_changes branch from bd56147 to e05cc0c Compare December 29, 2019 20:02
@alipman88
Copy link
Contributor Author

alipman88 commented Dec 29, 2019

Note that this commit adds a few integration tests to actionpack/test/controller/api/conditional_get_test.rb. Adding these tests feels prudent, as they would have prevented the regression introduced in 4f2ac80. However, as a relatively new Rails contributor, I'm not exactly certain where tests that involve multiple components should live within the Rails test suite.

If my test infringes on any Rails conventions, any feedback on how to better meet said conventions is appreciated!

@alipman88 alipman88 marked this pull request as ready for review December 29, 2019 20:39
@alipman88 alipman88 changed the title Enforce fresh ETag header after collection changes Fix regression, enforce fresh ETag header after collection contents change Dec 29, 2019
@alipman88 alipman88 force-pushed the enforce_fresh_etag_after_collection_changes branch from e05cc0c to b0f3d42 Compare December 30, 2019 16:09
@le0pard
Copy link
Contributor

le0pard commented Jan 1, 2020

Thanks @alipman88

#38078 (comment)

Should you add fix for collection cache_key in this PR or it should be separate? As you remember, AR relation cache_key (with ActiveRecord::Base.collection_cache_versioning = true) still doing SQL for cache_version, which is not used for generation of a key.

@alipman88
Copy link
Contributor Author

I submitted the PR eliminating the unnecessary query separately - #38099.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action Pack tests can't depend on Active Record. If you really need to test this behavior in a controller you will need to write a test in the railtie folder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification. I've removed the Action Pack tests. (I'm ambivalent on whether there really needs to be a test for this. Now that cache_key and cache_version have been split into separate methods, I expect this interface will remain stable.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to copy this method to relation than adding a new module just for this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable, rebased accordingly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You name needs to be in a different line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased accordingly - but the Rails guide seems to indicate that a single paragraph is appropriate:

Your name can be added directly after the last word if there are no code examples or multiple paragraphs. Otherwise, it's best to make a new paragraph.

https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#updating-the-changelog

@alipman88 alipman88 force-pushed the enforce_fresh_etag_after_collection_changes branch from b0f3d42 to f6c82e0 Compare January 3, 2020 01:44
Add ActiveRecord::Relation#cache_key_with_version. This method will be
used by ActionController::ConditionalGet to ensure that when collection
cache versioning is enabled, requests using ConditionalGet don't return
the same ETag header after a collection is modified.

Prior to the introduction of collection cache versioning in
4f2ac80, all collection cache keys
included a version. However, with cache versioning enabled, collection
cache keys remain constant. In turn, ETag headers remain constant,
rendering them ineffective.

This commit takes the cache_key_with_version method used for individual
Active Record objects (from aa8749e),
and adds it to collections.
@alipman88 alipman88 force-pushed the enforce_fresh_etag_after_collection_changes branch from f6c82e0 to 58b0409 Compare January 3, 2020 02:11
@rafaelfranca rafaelfranca merged commit 44af339 into rails:master Jan 3, 2020
rafaelfranca added a commit that referenced this pull request Jan 3, 2020
…lection_changes

Fix regression, enforce fresh ETag header after collection contents change
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cache_key for AR relations is broken for usage in ConditionalGet API

3 participants