Skip to content

Commit e8688dd

Browse files
committed
Fix partial caching ignore repeated items issue
This is because we only use hash to maintain the result. So when the key are the same, the result would be skipped. The solution is to maintain an array for tracking every item's position to restructure the result.
1 parent 2c44e22 commit e8688dd

File tree

3 files changed

+43
-13
lines changed

3 files changed

+43
-13
lines changed

actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ def cache_collection_render(instrumentation_payload, view, template)
1717
# Result is a hash with the key represents the
1818
# key used for cache lookup and the value is the item
1919
# on which the partial is being rendered
20-
keyed_collection = collection_by_cache_keys(view, template)
20+
keyed_collection, ordered_keys = collection_by_cache_keys(view, template)
2121

2222
# Pull all partials from cache
2323
# Result is a hash, key matches the entry in
2424
# `keyed_collection` where the cache was retrieved and the
2525
# value is the value that was present in the cache
26-
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
26+
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
2727
instrumentation_payload[:cache_hits] = cached_partials.size
2828

2929
# Extract the items for the keys that are not found
@@ -40,11 +40,15 @@ def cache_collection_render(instrumentation_payload, view, template)
4040
rendered_partials = @collection.empty? ? [] : yield
4141

4242
index = 0
43-
fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
43+
keyed_partials = fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
4444
# This block is called once
4545
# for every cache miss while preserving order.
4646
rendered_partials[index].tap { index += 1 }
4747
end
48+
49+
ordered_keys.map do |key|
50+
keyed_partials[key]
51+
end
4852
end
4953

5054
def callable_cache_key?
@@ -56,8 +60,10 @@ def collection_by_cache_keys(view, template)
5660

5761
digest_path = view.digest_path_from_template(template)
5862

59-
@collection.each_with_object({}) do |item, hash|
60-
hash[expanded_cache_key(seed.call(item), view, template, digest_path)] = item
63+
@collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
64+
key = expanded_cache_key(seed.call(item), view, template, digest_path)
65+
ordered_keys << key
66+
hash[key] = item
6167
end
6268
end
6369

@@ -82,15 +88,16 @@ def expanded_cache_key(key, view, template, digest_path)
8288
# If the partial is not already cached it will also be
8389
# written back to the underlying cache store.
8490
def fetch_or_cache_partial(cached_partials, template, order_by:)
85-
order_by.map do |cache_key|
86-
if content = cached_partials[cache_key]
87-
build_rendered_template(content, template)
88-
else
89-
yield.tap do |rendered_partial|
90-
collection_cache.write(cache_key, rendered_partial.body)
91-
end
91+
order_by.each_with_object({}) do |cache_key, hash|
92+
hash[cache_key] =
93+
if content = cached_partials[cache_key]
94+
build_rendered_template(content, template)
95+
else
96+
yield.tap do |rendered_partial|
97+
collection_cache.write(cache_key, rendered_partial.body)
98+
end
99+
end
92100
end
93-
end
94101
end
95102
end
96103
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= cached_set.first %> | <%= cached_set.second %> | <%= cached_set.third %> | <%= cached_set.fourth %> | <%= cached_set.fifth %>

actionview/test/template/render_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,28 @@ def b.to_partial_path; "test/partial_iteration_2"; end
811811
end
812812
end
813813

814+
test "collection caching with repeated collection" do
815+
sets = [
816+
[1, 2, 3, 4, 5],
817+
[1, 2, 3, 4, 4],
818+
[1, 2, 3, 4, 5],
819+
[1, 2, 3, 4, 4],
820+
[1, 2, 3, 4, 6]
821+
]
822+
823+
result = @view.render(partial: "test/cached_set", collection: sets, cached: true)
824+
825+
splited_result = result.split("\n")
826+
assert_equal 5, splited_result.count
827+
assert_equal [
828+
"1 | 2 | 3 | 4 | 5",
829+
"1 | 2 | 3 | 4 | 4",
830+
"1 | 2 | 3 | 4 | 5",
831+
"1 | 2 | 3 | 4 | 4",
832+
"1 | 2 | 3 | 4 | 6"
833+
], splited_result
834+
end
835+
814836
private
815837
def cache_key(*names, virtual_path)
816838
digest = ActionView::Digestor.digest name: virtual_path, format: :html, finder: @view.lookup_context, dependencies: []

0 commit comments

Comments
 (0)