Skip to content

Commit 135d3e1

Browse files
committed
[close rails#33907] Error when using "recyclable" cache keys with a store that does not support it
If you are using the "in cache versioning" also known as "recyclable cache keys" the cache store must be aware of this scheme, otherwise you will generate cache entries that never invalidate. This PR adds a check to the initialization process to ensure that if recyclable cache keys are being used via ``` config.active_record.cache_versioning = true ``` Then the cache store needs to show that it supports this versioning scheme. Cache stores can let Rails know that they support this scheme by adding a method `supports_in_cache_versioning?` and returning true.
1 parent b45b96b commit 135d3e1

File tree

8 files changed

+75
-0
lines changed

8 files changed

+75
-0
lines changed

activerecord/lib/active_record/railtie.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,29 @@ class Railtie < Rails::Railtie # :nodoc:
8888
end
8989
end
9090

91+
initializer "Check for cache versioning support" do
92+
config.after_initialize do |app|
93+
ActiveSupport.on_load(:active_record) do
94+
if app.config.active_record.cache_versioning && Rails.cache
95+
unless Rails.cache.try(:supports_in_cache_versioning?)
96+
raise <<-end_error
97+
98+
You're using a cache store `#{Rails.cache.class}` that does not support
99+
"recyclable" cache keys, also known as "in cache versioning". To
100+
fix this issue either disable "recyclable" cache keys by setting:
101+
102+
config.active_record.cache_versioning = false
103+
104+
Or switching to a cache store that supports this functionality:
105+
https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
106+
107+
end_error
108+
end
109+
end
110+
end
111+
end
112+
end
113+
91114
initializer "active_record.check_schema_cache_dump" do
92115
if config.active_record.delete(:use_schema_cache_dump)
93116
config.after_initialize do |app|

activesupport/lib/active_support/cache/file_store.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ def initialize(cache_path, options = nil)
2626
@cache_path = cache_path.to_s
2727
end
2828

29+
# Advertise that this cache store can be used
30+
# with "recyclable cache keys" otherwise known
31+
# as cache versioning.
32+
def supports_in_cache_versioning?
33+
true
34+
end
35+
2936
# Deletes all items from the cache. In this case it deletes all the entries in the specified
3037
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
3138
# config file when using +FileStore+ because everything in that directory will be deleted.

activesupport/lib/active_support/cache/mem_cache_store.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def write_entry(key, entry, options)
4747
end
4848
end
4949

50+
# Advertise that this cache store can be used
51+
# with "recyclable cache keys" otherwise known
52+
# as cache versioning.
53+
def supports_in_cache_versioning?
54+
true
55+
end
56+
5057
prepend Strategy::LocalCache
5158
prepend LocalCacheWithRaw
5259

activesupport/lib/active_support/cache/memory_store.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ def initialize(options = nil)
3030
@pruning = false
3131
end
3232

33+
# Advertise that this cache store can be used
34+
# with "recyclable cache keys" otherwise known
35+
# as cache versioning.
36+
def supports_in_cache_versioning?
37+
true
38+
end
39+
3340
# Delete all data stored in a given cache store.
3441
def clear(options = nil)
3542
synchronize do

activesupport/lib/active_support/cache/null_store.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ module Cache
1212
class NullStore < Store
1313
prepend Strategy::LocalCache
1414

15+
# Advertise that this cache store can be used
16+
# with "recyclable cache keys" otherwise known
17+
# as cache versioning.
18+
def supports_in_cache_versioning?
19+
true
20+
end
21+
1522
def clear(options = nil)
1623
end
1724

activesupport/lib/active_support/cache/redis_cache_store.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ class RedisCacheStore < Store
6666
SCAN_BATCH_SIZE = 1000
6767
private_constant :SCAN_BATCH_SIZE
6868

69+
# Advertise that this cache store can be used
70+
# with "recyclable cache keys" otherwise known
71+
# as cache versioning.
72+
def supports_in_cache_versioning?
73+
true
74+
end
75+
6976
# Support raw values in the local cache strategy.
7077
module LocalCacheWithRaw # :nodoc:
7178
private

railties/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Raise an error when "recyclable cache keys" are being used by a cache store
2+
that does not explicitly support it.
3+
4+
*Richard Schneeman*
5+
16
* Support environment specific credentials file.
27

38
For `production` environment look first for `config/credentials/production.yml.enc` file that can be decrypted by

railties/test/application/configuration_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ class MyLogger < ::Logger
124124
assert_equal "MyLogger", Rails.application.config.logger.class.name
125125
end
126126

127+
test "raises an error if cache does not support recyclable cache keys" do
128+
build_app(initializers: true)
129+
add_to_env_config "production", "config.cache_store = Class.new {}.new"
130+
add_to_env_config "production", "config.active_record.cache_versioning = true"
131+
132+
error = assert_raise(RuntimeError) do
133+
app "production"
134+
end
135+
136+
assert_match(/You're using a cache store/, error.message)
137+
end
138+
127139
test "a renders exception on pending migration" do
128140
add_to_config <<-RUBY
129141
config.active_record.migration_error = :page_load

0 commit comments

Comments
 (0)