activesupport 6.1.7.4 → 7.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +151 -632
- data/MIT-LICENSE +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +0 -2
- data/lib/active_support/benchmarkable.rb +2 -2
- data/lib/active_support/cache/file_store.rb +15 -9
- data/lib/active_support/cache/mem_cache_store.rb +119 -28
- data/lib/active_support/cache/memory_store.rb +21 -13
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +39 -59
- data/lib/active_support/cache/strategy/local_cache.rb +29 -49
- data/lib/active_support/cache.rb +189 -45
- data/lib/active_support/callbacks.rb +35 -31
- data/lib/active_support/concern.rb +5 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +6 -3
- data/lib/active_support/configuration_file.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +6 -6
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +2 -2
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/digest/uuid.rb +13 -13
- data/lib/active_support/core_ext/enumerable.rb +64 -12
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/module/delegation.rb +2 -8
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +29 -24
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +60 -68
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/time/calculations.rb +4 -5
- data/lib/active_support/core_ext/time/zones.rb +2 -17
- data/lib/active_support/core_ext/uri.rb +0 -14
- data/lib/active_support/current_attributes.rb +17 -2
- 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 +58 -788
- data/lib/active_support/deprecation/behaviors.rb +4 -1
- data/lib/active_support/deprecation/method_wrappers.rb +3 -3
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/descendants_tracker.rb +12 -9
- data/lib/active_support/digest.rb +4 -4
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -1
- data/lib/active_support/duration.rb +80 -52
- data/lib/active_support/encrypted_configuration.rb +11 -1
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/environment_inquirer.rb +1 -1
- data/lib/active_support/evented_file_update_checker.rb +1 -1
- data/lib/active_support/execution_wrapper.rb +13 -16
- data/lib/active_support/fork_tracker.rb +2 -4
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +3 -1
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/inflector/inflections.rb +11 -4
- data/lib/active_support/inflector/methods.rb +23 -46
- data/lib/active_support/json/encoding.rb +3 -3
- data/lib/active_support/key_generator.rb +18 -1
- data/lib/active_support/locale/en.yml +1 -1
- data/lib/active_support/log_subscriber.rb +13 -3
- data/lib/active_support/logger_thread_safe_level.rb +5 -13
- data/lib/active_support/message_encryptor.rb +3 -3
- data/lib/active_support/message_verifier.rb +4 -4
- data/lib/active_support/messages/metadata.rb +2 -2
- data/lib/active_support/multibyte/chars.rb +10 -11
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +31 -11
- data/lib/active_support/notifications/instrumenter.rb +17 -0
- data/lib/active_support/notifications.rb +10 -0
- data/lib/active_support/number_helper/number_converter.rb +1 -3
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/rounding_helper.rb +1 -5
- data/lib/active_support/number_helper.rb +0 -2
- data/lib/active_support/option_merger.rb +4 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/parameter_filter.rb +5 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/railtie.rb +33 -10
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +2 -2
- data/lib/active_support/secure_compare_rotator.rb +1 -1
- data/lib/active_support/string_inquirer.rb +0 -2
- data/lib/active_support/subscriber.rb +5 -0
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +34 -4
- data/lib/active_support/testing/deprecation.rb +1 -1
- data/lib/active_support/testing/isolation.rb +1 -1
- data/lib/active_support/testing/method_call_assertions.rb +5 -5
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +13 -2
- data/lib/active_support/time_with_zone.rb +19 -6
- data/lib/active_support/values/time_zone.rb +25 -11
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +2 -1
- data/lib/active_support.rb +14 -1
- metadata +14 -29
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
data/MIT-LICENSE
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
|
-
# Actionable errors
|
4
|
+
# Actionable errors lets you define actions to resolve an error.
|
5
5
|
#
|
6
6
|
# To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
|
7
7
|
# module and invoke the +action+ class macro to define the action. An action
|
@@ -34,13 +34,13 @@ module ActiveSupport
|
|
34
34
|
# <% benchmark 'Process data files', level: :info, silence: true do %>
|
35
35
|
# <%= expensive_and_chatty_files_operation %>
|
36
36
|
# <% end %>
|
37
|
-
def benchmark(message = "Benchmarking", options = {})
|
37
|
+
def benchmark(message = "Benchmarking", options = {}, &block)
|
38
38
|
if logger
|
39
39
|
options.assert_valid_keys(:level, :silence)
|
40
40
|
options[:level] ||= :info
|
41
41
|
|
42
42
|
result = nil
|
43
|
-
ms = Benchmark.ms { result = options[:silence] ? logger.silence
|
43
|
+
ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
|
44
44
|
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
45
45
|
result
|
46
46
|
else
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/marshal"
|
4
3
|
require "active_support/core_ext/file/atomic"
|
5
4
|
require "active_support/core_ext/string/conversions"
|
6
5
|
require "uri/common"
|
@@ -12,7 +11,6 @@ module ActiveSupport
|
|
12
11
|
# FileStore implements the Strategy::LocalCache strategy which implements
|
13
12
|
# an in-memory cache inside of a block.
|
14
13
|
class FileStore < Store
|
15
|
-
prepend Strategy::LocalCache
|
16
14
|
attr_reader :cache_path
|
17
15
|
|
18
16
|
DIR_FORMATTER = "%03X"
|
@@ -73,19 +71,27 @@ module ActiveSupport
|
|
73
71
|
|
74
72
|
private
|
75
73
|
def read_entry(key, **options)
|
76
|
-
if
|
77
|
-
entry =
|
74
|
+
if payload = read_serialized_entry(key, **options)
|
75
|
+
entry = deserialize_entry(payload)
|
78
76
|
entry if entry.is_a?(Cache::Entry)
|
79
77
|
end
|
80
|
-
|
81
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_serialized_entry(key, **)
|
81
|
+
File.binread(key) if File.exist?(key)
|
82
|
+
rescue => error
|
83
|
+
logger.error("FileStoreError (#{error}): #{error.message}") if logger
|
82
84
|
nil
|
83
85
|
end
|
84
86
|
|
85
87
|
def write_entry(key, entry, **options)
|
88
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_serialized_entry(key, payload, **options)
|
86
92
|
return false if options[:unless_exist] && File.exist?(key)
|
87
93
|
ensure_cache_path(File.dirname(key))
|
88
|
-
File.atomic_write(key, cache_path) { |f| f.write(
|
94
|
+
File.atomic_write(key, cache_path) { |f| f.write(payload) }
|
89
95
|
true
|
90
96
|
end
|
91
97
|
|
@@ -95,9 +101,9 @@ module ActiveSupport
|
|
95
101
|
File.delete(key)
|
96
102
|
delete_empty_directories(File.dirname(key))
|
97
103
|
true
|
98
|
-
rescue
|
104
|
+
rescue
|
99
105
|
# Just in case the error was caused by another process deleting the file first.
|
100
|
-
raise
|
106
|
+
raise if File.exist?(key)
|
101
107
|
false
|
102
108
|
end
|
103
109
|
end
|
@@ -7,8 +7,8 @@ rescue LoadError => e
|
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
+
require "delegate"
|
10
11
|
require "active_support/core_ext/enumerable"
|
11
|
-
require "active_support/core_ext/marshal"
|
12
12
|
require "active_support/core_ext/array/extract_options"
|
13
13
|
|
14
14
|
module ActiveSupport
|
@@ -26,29 +26,52 @@ module ActiveSupport
|
|
26
26
|
# MemCacheStore implements the Strategy::LocalCache strategy which implements
|
27
27
|
# an in-memory cache inside of a block.
|
28
28
|
class MemCacheStore < Store
|
29
|
-
|
29
|
+
# Advertise cache versioning support.
|
30
|
+
def self.supports_cache_versioning?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
prepend Strategy::LocalCache
|
35
|
+
|
36
|
+
module DupLocalCache
|
37
|
+
class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore)
|
38
|
+
def write_entry(_key, entry)
|
39
|
+
if entry.is_a?(Entry)
|
40
|
+
entry.dup_value!
|
41
|
+
end
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch_entry(key)
|
46
|
+
entry = super do
|
47
|
+
new_entry = yield
|
48
|
+
if entry.is_a?(Entry)
|
49
|
+
new_entry.dup_value!
|
50
|
+
end
|
51
|
+
new_entry
|
52
|
+
end
|
53
|
+
entry = entry.dup
|
54
|
+
|
55
|
+
if entry.is_a?(Entry)
|
56
|
+
entry.dup_value!
|
57
|
+
end
|
58
|
+
|
59
|
+
entry
|
60
|
+
end
|
61
|
+
end
|
30
62
|
|
31
|
-
# Provide support for raw values in the local cache strategy.
|
32
|
-
module LocalCacheWithRaw # :nodoc:
|
33
63
|
private
|
34
|
-
def
|
35
|
-
if
|
36
|
-
|
37
|
-
|
38
|
-
|
64
|
+
def local_cache
|
65
|
+
if ActiveSupport::Cache.format_version == 6.1
|
66
|
+
if local_cache = super
|
67
|
+
DupLocalStore.new(local_cache)
|
68
|
+
end
|
39
69
|
else
|
40
70
|
super
|
41
71
|
end
|
42
72
|
end
|
43
73
|
end
|
44
|
-
|
45
|
-
# Advertise cache versioning support.
|
46
|
-
def self.supports_cache_versioning?
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
prepend Strategy::LocalCache
|
51
|
-
prepend LocalCacheWithRaw
|
74
|
+
prepend DupLocalCache
|
52
75
|
|
53
76
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
54
77
|
|
@@ -86,6 +109,9 @@ module ActiveSupport
|
|
86
109
|
def initialize(*addresses)
|
87
110
|
addresses = addresses.flatten
|
88
111
|
options = addresses.extract_options!
|
112
|
+
if options.key?(:cache_nils)
|
113
|
+
options[:skip_nil] = !options.delete(:cache_nils)
|
114
|
+
end
|
89
115
|
super(options)
|
90
116
|
|
91
117
|
unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
|
@@ -138,15 +164,72 @@ module ActiveSupport
|
|
138
164
|
end
|
139
165
|
|
140
166
|
private
|
167
|
+
module Coders # :nodoc:
|
168
|
+
class << self
|
169
|
+
def [](version)
|
170
|
+
case version
|
171
|
+
when 6.1
|
172
|
+
Rails61Coder
|
173
|
+
when 7.0
|
174
|
+
Rails70Coder
|
175
|
+
else
|
176
|
+
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
module Loader
|
182
|
+
def load(payload)
|
183
|
+
if payload.is_a?(Entry)
|
184
|
+
payload
|
185
|
+
else
|
186
|
+
Cache::Coders::Loader.load(payload)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
module Rails61Coder
|
192
|
+
include Loader
|
193
|
+
extend self
|
194
|
+
|
195
|
+
def dump(entry)
|
196
|
+
entry
|
197
|
+
end
|
198
|
+
|
199
|
+
def dump_compressed(entry, threshold)
|
200
|
+
entry.compressed(threshold)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
module Rails70Coder
|
205
|
+
include Cache::Coders::Rails70Coder
|
206
|
+
include Loader
|
207
|
+
extend self
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def default_coder
|
212
|
+
Coders[Cache.format_version]
|
213
|
+
end
|
214
|
+
|
141
215
|
# Read an entry from the cache.
|
142
216
|
def read_entry(key, **options)
|
143
|
-
|
217
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
218
|
+
end
|
219
|
+
|
220
|
+
def read_serialized_entry(key, **options)
|
221
|
+
rescue_error_with(nil) do
|
222
|
+
@data.with { |c| c.get(key, options) }
|
223
|
+
end
|
144
224
|
end
|
145
225
|
|
146
226
|
# Write an entry to the cache.
|
147
227
|
def write_entry(key, entry, **options)
|
228
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
229
|
+
end
|
230
|
+
|
231
|
+
def write_serialized_entry(key, payload, **options)
|
148
232
|
method = options[:unless_exist] ? :add : :set
|
149
|
-
value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
|
150
233
|
expires_in = options[:expires_in].to_i
|
151
234
|
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
152
235
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
@@ -154,7 +237,7 @@ module ActiveSupport
|
|
154
237
|
end
|
155
238
|
rescue_error_with false do
|
156
239
|
# The value "compress: false" prevents duplicate compression within Dalli.
|
157
|
-
@data.with { |c| c.send(method, key,
|
240
|
+
@data.with { |c| c.send(method, key, payload, expires_in, **options, compress: false) }
|
158
241
|
end
|
159
242
|
end
|
160
243
|
|
@@ -166,7 +249,7 @@ module ActiveSupport
|
|
166
249
|
values = {}
|
167
250
|
|
168
251
|
raw_values.each do |key, value|
|
169
|
-
entry = deserialize_entry(value)
|
252
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
170
253
|
|
171
254
|
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
172
255
|
values[keys_to_names[key]] = entry.value
|
@@ -181,25 +264,33 @@ module ActiveSupport
|
|
181
264
|
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
182
265
|
end
|
183
266
|
|
267
|
+
def serialize_entry(entry, raw: false, **options)
|
268
|
+
if raw
|
269
|
+
entry.value.to_s
|
270
|
+
else
|
271
|
+
super(entry, raw: raw, **options)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
184
275
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
185
276
|
# before applying the regular expression to ensure we are escaping all
|
186
277
|
# characters properly.
|
187
278
|
def normalize_key(key, options)
|
188
279
|
key = super
|
189
|
-
|
190
280
|
if key
|
191
281
|
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
192
282
|
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
193
|
-
key = "#{key[0,
|
283
|
+
key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
194
284
|
end
|
195
|
-
|
196
285
|
key
|
197
286
|
end
|
198
287
|
|
199
|
-
def deserialize_entry(payload)
|
200
|
-
|
201
|
-
|
202
|
-
|
288
|
+
def deserialize_entry(payload, raw: false, **)
|
289
|
+
if payload && raw
|
290
|
+
Entry.new(payload)
|
291
|
+
else
|
292
|
+
super(payload)
|
293
|
+
end
|
203
294
|
end
|
204
295
|
|
205
296
|
def rescue_error_with(fallback)
|
@@ -25,21 +25,25 @@ module ActiveSupport
|
|
25
25
|
# MemoryStore is thread-safe.
|
26
26
|
class MemoryStore < Store
|
27
27
|
module DupCoder # :nodoc:
|
28
|
-
|
29
|
-
def load(entry)
|
30
|
-
entry = entry.dup
|
31
|
-
entry.dup_value!
|
32
|
-
entry
|
33
|
-
end
|
28
|
+
extend self
|
34
29
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
39
|
end
|
40
|
-
end
|
41
40
|
|
42
|
-
|
41
|
+
def load(entry)
|
42
|
+
entry = entry.dup
|
43
|
+
entry.dup_value!
|
44
|
+
entry
|
45
|
+
end
|
46
|
+
end
|
43
47
|
|
44
48
|
def initialize(options = nil)
|
45
49
|
options ||= {}
|
@@ -139,6 +143,10 @@ module ActiveSupport
|
|
139
143
|
private
|
140
144
|
PER_ENTRY_OVERHEAD = 240
|
141
145
|
|
146
|
+
def default_coder
|
147
|
+
DupCoder
|
148
|
+
end
|
149
|
+
|
142
150
|
def cached_size(key, payload)
|
143
151
|
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
144
152
|
end
|
@@ -156,7 +164,7 @@ module ActiveSupport
|
|
156
164
|
end
|
157
165
|
|
158
166
|
def write_entry(key, entry, **options)
|
159
|
-
payload = serialize_entry(entry)
|
167
|
+
payload = serialize_entry(entry, **options)
|
160
168
|
synchronize do
|
161
169
|
return false if options[:unless_exist] && @data.key?(key)
|
162
170
|
|
@@ -33,10 +33,18 @@ module ActiveSupport
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
-
def read_entry(key, **
|
36
|
+
def read_entry(key, **s)
|
37
|
+
deserialize_entry(read_serialized_entry(key))
|
37
38
|
end
|
38
39
|
|
39
|
-
def
|
40
|
+
def read_serialized_entry(_key, **)
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_entry(key, entry, **)
|
44
|
+
write_serialized_entry(key, serialize_entry(entry))
|
45
|
+
end
|
46
|
+
|
47
|
+
def write_serialized_entry(_key, _payload, **)
|
40
48
|
true
|
41
49
|
end
|
42
50
|
|
@@ -15,8 +15,7 @@ begin
|
|
15
15
|
rescue LoadError
|
16
16
|
end
|
17
17
|
|
18
|
-
require "digest
|
19
|
-
require "active_support/core_ext/marshal"
|
18
|
+
require "active_support/digest"
|
20
19
|
|
21
20
|
module ActiveSupport
|
22
21
|
module Cache
|
@@ -46,7 +45,7 @@ module ActiveSupport
|
|
46
45
|
# 4.0.1+ for distributed mget support.
|
47
46
|
# * +delete_matched+ support for Redis KEYS globs.
|
48
47
|
class RedisCacheStore < Store
|
49
|
-
# Keys are truncated with
|
48
|
+
# Keys are truncated with the ActiveSupport digest if they exceed 1kB
|
50
49
|
MAX_KEY_BYTESIZE = 1024
|
51
50
|
|
52
51
|
DEFAULT_REDIS_OPTIONS = {
|
@@ -71,35 +70,7 @@ module ActiveSupport
|
|
71
70
|
true
|
72
71
|
end
|
73
72
|
|
74
|
-
# Support raw values in the local cache strategy.
|
75
|
-
module LocalCacheWithRaw # :nodoc:
|
76
|
-
private
|
77
|
-
def write_entry(key, entry, **options)
|
78
|
-
if options[:raw] && local_cache
|
79
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
80
|
-
raw_entry.expires_at = entry.expires_at
|
81
|
-
super(key, raw_entry, **options)
|
82
|
-
else
|
83
|
-
super
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def write_multi_entries(entries, **options)
|
88
|
-
if options[:raw] && local_cache
|
89
|
-
raw_entries = entries.map do |key, entry|
|
90
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
91
|
-
raw_entry.expires_at = entry.expires_at
|
92
|
-
end.to_h
|
93
|
-
|
94
|
-
super(raw_entries, **options)
|
95
|
-
else
|
96
|
-
super
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
73
|
prepend Strategy::LocalCache
|
102
|
-
prepend LocalCacheWithRaw
|
103
74
|
|
104
75
|
class << self
|
105
76
|
# Factory method to create a new Redis instance.
|
@@ -113,7 +84,7 @@ module ActiveSupport
|
|
113
84
|
# :url String -> Redis.new(url: …)
|
114
85
|
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
115
86
|
#
|
116
|
-
def build_redis(redis: nil, url: nil, **redis_options)
|
87
|
+
def build_redis(redis: nil, url: nil, **redis_options) # :nodoc:
|
117
88
|
urls = Array(url)
|
118
89
|
|
119
90
|
if redis.is_a?(Proc)
|
@@ -169,7 +140,7 @@ module ActiveSupport
|
|
169
140
|
# Race condition TTL is not set by default. This can be used to avoid
|
170
141
|
# "thundering herd" cache writes when hot cache entries are expired.
|
171
142
|
# See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
|
172
|
-
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder:
|
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)
|
173
144
|
@redis_options = redis_options
|
174
145
|
|
175
146
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
@@ -319,12 +290,17 @@ module ActiveSupport
|
|
319
290
|
end
|
320
291
|
end
|
321
292
|
|
322
|
-
|
293
|
+
# Get info from redis servers.
|
294
|
+
def stats
|
295
|
+
redis.with { |c| c.info }
|
296
|
+
end
|
297
|
+
|
298
|
+
def mget_capable? # :nodoc:
|
323
299
|
set_redis_capabilities unless defined? @mget_capable
|
324
300
|
@mget_capable
|
325
301
|
end
|
326
302
|
|
327
|
-
def mset_capable?
|
303
|
+
def mset_capable? # :nodoc:
|
328
304
|
set_redis_capabilities unless defined? @mset_capable
|
329
305
|
@mset_capable
|
330
306
|
end
|
@@ -344,9 +320,12 @@ module ActiveSupport
|
|
344
320
|
# Store provider interface:
|
345
321
|
# Read an entry from the cache.
|
346
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)
|
347
327
|
failsafe :read_entry do
|
348
|
-
|
349
|
-
deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
|
328
|
+
redis.with { |c| c.get(key) }
|
350
329
|
end
|
351
330
|
end
|
352
331
|
|
@@ -383,9 +362,11 @@ module ActiveSupport
|
|
383
362
|
# Write an entry to the cache.
|
384
363
|
#
|
385
364
|
# Requires Redis 2.6.12+ for extended SET options.
|
386
|
-
def write_entry(key, entry,
|
387
|
-
|
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
|
388
368
|
|
369
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
|
389
370
|
# If race condition TTL is in use, ensure that cache entries
|
390
371
|
# stick around a bit longer after they would have expired
|
391
372
|
# so we can purposefully serve stale entries.
|
@@ -393,16 +374,14 @@ module ActiveSupport
|
|
393
374
|
expires_in += 5.minutes
|
394
375
|
end
|
395
376
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
401
382
|
|
402
|
-
|
403
|
-
|
404
|
-
redis.with { |c| c.set key, serialized_entry }
|
405
|
-
end
|
383
|
+
failsafe :write_entry, returning: false do
|
384
|
+
redis.with { |c| c.set key, payload, **modifiers }
|
406
385
|
end
|
407
386
|
end
|
408
387
|
|
@@ -429,7 +408,10 @@ module ActiveSupport
|
|
429
408
|
if entries.any?
|
430
409
|
if mset_capable? && expires_in.nil?
|
431
410
|
failsafe :write_multi_entries do
|
432
|
-
|
411
|
+
payload = serialize_entries(entries, **options)
|
412
|
+
redis.with do |c|
|
413
|
+
c.mapped_mset(payload)
|
414
|
+
end
|
433
415
|
end
|
434
416
|
else
|
435
417
|
super
|
@@ -444,7 +426,7 @@ module ActiveSupport
|
|
444
426
|
|
445
427
|
def truncate_key(key)
|
446
428
|
if key && key.bytesize > max_key_bytesize
|
447
|
-
suffix = ":
|
429
|
+
suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
|
448
430
|
truncate_at = max_key_bytesize - suffix.bytesize
|
449
431
|
"#{key.byteslice(0, truncate_at)}#{suffix}"
|
450
432
|
else
|
@@ -452,25 +434,25 @@ module ActiveSupport
|
|
452
434
|
end
|
453
435
|
end
|
454
436
|
|
455
|
-
def deserialize_entry(payload, raw:)
|
456
|
-
if
|
457
|
-
Entry.new(payload
|
437
|
+
def deserialize_entry(payload, raw: false, **)
|
438
|
+
if raw && !payload.nil?
|
439
|
+
Entry.new(payload)
|
458
440
|
else
|
459
441
|
super(payload)
|
460
442
|
end
|
461
443
|
end
|
462
444
|
|
463
|
-
def serialize_entry(entry, raw: false)
|
445
|
+
def serialize_entry(entry, raw: false, **options)
|
464
446
|
if raw
|
465
447
|
entry.value.to_s
|
466
448
|
else
|
467
|
-
super(entry)
|
449
|
+
super(entry, raw: raw, **options)
|
468
450
|
end
|
469
451
|
end
|
470
452
|
|
471
|
-
def serialize_entries(entries,
|
453
|
+
def serialize_entries(entries, **options)
|
472
454
|
entries.transform_values do |entry|
|
473
|
-
serialize_entry
|
455
|
+
serialize_entry(entry, **options)
|
474
456
|
end
|
475
457
|
end
|
476
458
|
|
@@ -485,8 +467,6 @@ module ActiveSupport
|
|
485
467
|
if @error_handler
|
486
468
|
@error_handler.(method: method, exception: exception, returning: returning)
|
487
469
|
end
|
488
|
-
rescue => failsafe
|
489
|
-
warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}"
|
490
470
|
end
|
491
471
|
end
|
492
472
|
end
|