Skip to content

Commit ccf59d6

Browse files
authored
Merge pull request rails#54586 from byroot/local-store-fetch-multi-recorded-miss
Fix LocalStore#read_multi_entries to distinguish recorded misses
2 parents 7df1b84 + 230a435 commit ccf59d6

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

activesupport/lib/active_support/cache/strategy/local_cache.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,40 @@ def decrement(name, amount = 1, **options) # :nodoc:
108108
value
109109
end
110110

111+
def fetch_multi(*names, &block) # :nodoc:
112+
return super if local_cache.nil? || names.empty?
113+
114+
options = names.extract_options!
115+
options = merged_options(options)
116+
117+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
118+
119+
local_entries = local_cache.read_multi_entries(keys_to_names.keys)
120+
results = local_entries.each_with_object({}) do |(key, value), result|
121+
# If we recorded a miss in the local cache, `#fetch_multi` will forward
122+
# that key to the real store, and the entry will be replaced
123+
# local_cache.delete_entry(key)
124+
next if value.nil?
125+
126+
entry = deserialize_entry(value, **options)
127+
128+
normalized_key = keys_to_names[key]
129+
if entry.nil?
130+
result[normalized_key] = nil
131+
elsif entry.expired? || entry.mismatched?(normalize_version(normalized_key, options))
132+
local_cache.delete_entry(key)
133+
else
134+
result[normalized_key] = entry.value
135+
end
136+
end
137+
138+
if results.size < names.size
139+
results.merge!(super(*(names - results.keys), options, &block))
140+
end
141+
142+
results
143+
end
144+
111145
private
112146
def read_serialized_entry(key, raw: false, **options)
113147
if cache = local_cache
@@ -131,6 +165,8 @@ def read_multi_entries(names, **options)
131165
local_entries = local_cache.read_multi_entries(keys_to_names.keys)
132166

133167
results = local_entries.each_with_object({}) do |(key, value), result|
168+
next if value.nil? # recorded cache miss
169+
134170
entry = deserialize_entry(value, **options)
135171

136172
normalized_key = keys_to_names[key]

activesupport/test/cache/behaviors/local_cache_behavior.rb

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ def test_local_cache_fetch
124124
end
125125
end
126126

127+
def test_local_cache_fetch_on_miss
128+
key = SecureRandom.uuid
129+
@cache.with_local_cache do
130+
assert_equal false, @cache.exist?(key)
131+
value = @cache.fetch(key) { "fetch-yielded" }
132+
assert_equal "fetch-yielded", value
133+
134+
assert_equal "fetch-yielded", @peek.read(key)
135+
end
136+
end
137+
127138
def test_local_cache_of_write_nil
128139
key = SecureRandom.uuid
129140
value = SecureRandom.alphanumeric
@@ -215,14 +226,24 @@ def test_local_cache_of_decrement
215226
end
216227

217228
def test_local_cache_of_fetch_multi
218-
key = SecureRandom.uuid
219-
other_key = SecureRandom.uuid
229+
existing_key = SecureRandom.uuid
230+
known_missing_key = SecureRandom.uuid
231+
unknown_key = SecureRandom.uuid
232+
220233
@cache.with_local_cache do
221-
@cache.fetch_multi(key, other_key) { |_key| true }
222-
@peek.delete(key)
223-
@peek.delete(other_key)
224-
assert_equal true, @cache.read(key)
225-
assert_equal true, @cache.read(other_key)
234+
@cache.fetch(existing_key) { "exist" }
235+
assert_equal false, @cache.exist?("known-missing")
236+
237+
results = @cache.fetch_multi(known_missing_key, existing_key, unknown_key) { "fetch-yielded" }
238+
expected = {
239+
known_missing_key => "fetch-yielded",
240+
existing_key => "exist",
241+
unknown_key => "fetch-yielded",
242+
}
243+
assert_equal(expected, results)
244+
245+
results = @peek.read_multi(known_missing_key, existing_key, unknown_key)
246+
assert_equal expected, results
226247
end
227248
end
228249

activesupport/test/cache/stores/null_store_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def test_local_store_repeated_reads
8585
assert_nil @cache.read("foo")
8686

8787
@cache.read_multi("foo", "bar")
88-
assert_equal({ "foo" => nil, "bar" => nil }, @cache.read_multi("foo", "bar"))
88+
assert_equal({}, @cache.read_multi("foo", "bar"))
8989
end
9090
end
9191
end

0 commit comments

Comments
 (0)