aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2017-05-18 18:12:32 +0200
committerGitHub <noreply@github.com>2017-05-18 18:12:32 +0200
commit75fa8dd309a84e125b59d01bf182d88419631eaa (patch)
treed76aa54a79b7bf07d6e6c5d3f6c07237e5f4af60 /actionview
parent385d9af299fbfac7f063de214d371545bafef5df (diff)
downloadrails-75fa8dd309a84e125b59d01bf182d88419631eaa.tar.gz
rails-75fa8dd309a84e125b59d01bf182d88419631eaa.tar.bz2
rails-75fa8dd309a84e125b59d01bf182d88419631eaa.zip
Use recyclable cache keys (#29092)
Diffstat (limited to 'actionview')
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb26
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb2
-rw-r--r--actionview/test/activerecord/relation_cache_test.rb2
-rw-r--r--actionview/test/template/log_subscriber_test.rb2
-rw-r--r--actionview/test/template/render_test.rb6
5 files changed, 23 insertions, 15 deletions
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 15ab7e304f..c3aecadcd6 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -8,10 +8,9 @@ module ActionView
# fragments, and so on. This method takes a block that contains
# the content you wish to cache.
#
- # The best way to use this is by doing key-based cache expiration
- # on top of a cache store like Memcached that'll automatically
- # kick out old entries. For more on key-based expiration, see:
- # http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works
+ # The best way to use this is by doing recyclable key-based cache expiration
+ # on top of a cache store like Memcached or Redis that'll automatically
+ # kick out old entries.
#
# When using this method, you list the cache dependency as the name of the cache, like so:
#
@@ -23,10 +22,14 @@ module ActionView
# This approach will assume that when a new topic is added, you'll touch
# the project. The cache key generated from this call will be something like:
#
- # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
- # ^class ^id ^updated_at ^template tree digest
+ # views/template/action.html.erb:7a1156131a6928cb0026877f8b749ac9/projects/123
+ # ^template path ^template tree digest ^class ^id
#
- # The cache is thus automatically bumped whenever the project updated_at is touched.
+ # This cache key is stable, but it's combined with a cache version derived from the project
+ # record. When the project updated_at is touched, the #cache_version changes, even
+ # if the key stays stable. This means that unlike a traditional key-based cache expiration
+ # approach, you won't be generating cache trash, unused keys, simply because the dependent
+ # record is updated.
#
# If your template cache depends on multiple sources (try to avoid this to keep things simple),
# you can name all these dependencies as part of an array:
@@ -217,10 +220,15 @@ module ActionView
def fragment_name_with_digest(name, virtual_path)
virtual_path ||= @virtual_path
+
if virtual_path
name = controller.url_for(name).split("://").last if name.is_a?(Hash)
- digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
- [ name, digest ]
+
+ if digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies).presence
+ [ "#{virtual_path}:#{digest}", name ]
+ else
+ [ virtual_path, name ]
+ end
else
name
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index 1fbe209200..847256ac78 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -38,7 +38,7 @@ module ActionView
end
def expanded_cache_key(key)
- key = @view.fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
+ key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
end
diff --git a/actionview/test/activerecord/relation_cache_test.rb b/actionview/test/activerecord/relation_cache_test.rb
index 43f7242ee9..fbab512c41 100644
--- a/actionview/test/activerecord/relation_cache_test.rb
+++ b/actionview/test/activerecord/relation_cache_test.rb
@@ -10,7 +10,7 @@ class RelationCacheTest < ActionView::TestCase
def test_cache_relation_other
cache(Project.all) { concat("Hello World") }
- assert_equal "Hello World", controller.cache_store.read("views/projects-#{Project.count}/")
+ assert_equal "Hello World", controller.cache_store.read("views/path/projects-#{Project.count}")
end
def view_cache_dependencies; end
diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb
index 7f358add7e..584666d54b 100644
--- a/actionview/test/template/log_subscriber_test.rb
+++ b/actionview/test/template/log_subscriber_test.rb
@@ -39,7 +39,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def set_view_cache_dependencies
def @view.view_cache_dependencies; []; end
- def @view.fragment_cache_key(*); "ahoy `controller` dependency"; end
+ def @view.combined_fragment_cache_key(*); "ahoy `controller` dependency"; end
end
def test_render_file_template
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 3f66ab3ed3..fef78807d1 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -10,8 +10,8 @@ module RenderTestCases
@view = Class.new(ActionView::Base) do
def view_cache_dependencies; end
- def fragment_cache_key(key)
- ActiveSupport::Cache.expand_cache_key(key, :views)
+ def combined_fragment_cache_key(key)
+ [ :views, key ]
end
end.new(paths, @assigns)
@@ -718,6 +718,6 @@ class CachedCollectionViewRenderTest < ActiveSupport::TestCase
private
def cache_key(*names, virtual_path)
digest = ActionView::Digestor.digest name: virtual_path, finder: @view.lookup_context, dependencies: []
- @view.fragment_cache_key([ *names, digest ])
+ @view.combined_fragment_cache_key([ "#{virtual_path}:#{digest}", *names ])
end
end