activesupport 5.2.4.3 → 7.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +244 -459
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -5
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +47 -41
- data/lib/active_support/cache/mem_cache_store.rb +151 -40
- data/lib/active_support/cache/memory_store.rb +68 -34
- data/lib/active_support/cache/null_store.rb +16 -3
- data/lib/active_support/cache/redis_cache_store.rb +103 -101
- data/lib/active_support/cache/strategy/local_cache.rb +56 -64
- data/lib/active_support/cache.rb +333 -116
- data/lib/active_support/callbacks.rb +244 -128
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +72 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -3
- data/lib/active_support/configurable.rb +15 -16
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +15 -7
- data/lib/active_support/core_ext/array/conversions.rb +18 -17
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +2 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +15 -14
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +241 -76
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -4
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +2 -31
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +70 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +3 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +14 -110
- data/lib/active_support/core_ext/object/json.rb +44 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +24 -14
- data/lib/active_support/core_ext/object/with_options.rb +21 -2
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +23 -27
- data/lib/active_support/core_ext/range/conversions.rb +32 -30
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -2
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +46 -7
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +129 -20
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +59 -10
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -22
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +47 -16
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +60 -715
- data/lib/active_support/deprecation/behaviors.rb +21 -5
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- data/lib/active_support/descendants_tracker.rb +190 -34
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +5 -7
- data/lib/active_support/duration/iso8601_serializer.rb +27 -15
- data/lib/active_support/duration.rb +149 -67
- data/lib/active_support/encrypted_configuration.rb +12 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +85 -122
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/hash_with_indifferent_access.rb +73 -43
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +2 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +25 -14
- data/lib/active_support/inflector/methods.rb +38 -71
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +14 -6
- data/lib/active_support/key_generator.rb +23 -38
- data/lib/active_support/lazy_load_hooks.rb +19 -5
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +51 -11
- data/lib/active_support/logger.rb +6 -22
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +45 -10
- data/lib/active_support/message_encryptor.rb +20 -19
- data/lib/active_support/message_verifier.rb +53 -21
- data/lib/active_support/messages/metadata.rb +13 -4
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +17 -76
- data/lib/active_support/multibyte/unicode.rb +7 -331
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +163 -37
- data/lib/active_support/notifications/instrumenter.rb +90 -11
- data/lib/active_support/notifications.rb +88 -30
- data/lib/active_support/number_helper/number_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +36 -12
- data/lib/active_support/option_merger.rb +15 -4
- data/lib/active_support/ordered_hash.rb +2 -2
- data/lib/active_support/ordered_options.rb +14 -4
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +5 -6
- data/lib/active_support/rescuable.rb +8 -8
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +2 -3
- data/lib/active_support/subscriber.rb +79 -46
- data/lib/active_support/tagged_logging.rb +58 -9
- data/lib/active_support/test_case.rb +79 -0
- data/lib/active_support/testing/assertions.rb +62 -11
- data/lib/active_support/testing/deprecation.rb +52 -2
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +4 -4
- data/lib/active_support/testing/method_call_assertions.rb +32 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +4 -7
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +60 -14
- data/lib/active_support/time_with_zone.rb +139 -64
- data/lib/active_support/values/time_zone.rb +66 -30
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -4
- data/lib/active_support/xml_mini/libxml.rb +7 -7
- data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
- data/lib/active_support/xml_mini/nokogiri.rb +6 -6
- data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
- data/lib/active_support/xml_mini/rexml.rb +11 -4
- data/lib/active_support/xml_mini.rb +7 -14
- data/lib/active_support.rb +30 -1
- metadata +64 -35
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -11,18 +11,46 @@ module ActiveSupport
|
|
11
11
|
# to share cache data with each other and this may not be the most
|
12
12
|
# appropriate cache in that scenario.
|
13
13
|
#
|
14
|
-
# This cache has a bounded size specified by the
|
14
|
+
# This cache has a bounded size specified by the +:size+ options to the
|
15
15
|
# initializer (default is 32Mb). When the cache exceeds the allotted size,
|
16
16
|
# a cleanup will occur which tries to prune the cache down to three quarters
|
17
17
|
# of the maximum size by removing the least recently used entries.
|
18
18
|
#
|
19
|
+
# Unlike other Cache store implementations, MemoryStore does not compress
|
20
|
+
# values by default. MemoryStore does not benefit from compression as much
|
21
|
+
# as other Store implementations, as it does not send data over a network.
|
22
|
+
# However, when compression is enabled, it still pays the full cost of
|
23
|
+
# compression in terms of cpu use.
|
24
|
+
#
|
19
25
|
# MemoryStore is thread-safe.
|
20
26
|
class MemoryStore < Store
|
27
|
+
module DupCoder # :nodoc:
|
28
|
+
extend self
|
29
|
+
|
30
|
+
def dump(entry)
|
31
|
+
entry.dup_value! unless entry.compressed?
|
32
|
+
entry
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump_compressed(entry, threshold)
|
36
|
+
entry = entry.compressed(threshold)
|
37
|
+
entry.dup_value! unless entry.compressed?
|
38
|
+
entry
|
39
|
+
end
|
40
|
+
|
41
|
+
def load(entry)
|
42
|
+
entry = entry.dup
|
43
|
+
entry.dup_value!
|
44
|
+
entry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
21
48
|
def initialize(options = nil)
|
22
49
|
options ||= {}
|
50
|
+
# Disable compression by default.
|
51
|
+
options[:compress] ||= false
|
23
52
|
super(options)
|
24
53
|
@data = {}
|
25
|
-
@key_access = {}
|
26
54
|
@max_size = options[:size] || 32.megabytes
|
27
55
|
@max_prune_time = options[:max_prune_time] || 2
|
28
56
|
@cache_size = 0
|
@@ -30,11 +58,15 @@ module ActiveSupport
|
|
30
58
|
@pruning = false
|
31
59
|
end
|
32
60
|
|
61
|
+
# Advertise cache versioning support.
|
62
|
+
def self.supports_cache_versioning?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
33
66
|
# Delete all data stored in a given cache store.
|
34
67
|
def clear(options = nil)
|
35
68
|
synchronize do
|
36
69
|
@data.clear
|
37
|
-
@key_access.clear
|
38
70
|
@cache_size = 0
|
39
71
|
end
|
40
72
|
end
|
@@ -46,7 +78,7 @@ module ActiveSupport
|
|
46
78
|
keys = synchronize { @data.keys }
|
47
79
|
keys.each do |key|
|
48
80
|
entry = @data[key]
|
49
|
-
delete_entry(key, options) if entry && entry.expired?
|
81
|
+
delete_entry(key, **options) if entry && entry.expired?
|
50
82
|
end
|
51
83
|
end
|
52
84
|
end
|
@@ -57,13 +89,13 @@ module ActiveSupport
|
|
57
89
|
return if pruning?
|
58
90
|
@pruning = true
|
59
91
|
begin
|
60
|
-
start_time =
|
92
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
61
93
|
cleanup
|
62
94
|
instrument(:prune, target_size, from: @cache_size) do
|
63
|
-
keys = synchronize { @
|
95
|
+
keys = synchronize { @data.keys }
|
64
96
|
keys.each do |key|
|
65
|
-
delete_entry(key, options)
|
66
|
-
return if @cache_size <= target_size || (max_time &&
|
97
|
+
delete_entry(key, **options)
|
98
|
+
return if @cache_size <= target_size || (max_time && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time > max_time)
|
67
99
|
end
|
68
100
|
end
|
69
101
|
ensure
|
@@ -93,13 +125,13 @@ module ActiveSupport
|
|
93
125
|
matcher = key_matcher(matcher, options)
|
94
126
|
keys = synchronize { @data.keys }
|
95
127
|
keys.each do |key|
|
96
|
-
delete_entry(key, options) if key.match(matcher)
|
128
|
+
delete_entry(key, **options) if key.match(matcher)
|
97
129
|
end
|
98
130
|
end
|
99
131
|
end
|
100
132
|
|
101
133
|
def inspect # :nodoc:
|
102
|
-
"
|
134
|
+
"#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
|
103
135
|
end
|
104
136
|
|
105
137
|
# Synchronize calls to the cache. This should be called wherever the underlying cache implementation
|
@@ -109,54 +141,56 @@ module ActiveSupport
|
|
109
141
|
end
|
110
142
|
|
111
143
|
private
|
112
|
-
|
113
144
|
PER_ENTRY_OVERHEAD = 240
|
114
145
|
|
115
|
-
def
|
116
|
-
|
146
|
+
def default_coder
|
147
|
+
DupCoder
|
117
148
|
end
|
118
149
|
|
119
|
-
def
|
120
|
-
|
150
|
+
def cached_size(key, payload)
|
151
|
+
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
152
|
+
end
|
153
|
+
|
154
|
+
def read_entry(key, **options)
|
155
|
+
entry = nil
|
121
156
|
synchronize do
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
157
|
+
payload = @data.delete(key)
|
158
|
+
if payload
|
159
|
+
@data[key] = payload
|
160
|
+
entry = deserialize_entry(payload)
|
126
161
|
end
|
127
162
|
end
|
128
163
|
entry
|
129
164
|
end
|
130
165
|
|
131
|
-
def write_entry(key, entry, options)
|
132
|
-
entry
|
166
|
+
def write_entry(key, entry, **options)
|
167
|
+
payload = serialize_entry(entry, **options)
|
133
168
|
synchronize do
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
169
|
+
return false if options[:unless_exist] && @data.key?(key)
|
170
|
+
|
171
|
+
old_payload = @data[key]
|
172
|
+
if old_payload
|
173
|
+
@cache_size -= (old_payload.bytesize - payload.bytesize)
|
138
174
|
else
|
139
|
-
@cache_size += cached_size(key,
|
175
|
+
@cache_size += cached_size(key, payload)
|
140
176
|
end
|
141
|
-
@
|
142
|
-
@data[key] = entry
|
177
|
+
@data[key] = payload
|
143
178
|
prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
|
144
179
|
true
|
145
180
|
end
|
146
181
|
end
|
147
182
|
|
148
|
-
def delete_entry(key, options)
|
183
|
+
def delete_entry(key, **options)
|
149
184
|
synchronize do
|
150
|
-
@
|
151
|
-
|
152
|
-
|
153
|
-
!!entry
|
185
|
+
payload = @data.delete(key)
|
186
|
+
@cache_size -= cached_size(key, payload) if payload
|
187
|
+
!!payload
|
154
188
|
end
|
155
189
|
end
|
156
190
|
|
157
191
|
def modify_value(name, amount, options)
|
192
|
+
options = merged_options(options)
|
158
193
|
synchronize do
|
159
|
-
options = merged_options(options)
|
160
194
|
if num = read(name, options)
|
161
195
|
num = num.to_i + amount
|
162
196
|
write(name, num, options)
|
@@ -12,6 +12,11 @@ module ActiveSupport
|
|
12
12
|
class NullStore < Store
|
13
13
|
prepend Strategy::LocalCache
|
14
14
|
|
15
|
+
# Advertise cache versioning support.
|
16
|
+
def self.supports_cache_versioning?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
15
20
|
def clear(options = nil)
|
16
21
|
end
|
17
22
|
|
@@ -28,14 +33,22 @@ module ActiveSupport
|
|
28
33
|
end
|
29
34
|
|
30
35
|
private
|
31
|
-
def read_entry(key,
|
36
|
+
def read_entry(key, **s)
|
37
|
+
deserialize_entry(read_serialized_entry(key))
|
38
|
+
end
|
39
|
+
|
40
|
+
def read_serialized_entry(_key, **)
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_entry(key, entry, **)
|
44
|
+
write_serialized_entry(key, serialize_entry(entry))
|
32
45
|
end
|
33
46
|
|
34
|
-
def
|
47
|
+
def write_serialized_entry(_key, _payload, **)
|
35
48
|
true
|
36
49
|
end
|
37
50
|
|
38
|
-
def delete_entry(key, options)
|
51
|
+
def delete_entry(key, **options)
|
39
52
|
false
|
40
53
|
end
|
41
54
|
end
|
@@ -15,9 +15,7 @@ begin
|
|
15
15
|
rescue LoadError
|
16
16
|
end
|
17
17
|
|
18
|
-
require "digest
|
19
|
-
require "active_support/core_ext/marshal"
|
20
|
-
require "active_support/core_ext/hash/transform_values"
|
18
|
+
require "active_support/digest"
|
21
19
|
|
22
20
|
module ActiveSupport
|
23
21
|
module Cache
|
@@ -47,7 +45,7 @@ module ActiveSupport
|
|
47
45
|
# 4.0.1+ for distributed mget support.
|
48
46
|
# * +delete_matched+ support for Redis KEYS globs.
|
49
47
|
class RedisCacheStore < Store
|
50
|
-
# Keys are truncated with
|
48
|
+
# Keys are truncated with the ActiveSupport digest if they exceed 1kB
|
51
49
|
MAX_KEY_BYTESIZE = 1024
|
52
50
|
|
53
51
|
DEFAULT_REDIS_OPTIONS = {
|
@@ -67,35 +65,12 @@ module ActiveSupport
|
|
67
65
|
SCAN_BATCH_SIZE = 1000
|
68
66
|
private_constant :SCAN_BATCH_SIZE
|
69
67
|
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
def write_entry(key, entry, options)
|
74
|
-
if options[:raw] && local_cache
|
75
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
76
|
-
raw_entry.expires_at = entry.expires_at
|
77
|
-
super(key, raw_entry, options)
|
78
|
-
else
|
79
|
-
super
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def write_multi_entries(entries, options)
|
84
|
-
if options[:raw] && local_cache
|
85
|
-
raw_entries = entries.map do |key, entry|
|
86
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
87
|
-
raw_entry.expires_at = entry.expires_at
|
88
|
-
end.to_h
|
89
|
-
|
90
|
-
super(raw_entries, options)
|
91
|
-
else
|
92
|
-
super
|
93
|
-
end
|
94
|
-
end
|
68
|
+
# Advertise cache versioning support.
|
69
|
+
def self.supports_cache_versioning?
|
70
|
+
true
|
95
71
|
end
|
96
72
|
|
97
73
|
prepend Strategy::LocalCache
|
98
|
-
prepend LocalCacheWithRaw
|
99
74
|
|
100
75
|
class << self
|
101
76
|
# Factory method to create a new Redis instance.
|
@@ -109,7 +84,7 @@ module ActiveSupport
|
|
109
84
|
# :url String -> Redis.new(url: …)
|
110
85
|
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
111
86
|
#
|
112
|
-
def build_redis(redis: nil, url: nil, **redis_options)
|
87
|
+
def build_redis(redis: nil, url: nil, **redis_options) # :nodoc:
|
113
88
|
urls = Array(url)
|
114
89
|
|
115
90
|
if redis.is_a?(Proc)
|
@@ -140,15 +115,17 @@ module ActiveSupport
|
|
140
115
|
|
141
116
|
# Creates a new Redis cache store.
|
142
117
|
#
|
143
|
-
# Handles
|
144
|
-
#
|
118
|
+
# Handles four options: :redis block, :redis instance, single :url
|
119
|
+
# string, and multiple :url strings.
|
145
120
|
#
|
146
|
-
#
|
147
|
-
# :
|
148
|
-
# :
|
121
|
+
# Option Class Result
|
122
|
+
# :redis Proc -> options[:redis].call
|
123
|
+
# :redis Object -> options[:redis]
|
124
|
+
# :url String -> Redis.new(url: …)
|
125
|
+
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
149
126
|
#
|
150
127
|
# No namespace is set by default. Provide one if the Redis cache
|
151
|
-
# server is shared with other apps: <tt>namespace: 'myapp-cache'
|
128
|
+
# server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
|
152
129
|
#
|
153
130
|
# Compression is enabled by default with a 1kB threshold, so cached
|
154
131
|
# values larger than 1kB are automatically compressed. Disable by
|
@@ -162,8 +139,8 @@ module ActiveSupport
|
|
162
139
|
#
|
163
140
|
# Race condition TTL is not set by default. This can be used to avoid
|
164
141
|
# "thundering herd" cache writes when hot cache entries are expired.
|
165
|
-
# See
|
166
|
-
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
142
|
+
# See ActiveSupport::Cache::Store#fetch for more.
|
143
|
+
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder: default_coder, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
167
144
|
@redis_options = redis_options
|
168
145
|
|
169
146
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
@@ -171,7 +148,8 @@ module ActiveSupport
|
|
171
148
|
|
172
149
|
super namespace: namespace,
|
173
150
|
compress: compress, compress_threshold: compress_threshold,
|
174
|
-
expires_in: expires_in, race_condition_ttl: race_condition_ttl
|
151
|
+
expires_in: expires_in, race_condition_ttl: race_condition_ttl,
|
152
|
+
coder: coder
|
175
153
|
end
|
176
154
|
|
177
155
|
def redis
|
@@ -189,7 +167,7 @@ module ActiveSupport
|
|
189
167
|
|
190
168
|
def inspect
|
191
169
|
instance = @redis || @redis_options
|
192
|
-
"
|
170
|
+
"#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
|
193
171
|
end
|
194
172
|
|
195
173
|
# Cache Store API implementation.
|
@@ -232,10 +210,14 @@ module ActiveSupport
|
|
232
210
|
pattern = namespace_key(matcher, options)
|
233
211
|
cursor = "0"
|
234
212
|
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
213
|
+
nodes = c.respond_to?(:nodes) ? c.nodes : [c]
|
214
|
+
|
215
|
+
nodes.each do |node|
|
216
|
+
begin
|
217
|
+
cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
|
218
|
+
node.del(*keys) unless keys.empty?
|
219
|
+
end until cursor == "0"
|
220
|
+
end
|
239
221
|
end
|
240
222
|
end
|
241
223
|
end
|
@@ -243,15 +225,22 @@ module ActiveSupport
|
|
243
225
|
# Cache Store API implementation.
|
244
226
|
#
|
245
227
|
# Increment a cached value. This method uses the Redis incr atomic
|
246
|
-
# operator and can only be used on values written with the
|
247
|
-
# Calling it on a value not stored with
|
228
|
+
# operator and can only be used on values written with the +:raw+ option.
|
229
|
+
# Calling it on a value not stored with +:raw+ will initialize that value
|
248
230
|
# to zero.
|
249
231
|
#
|
250
232
|
# Failsafe: Raises errors.
|
251
233
|
def increment(name, amount = 1, options = nil)
|
252
234
|
instrument :increment, name, amount: amount do
|
253
235
|
failsafe :increment do
|
254
|
-
|
236
|
+
options = merged_options(options)
|
237
|
+
key = normalize_key(name, options)
|
238
|
+
|
239
|
+
redis.with do |c|
|
240
|
+
c.incrby(key, amount).tap do
|
241
|
+
write_key_expiry(c, key, options)
|
242
|
+
end
|
243
|
+
end
|
255
244
|
end
|
256
245
|
end
|
257
246
|
end
|
@@ -259,15 +248,22 @@ module ActiveSupport
|
|
259
248
|
# Cache Store API implementation.
|
260
249
|
#
|
261
250
|
# Decrement a cached value. This method uses the Redis decr atomic
|
262
|
-
# operator and can only be used on values written with the
|
263
|
-
# Calling it on a value not stored with
|
251
|
+
# operator and can only be used on values written with the +:raw+ option.
|
252
|
+
# Calling it on a value not stored with +:raw+ will initialize that value
|
264
253
|
# to zero.
|
265
254
|
#
|
266
255
|
# Failsafe: Raises errors.
|
267
256
|
def decrement(name, amount = 1, options = nil)
|
268
257
|
instrument :decrement, name, amount: amount do
|
269
258
|
failsafe :decrement do
|
270
|
-
|
259
|
+
options = merged_options(options)
|
260
|
+
key = normalize_key(name, options)
|
261
|
+
|
262
|
+
redis.with do |c|
|
263
|
+
c.decrby(key, amount).tap do
|
264
|
+
write_key_expiry(c, key, options)
|
265
|
+
end
|
266
|
+
end
|
271
267
|
end
|
272
268
|
end
|
273
269
|
end
|
@@ -294,12 +290,17 @@ module ActiveSupport
|
|
294
290
|
end
|
295
291
|
end
|
296
292
|
|
297
|
-
|
293
|
+
# Get info from redis servers.
|
294
|
+
def stats
|
295
|
+
redis.with { |c| c.info }
|
296
|
+
end
|
297
|
+
|
298
|
+
def mget_capable? # :nodoc:
|
298
299
|
set_redis_capabilities unless defined? @mget_capable
|
299
300
|
@mget_capable
|
300
301
|
end
|
301
302
|
|
302
|
-
def mset_capable?
|
303
|
+
def mset_capable? # :nodoc:
|
303
304
|
set_redis_capabilities unless defined? @mset_capable
|
304
305
|
@mset_capable
|
305
306
|
end
|
@@ -318,16 +319,19 @@ module ActiveSupport
|
|
318
319
|
|
319
320
|
# Store provider interface:
|
320
321
|
# Read an entry from the cache.
|
321
|
-
def read_entry(key, options
|
322
|
+
def read_entry(key, **options)
|
323
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
324
|
+
end
|
325
|
+
|
326
|
+
def read_serialized_entry(key, raw: false, **options)
|
322
327
|
failsafe :read_entry do
|
323
|
-
|
324
|
-
deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
|
328
|
+
redis.with { |c| c.get(key) }
|
325
329
|
end
|
326
330
|
end
|
327
331
|
|
328
|
-
def read_multi_entries(names,
|
332
|
+
def read_multi_entries(names, **options)
|
329
333
|
if mget_capable?
|
330
|
-
read_multi_mget(*names)
|
334
|
+
read_multi_mget(*names, **options)
|
331
335
|
else
|
332
336
|
super
|
333
337
|
end
|
@@ -336,6 +340,7 @@ module ActiveSupport
|
|
336
340
|
def read_multi_mget(*names)
|
337
341
|
options = names.extract_options!
|
338
342
|
options = merged_options(options)
|
343
|
+
return {} if names == []
|
339
344
|
raw = options&.fetch(:raw, false)
|
340
345
|
|
341
346
|
keys = names.map { |name| normalize_key(name, options) }
|
@@ -357,9 +362,11 @@ module ActiveSupport
|
|
357
362
|
# Write an entry to the cache.
|
358
363
|
#
|
359
364
|
# Requires Redis 2.6.12+ for extended SET options.
|
360
|
-
def write_entry(key, entry,
|
361
|
-
|
365
|
+
def write_entry(key, entry, raw: false, **options)
|
366
|
+
write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
|
367
|
+
end
|
362
368
|
|
369
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
|
363
370
|
# If race condition TTL is in use, ensure that cache entries
|
364
371
|
# stick around a bit longer after they would have expired
|
365
372
|
# so we can purposefully serve stale entries.
|
@@ -367,16 +374,20 @@ module ActiveSupport
|
|
367
374
|
expires_in += 5.minutes
|
368
375
|
end
|
369
376
|
|
377
|
+
modifiers = {}
|
378
|
+
if unless_exist || expires_in
|
379
|
+
modifiers[:nx] = unless_exist
|
380
|
+
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
381
|
+
end
|
382
|
+
|
370
383
|
failsafe :write_entry, returning: false do
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
384
|
+
redis.with { |c| c.set key, payload, **modifiers }
|
385
|
+
end
|
386
|
+
end
|
375
387
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
end
|
388
|
+
def write_key_expiry(client, key, options)
|
389
|
+
if options[:expires_in] && client.ttl(key).negative?
|
390
|
+
client.expire key, options[:expires_in].to_i
|
380
391
|
end
|
381
392
|
end
|
382
393
|
|
@@ -387,12 +398,20 @@ module ActiveSupport
|
|
387
398
|
end
|
388
399
|
end
|
389
400
|
|
401
|
+
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
402
|
+
def delete_multi_entries(entries, **_options)
|
403
|
+
redis.with { |c| c.del(entries) }
|
404
|
+
end
|
405
|
+
|
390
406
|
# Nonstandard store provider API to write multiple values at once.
|
391
407
|
def write_multi_entries(entries, expires_in: nil, **options)
|
392
408
|
if entries.any?
|
393
409
|
if mset_capable? && expires_in.nil?
|
394
410
|
failsafe :write_multi_entries do
|
395
|
-
|
411
|
+
payload = serialize_entries(entries, **options)
|
412
|
+
redis.with do |c|
|
413
|
+
c.mapped_mset(payload)
|
414
|
+
end
|
396
415
|
end
|
397
416
|
else
|
398
417
|
super
|
@@ -402,12 +421,12 @@ module ActiveSupport
|
|
402
421
|
|
403
422
|
# Truncate keys that exceed 1kB.
|
404
423
|
def normalize_key(key, options)
|
405
|
-
truncate_key super
|
424
|
+
truncate_key super&.b
|
406
425
|
end
|
407
426
|
|
408
427
|
def truncate_key(key)
|
409
|
-
if key.bytesize > max_key_bytesize
|
410
|
-
suffix = ":
|
428
|
+
if key && key.bytesize > max_key_bytesize
|
429
|
+
suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
|
411
430
|
truncate_at = max_key_bytesize - suffix.bytesize
|
412
431
|
"#{key.byteslice(0, truncate_at)}#{suffix}"
|
413
432
|
else
|
@@ -415,52 +434,35 @@ module ActiveSupport
|
|
415
434
|
end
|
416
435
|
end
|
417
436
|
|
418
|
-
def deserialize_entry(
|
419
|
-
if
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
if raw != written_raw
|
424
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
425
|
-
Using a different value for the raw option when reading and writing
|
426
|
-
to a cache key is deprecated for :redis_cache_store and Rails 6.0
|
427
|
-
will stop automatically detecting the format when reading to avoid
|
428
|
-
marshal loading untrusted raw strings.
|
429
|
-
MSG
|
430
|
-
end
|
431
|
-
|
432
|
-
entry.is_a?(Entry) ? entry : Entry.new(entry)
|
437
|
+
def deserialize_entry(payload, raw: false, **)
|
438
|
+
if raw && !payload.nil?
|
439
|
+
Entry.new(payload)
|
440
|
+
else
|
441
|
+
super(payload)
|
433
442
|
end
|
434
443
|
end
|
435
444
|
|
436
|
-
def serialize_entry(entry, raw: false)
|
445
|
+
def serialize_entry(entry, raw: false, **options)
|
437
446
|
if raw
|
438
447
|
entry.value.to_s
|
439
448
|
else
|
440
|
-
|
449
|
+
super(entry, raw: raw, **options)
|
441
450
|
end
|
442
451
|
end
|
443
452
|
|
444
|
-
def serialize_entries(entries,
|
453
|
+
def serialize_entries(entries, **options)
|
445
454
|
entries.transform_values do |entry|
|
446
|
-
serialize_entry
|
455
|
+
serialize_entry(entry, **options)
|
447
456
|
end
|
448
457
|
end
|
449
458
|
|
450
459
|
def failsafe(method, returning: nil)
|
451
460
|
yield
|
452
|
-
rescue ::Redis::
|
453
|
-
|
461
|
+
rescue ::Redis::BaseError => error
|
462
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
463
|
+
@error_handler&.call(method: method, exception: error, returning: returning)
|
454
464
|
returning
|
455
465
|
end
|
456
|
-
|
457
|
-
def handle_exception(exception:, method:, returning:)
|
458
|
-
if @error_handler
|
459
|
-
@error_handler.(method: method, exception: exception, returning: returning)
|
460
|
-
end
|
461
|
-
rescue => failsafe
|
462
|
-
warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}"
|
463
|
-
end
|
464
466
|
end
|
465
467
|
end
|
466
468
|
end
|