activesupport 7.0.10 → 7.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +703 -361
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +2 -0
- data/lib/active_support/backtrace_cleaner.rb +25 -5
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +128 -0
- data/lib/active_support/cache/file_store.rb +37 -10
- data/lib/active_support/cache/mem_cache_store.rb +84 -68
- data/lib/active_support/cache/memory_store.rb +76 -26
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +126 -131
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +20 -8
- data/lib/active_support/cache.rb +304 -246
- data/lib/active_support/callbacks.rb +38 -18
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +2 -1
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/attribute.rb +1 -0
- data/lib/active_support/core_ext/class/subclasses.rb +13 -10
- data/lib/active_support/core_ext/date/conversions.rb +1 -0
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +1 -10
- data/lib/active_support/core_ext/enumerable.rb +3 -75
- data/lib/active_support/core_ext/erb/util.rb +196 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +40 -11
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +15 -24
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
- data/lib/active_support/core_ext/object/json.rb +14 -8
- data/lib/active_support/core_ext/object/with.rb +44 -0
- data/lib/active_support/core_ext/object/with_options.rb +4 -4
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +16 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +12 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +24 -12
- data/lib/active_support/core_ext/string/filters.rb +20 -14
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -5
- data/lib/active_support/core_ext/string/output_safety.rb +38 -174
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +18 -2
- data/lib/active_support/core_ext/time/conversions.rb +2 -2
- data/lib/active_support/core_ext/time/zones.rb +4 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/current_attributes.rb +15 -6
- data/lib/active_support/dependencies/autoload.rb +17 -12
- data/lib/active_support/deprecation/behaviors.rb +55 -34
- data/lib/active_support/deprecation/constant_accessor.rb +5 -4
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/instance_delegator.rb +31 -4
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
- data/lib/active_support/deprecation/reporting.rb +40 -29
- data/lib/active_support/deprecation.rb +32 -5
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +104 -132
- data/lib/active_support/duration/iso8601_serializer.rb +0 -2
- data/lib/active_support/duration.rb +2 -1
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +8 -3
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +121 -35
- data/lib/active_support/execution_wrapper.rb +4 -4
- data/lib/active_support/file_update_checker.rb +4 -2
- data/lib/active_support/fork_tracker.rb +10 -2
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +35 -17
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +23 -11
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +6 -4
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +78 -33
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_thread_safe_level.rb +9 -22
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +140 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +292 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +212 -93
- data/lib/active_support/message_verifiers.rb +134 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +2 -0
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +239 -81
- data/lib/active_support/notifications/instrumenter.rb +79 -30
- data/lib/active_support/notifications.rb +1 -1
- data/lib/active_support/number_helper/number_converter.rb +5 -14
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +318 -379
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +14 -0
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +33 -21
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +2 -0
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +3 -1
- data/lib/active_support/subscriber.rb +9 -27
- data/lib/active_support/syntax_error_proxy.rb +49 -0
- data/lib/active_support/tagged_logging.rb +60 -24
- data/lib/active_support/test_case.rb +153 -6
- data/lib/active_support/testing/assertions.rb +26 -10
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +32 -0
- data/lib/active_support/testing/deprecation.rb +25 -25
- data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
- data/lib/active_support/testing/isolation.rb +1 -1
- data/lib/active_support/testing/method_call_assertions.rb +21 -8
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +38 -0
- data/lib/active_support/testing/time_helpers.rb +32 -14
- data/lib/active_support/time_with_zone.rb +4 -14
- data/lib/active_support/values/time_zone.rb +9 -7
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- 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 -2
- data/lib/active_support.rb +13 -3
- metadata +48 -58
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
- data/lib/active_support/core_ext/range/overlaps.rb +0 -36
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/per_thread_registry.rb +0 -65
|
@@ -9,30 +9,15 @@ rescue LoadError
|
|
|
9
9
|
raise
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
else
|
|
17
|
-
require "hiredis-client"
|
|
18
|
-
end
|
|
19
|
-
rescue LoadError
|
|
20
|
-
end
|
|
21
|
-
|
|
12
|
+
require "connection_pool"
|
|
13
|
+
require "active_support/core_ext/array/wrap"
|
|
14
|
+
require "active_support/core_ext/hash/slice"
|
|
15
|
+
require "active_support/core_ext/numeric/time"
|
|
22
16
|
require "active_support/digest"
|
|
23
17
|
|
|
24
18
|
module ActiveSupport
|
|
25
19
|
module Cache
|
|
26
|
-
|
|
27
|
-
def with
|
|
28
|
-
yield self
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
::Redis.include(ConnectionPoolLike)
|
|
33
|
-
::Redis::Distributed.include(ConnectionPoolLike)
|
|
34
|
-
|
|
35
|
-
# Redis cache store.
|
|
20
|
+
# = Redis \Cache \Store
|
|
36
21
|
#
|
|
37
22
|
# Deployment note: Take care to use a *dedicated Redis cache* rather
|
|
38
23
|
# than pointing this at your existing Redis server. It won't cope well
|
|
@@ -53,16 +38,20 @@ module ActiveSupport
|
|
|
53
38
|
MAX_KEY_BYTESIZE = 1024
|
|
54
39
|
|
|
55
40
|
DEFAULT_REDIS_OPTIONS = {
|
|
56
|
-
connect_timeout:
|
|
41
|
+
connect_timeout: 1,
|
|
57
42
|
read_timeout: 1,
|
|
58
43
|
write_timeout: 1,
|
|
59
|
-
reconnect_attempts: 0,
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do
|
|
63
47
|
if logger
|
|
64
48
|
logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
|
|
65
49
|
end
|
|
50
|
+
ActiveSupport.error_reporter&.report(
|
|
51
|
+
exception,
|
|
52
|
+
severity: :warning,
|
|
53
|
+
source: "redis_cache_store.active_support",
|
|
54
|
+
)
|
|
66
55
|
end
|
|
67
56
|
|
|
68
57
|
# The maximum number of entries to receive per SCAN call.
|
|
@@ -116,8 +105,8 @@ module ActiveSupport
|
|
|
116
105
|
end
|
|
117
106
|
end
|
|
118
107
|
|
|
119
|
-
attr_reader :redis_options
|
|
120
108
|
attr_reader :max_key_bytesize
|
|
109
|
+
attr_reader :redis
|
|
121
110
|
|
|
122
111
|
# Creates a new Redis cache store.
|
|
123
112
|
#
|
|
@@ -145,35 +134,31 @@ module ActiveSupport
|
|
|
145
134
|
#
|
|
146
135
|
# Race condition TTL is not set by default. This can be used to avoid
|
|
147
136
|
# "thundering herd" cache writes when hot cache entries are expired.
|
|
148
|
-
# See ActiveSupport::Cache::Store#fetch for more.
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
# See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
|
|
138
|
+
#
|
|
139
|
+
# Setting <tt>skip_nil: true</tt> will not cache nil results:
|
|
140
|
+
#
|
|
141
|
+
# cache.fetch('foo') { nil }
|
|
142
|
+
# cache.fetch('bar', skip_nil: true) { nil }
|
|
143
|
+
# cache.exist?('foo') # => true
|
|
144
|
+
# cache.exist?('bar') # => false
|
|
145
|
+
def initialize(error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
|
146
|
+
universal_options = redis_options.extract!(*UNIVERSAL_OPTIONS)
|
|
147
|
+
|
|
148
|
+
if pool_options = self.class.send(:retrieve_pool_options, redis_options)
|
|
149
|
+
@redis = ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
|
|
150
|
+
else
|
|
151
|
+
@redis = self.class.build_redis(**redis_options)
|
|
152
|
+
end
|
|
151
153
|
|
|
152
154
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
|
153
155
|
@error_handler = error_handler
|
|
154
156
|
|
|
155
|
-
super
|
|
156
|
-
compress: compress, compress_threshold: compress_threshold,
|
|
157
|
-
expires_in: expires_in, race_condition_ttl: race_condition_ttl,
|
|
158
|
-
coder: coder
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def redis
|
|
162
|
-
@redis ||= begin
|
|
163
|
-
pool_options = self.class.send(:retrieve_pool_options, redis_options)
|
|
164
|
-
|
|
165
|
-
if pool_options.any?
|
|
166
|
-
self.class.send(:ensure_connection_pool_added!)
|
|
167
|
-
::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
|
|
168
|
-
else
|
|
169
|
-
self.class.build_redis(**redis_options)
|
|
170
|
-
end
|
|
171
|
-
end
|
|
157
|
+
super(universal_options)
|
|
172
158
|
end
|
|
173
159
|
|
|
174
160
|
def inspect
|
|
175
|
-
|
|
176
|
-
"#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
|
|
161
|
+
"#<#{self.class} options=#{options.inspect} redis=#{redis.inspect}>"
|
|
177
162
|
end
|
|
178
163
|
|
|
179
164
|
# Cache Store API implementation.
|
|
@@ -181,14 +166,13 @@ module ActiveSupport
|
|
|
181
166
|
# Read multiple values at once. Returns a hash of requested keys ->
|
|
182
167
|
# fetched values.
|
|
183
168
|
def read_multi(*names)
|
|
184
|
-
if
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
169
|
+
return {} if names.empty?
|
|
170
|
+
|
|
171
|
+
options = names.extract_options!
|
|
172
|
+
instrument_multi(:read_multi, names, options) do |payload|
|
|
173
|
+
read_multi_entries(names, **options).tap do |results|
|
|
174
|
+
payload[:hits] = results.keys
|
|
189
175
|
end
|
|
190
|
-
else
|
|
191
|
-
super
|
|
192
176
|
end
|
|
193
177
|
end
|
|
194
178
|
|
|
@@ -212,7 +196,7 @@ module ActiveSupport
|
|
|
212
196
|
unless String === matcher
|
|
213
197
|
raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
|
|
214
198
|
end
|
|
215
|
-
redis.
|
|
199
|
+
redis.then do |c|
|
|
216
200
|
pattern = namespace_key(matcher, options)
|
|
217
201
|
cursor = "0"
|
|
218
202
|
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
|
|
@@ -228,12 +212,21 @@ module ActiveSupport
|
|
|
228
212
|
end
|
|
229
213
|
end
|
|
230
214
|
|
|
231
|
-
#
|
|
215
|
+
# Increment a cached integer value using the Redis incrby atomic operator.
|
|
216
|
+
# Returns the updated value.
|
|
217
|
+
#
|
|
218
|
+
# If the key is unset or has expired, it will be set to +amount+:
|
|
219
|
+
#
|
|
220
|
+
# cache.increment("foo") # => 1
|
|
221
|
+
# cache.increment("bar", 100) # => 100
|
|
232
222
|
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
223
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
224
|
+
#
|
|
225
|
+
# cache.write("baz", 5, raw: true)
|
|
226
|
+
# cache.increment("baz") # => 6
|
|
227
|
+
#
|
|
228
|
+
# Incrementing a non-numeric value, or a value written without
|
|
229
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
237
230
|
#
|
|
238
231
|
# Failsafe: Raises errors.
|
|
239
232
|
def increment(name, amount = 1, options = nil)
|
|
@@ -241,22 +234,25 @@ module ActiveSupport
|
|
|
241
234
|
failsafe :increment do
|
|
242
235
|
options = merged_options(options)
|
|
243
236
|
key = normalize_key(name, options)
|
|
244
|
-
|
|
245
|
-
redis.with do |c|
|
|
246
|
-
c.incrby(key, amount).tap do
|
|
247
|
-
write_key_expiry(c, key, options)
|
|
248
|
-
end
|
|
249
|
-
end
|
|
237
|
+
change_counter(key, amount, options)
|
|
250
238
|
end
|
|
251
239
|
end
|
|
252
240
|
end
|
|
253
241
|
|
|
254
|
-
#
|
|
242
|
+
# Decrement a cached integer value using the Redis decrby atomic operator.
|
|
243
|
+
# Returns the updated value.
|
|
244
|
+
#
|
|
245
|
+
# If the key is unset or has expired, it will be set to -amount:
|
|
246
|
+
#
|
|
247
|
+
# cache.decrement("foo") # => -1
|
|
255
248
|
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
249
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
250
|
+
#
|
|
251
|
+
# cache.write("baz", 5, raw: true)
|
|
252
|
+
# cache.decrement("baz") # => 4
|
|
253
|
+
#
|
|
254
|
+
# Decrementing a non-numeric value, or a value written without
|
|
255
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
260
256
|
#
|
|
261
257
|
# Failsafe: Raises errors.
|
|
262
258
|
def decrement(name, amount = 1, options = nil)
|
|
@@ -264,12 +260,7 @@ module ActiveSupport
|
|
|
264
260
|
failsafe :decrement do
|
|
265
261
|
options = merged_options(options)
|
|
266
262
|
key = normalize_key(name, options)
|
|
267
|
-
|
|
268
|
-
redis.with do |c|
|
|
269
|
-
c.decrby(key, amount).tap do
|
|
270
|
-
write_key_expiry(c, key, options)
|
|
271
|
-
end
|
|
272
|
-
end
|
|
263
|
+
change_counter(key, -amount, options)
|
|
273
264
|
end
|
|
274
265
|
end
|
|
275
266
|
end
|
|
@@ -291,38 +282,27 @@ module ActiveSupport
|
|
|
291
282
|
if namespace = merged_options(options)[:namespace]
|
|
292
283
|
delete_matched "*", namespace: namespace
|
|
293
284
|
else
|
|
294
|
-
redis.
|
|
285
|
+
redis.then { |c| c.flushdb }
|
|
295
286
|
end
|
|
296
287
|
end
|
|
297
288
|
end
|
|
298
289
|
|
|
299
290
|
# Get info from redis servers.
|
|
300
291
|
def stats
|
|
301
|
-
redis.
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def mget_capable? # :nodoc:
|
|
305
|
-
set_redis_capabilities unless defined? @mget_capable
|
|
306
|
-
@mget_capable
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def mset_capable? # :nodoc:
|
|
310
|
-
set_redis_capabilities unless defined? @mset_capable
|
|
311
|
-
@mset_capable
|
|
292
|
+
redis.then { |c| c.info }
|
|
312
293
|
end
|
|
313
294
|
|
|
314
295
|
private
|
|
315
|
-
def
|
|
316
|
-
redis.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
296
|
+
def pipeline_entries(entries, &block)
|
|
297
|
+
redis.then { |c|
|
|
298
|
+
if c.is_a?(Redis::Distributed)
|
|
299
|
+
entries.group_by { |k, _v| c.node_for(k) }.each do |node, sub_entries|
|
|
300
|
+
node.pipelined { |pipe| yield(pipe, sub_entries) }
|
|
301
|
+
end
|
|
321
302
|
else
|
|
322
|
-
|
|
323
|
-
@mset_capable = true
|
|
303
|
+
c.pipelined { |pipe| yield(pipe, entries) }
|
|
324
304
|
end
|
|
325
|
-
|
|
305
|
+
}
|
|
326
306
|
end
|
|
327
307
|
|
|
328
308
|
# Store provider interface:
|
|
@@ -333,28 +313,19 @@ module ActiveSupport
|
|
|
333
313
|
|
|
334
314
|
def read_serialized_entry(key, raw: false, **options)
|
|
335
315
|
failsafe :read_entry do
|
|
336
|
-
redis.
|
|
316
|
+
redis.then { |c| c.get(key) }
|
|
337
317
|
end
|
|
338
318
|
end
|
|
339
319
|
|
|
340
320
|
def read_multi_entries(names, **options)
|
|
341
|
-
if mget_capable?
|
|
342
|
-
read_multi_mget(*names, **options)
|
|
343
|
-
else
|
|
344
|
-
super
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def read_multi_mget(*names)
|
|
349
|
-
options = names.extract_options!
|
|
350
321
|
options = merged_options(options)
|
|
351
322
|
return {} if names == []
|
|
352
323
|
raw = options&.fetch(:raw, false)
|
|
353
324
|
|
|
354
325
|
keys = names.map { |name| normalize_key(name, options) }
|
|
355
326
|
|
|
356
|
-
values = failsafe(:
|
|
357
|
-
redis.
|
|
327
|
+
values = failsafe(:read_multi_entries, returning: {}) do
|
|
328
|
+
redis.then { |c| c.mget(*keys) }
|
|
358
329
|
end
|
|
359
330
|
|
|
360
331
|
names.zip(values).each_with_object({}) do |(name, value), results|
|
|
@@ -374,7 +345,7 @@ module ActiveSupport
|
|
|
374
345
|
write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
|
|
375
346
|
end
|
|
376
347
|
|
|
377
|
-
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
|
|
348
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, pipeline: nil, **options)
|
|
378
349
|
# If race condition TTL is in use, ensure that cache entries
|
|
379
350
|
# stick around a bit longer after they would have expired
|
|
380
351
|
# so we can purposefully serve stale entries.
|
|
@@ -388,41 +359,40 @@ module ActiveSupport
|
|
|
388
359
|
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
|
389
360
|
end
|
|
390
361
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if options[:expires_in] && client.ttl(key).negative?
|
|
398
|
-
client.expire key, options[:expires_in].to_i
|
|
362
|
+
if pipeline
|
|
363
|
+
pipeline.set(key, payload, **modifiers)
|
|
364
|
+
else
|
|
365
|
+
failsafe :write_entry, returning: false do
|
|
366
|
+
redis.then { |c| c.set key, payload, **modifiers }
|
|
367
|
+
end
|
|
399
368
|
end
|
|
400
369
|
end
|
|
401
370
|
|
|
402
371
|
# Delete an entry from the cache.
|
|
403
|
-
def delete_entry(key, options)
|
|
372
|
+
def delete_entry(key, **options)
|
|
404
373
|
failsafe :delete_entry, returning: false do
|
|
405
|
-
redis.
|
|
374
|
+
redis.then { |c| c.del(key) == 1 }
|
|
406
375
|
end
|
|
407
376
|
end
|
|
408
377
|
|
|
409
378
|
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
|
410
379
|
def delete_multi_entries(entries, **_options)
|
|
411
|
-
|
|
380
|
+
failsafe :delete_multi_entries, returning: 0 do
|
|
381
|
+
redis.then { |c| c.del(entries) }
|
|
382
|
+
end
|
|
412
383
|
end
|
|
413
384
|
|
|
414
385
|
# Nonstandard store provider API to write multiple values at once.
|
|
415
|
-
def write_multi_entries(entries, expires_in: nil, **options)
|
|
416
|
-
if entries.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
386
|
+
def write_multi_entries(entries, expires_in: nil, race_condition_ttl: nil, **options)
|
|
387
|
+
return if entries.empty?
|
|
388
|
+
|
|
389
|
+
failsafe :write_multi_entries do
|
|
390
|
+
pipeline_entries(entries) do |pipeline, sharded_entries|
|
|
391
|
+
options = options.dup
|
|
392
|
+
options[:pipeline] = pipeline
|
|
393
|
+
sharded_entries.each do |key, entry|
|
|
394
|
+
write_entry key, entry, **options
|
|
423
395
|
end
|
|
424
|
-
else
|
|
425
|
-
super
|
|
426
396
|
end
|
|
427
397
|
end
|
|
428
398
|
end
|
|
@@ -464,10 +434,35 @@ module ActiveSupport
|
|
|
464
434
|
end
|
|
465
435
|
end
|
|
466
436
|
|
|
437
|
+
def change_counter(key, amount, options)
|
|
438
|
+
redis.then do |c|
|
|
439
|
+
c = c.node_for(key) if c.is_a?(Redis::Distributed)
|
|
440
|
+
|
|
441
|
+
if options[:expires_in] && supports_expire_nx?
|
|
442
|
+
c.pipelined do |pipeline|
|
|
443
|
+
pipeline.incrby(key, amount)
|
|
444
|
+
pipeline.call(:expire, key, options[:expires_in].to_i, "NX")
|
|
445
|
+
end.first
|
|
446
|
+
else
|
|
447
|
+
count = c.incrby(key, amount)
|
|
448
|
+
if count != amount && options[:expires_in] && c.ttl(key) < 0
|
|
449
|
+
c.expire(key, options[:expires_in].to_i)
|
|
450
|
+
end
|
|
451
|
+
count
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def supports_expire_nx?
|
|
457
|
+
return @supports_expire_nx if defined?(@supports_expire_nx)
|
|
458
|
+
|
|
459
|
+
redis_versions = redis.then { |c| Array.wrap(c.info("server")).pluck("redis_version") }
|
|
460
|
+
@supports_expire_nx = redis_versions.all? { |v| Gem::Version.new(v) >= Gem::Version.new("7.0.0") }
|
|
461
|
+
end
|
|
462
|
+
|
|
467
463
|
def failsafe(method, returning: nil)
|
|
468
464
|
yield
|
|
469
465
|
rescue ::Redis::BaseError => error
|
|
470
|
-
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
|
471
466
|
@error_handler&.call(method: method, exception: error, returning: returning)
|
|
472
467
|
returning
|
|
473
468
|
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
require "active_support/core_ext/kernel/reporting"
|
|
5
|
+
|
|
6
|
+
module ActiveSupport
|
|
7
|
+
module Cache
|
|
8
|
+
module SerializerWithFallback # :nodoc:
|
|
9
|
+
def self.[](format)
|
|
10
|
+
if format.to_s.include?("message_pack") && !defined?(ActiveSupport::MessagePack)
|
|
11
|
+
require "active_support/message_pack"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
SERIALIZERS.fetch(format)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def load(dumped)
|
|
18
|
+
if dumped.is_a?(String)
|
|
19
|
+
case
|
|
20
|
+
when MessagePackWithFallback.dumped?(dumped)
|
|
21
|
+
MessagePackWithFallback._load(dumped)
|
|
22
|
+
when Marshal71WithFallback.dumped?(dumped)
|
|
23
|
+
Marshal71WithFallback._load(dumped)
|
|
24
|
+
when Marshal70WithFallback.dumped?(dumped)
|
|
25
|
+
Marshal70WithFallback._load(dumped)
|
|
26
|
+
else
|
|
27
|
+
Cache::Store.logger&.warn("Unrecognized payload prefix #{dumped.byteslice(0).inspect}; deserializing as nil")
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
elsif PassthroughWithFallback.dumped?(dumped)
|
|
31
|
+
PassthroughWithFallback._load(dumped)
|
|
32
|
+
else
|
|
33
|
+
Cache::Store.logger&.warn("Unrecognized payload class #{dumped.class}; deserializing as nil")
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def marshal_load(payload)
|
|
40
|
+
Marshal.load(payload)
|
|
41
|
+
rescue ArgumentError => error
|
|
42
|
+
raise Cache::DeserializationError, error.message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module PassthroughWithFallback
|
|
46
|
+
include SerializerWithFallback
|
|
47
|
+
extend self
|
|
48
|
+
|
|
49
|
+
def dump(entry)
|
|
50
|
+
entry
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def dump_compressed(entry, threshold)
|
|
54
|
+
entry.compressed(threshold)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _load(entry)
|
|
58
|
+
entry
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def dumped?(dumped)
|
|
62
|
+
dumped.is_a?(Cache::Entry)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module Marshal61WithFallback
|
|
67
|
+
include SerializerWithFallback
|
|
68
|
+
extend self
|
|
69
|
+
|
|
70
|
+
MARSHAL_SIGNATURE = "\x04\x08".b.freeze
|
|
71
|
+
|
|
72
|
+
def dump(entry)
|
|
73
|
+
Marshal.dump(entry)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def dump_compressed(entry, threshold)
|
|
77
|
+
Marshal.dump(entry.compressed(threshold))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
alias_method :_load, :marshal_load
|
|
81
|
+
public :_load
|
|
82
|
+
|
|
83
|
+
def dumped?(dumped)
|
|
84
|
+
dumped.start_with?(MARSHAL_SIGNATURE)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
module Marshal70WithFallback
|
|
89
|
+
include SerializerWithFallback
|
|
90
|
+
extend self
|
|
91
|
+
|
|
92
|
+
MARK_UNCOMPRESSED = "\x00".b.freeze
|
|
93
|
+
MARK_COMPRESSED = "\x01".b.freeze
|
|
94
|
+
|
|
95
|
+
def dump(entry)
|
|
96
|
+
MARK_UNCOMPRESSED + Marshal.dump(entry.pack)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def dump_compressed(entry, threshold)
|
|
100
|
+
dumped = Marshal.dump(entry.pack)
|
|
101
|
+
|
|
102
|
+
if dumped.bytesize >= threshold
|
|
103
|
+
compressed = Zlib::Deflate.deflate(dumped)
|
|
104
|
+
return MARK_COMPRESSED + compressed if compressed.bytesize < dumped.bytesize
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
MARK_UNCOMPRESSED + dumped
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def _load(marked)
|
|
111
|
+
dumped = marked.byteslice(1..-1)
|
|
112
|
+
dumped = Zlib::Inflate.inflate(dumped) if marked.start_with?(MARK_COMPRESSED)
|
|
113
|
+
Cache::Entry.unpack(marshal_load(dumped))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def dumped?(dumped)
|
|
117
|
+
dumped.start_with?(MARK_UNCOMPRESSED, MARK_COMPRESSED)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
module Marshal71WithFallback
|
|
122
|
+
include SerializerWithFallback
|
|
123
|
+
extend self
|
|
124
|
+
|
|
125
|
+
MARSHAL_SIGNATURE = "\x04\x08".b.freeze
|
|
126
|
+
|
|
127
|
+
def dump(value)
|
|
128
|
+
Marshal.dump(value)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def _load(dumped)
|
|
132
|
+
marshal_load(dumped)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def dumped?(dumped)
|
|
136
|
+
dumped.start_with?(MARSHAL_SIGNATURE)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
module MessagePackWithFallback
|
|
141
|
+
include SerializerWithFallback
|
|
142
|
+
extend self
|
|
143
|
+
|
|
144
|
+
def dump(value)
|
|
145
|
+
ActiveSupport::MessagePack::CacheSerializer.dump(value)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def _load(dumped)
|
|
149
|
+
ActiveSupport::MessagePack::CacheSerializer.load(dumped)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def dumped?(dumped)
|
|
153
|
+
available? && ActiveSupport::MessagePack.signature?(dumped)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
def available?
|
|
158
|
+
return @available if defined?(@available)
|
|
159
|
+
silence_warnings { require "active_support/message_pack" }
|
|
160
|
+
@available = true
|
|
161
|
+
rescue LoadError
|
|
162
|
+
@available = false
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
SERIALIZERS = {
|
|
167
|
+
passthrough: PassthroughWithFallback,
|
|
168
|
+
marshal_6_1: Marshal61WithFallback,
|
|
169
|
+
marshal_7_0: Marshal70WithFallback,
|
|
170
|
+
marshal_7_1: Marshal71WithFallback,
|
|
171
|
+
message_pack: MessagePackWithFallback,
|
|
172
|
+
}
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -5,6 +5,8 @@ require "active_support/core_ext/string/inflections"
|
|
|
5
5
|
module ActiveSupport
|
|
6
6
|
module Cache
|
|
7
7
|
module Strategy
|
|
8
|
+
# = Local \Cache \Strategy
|
|
9
|
+
#
|
|
8
10
|
# Caches that implement LocalCache will be backed by an in-memory cache for the
|
|
9
11
|
# duration of a block. Repeated calls to the cache for the same key will hit the
|
|
10
12
|
# in-memory cache for faster access.
|
|
@@ -26,6 +28,8 @@ module ActiveSupport
|
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
# = Local \Cache \Store
|
|
32
|
+
#
|
|
29
33
|
# Simple memory backed cache. This cache is not thread safe and is intended only
|
|
30
34
|
# for serving as a temporary memory cache for a single thread.
|
|
31
35
|
class LocalStore
|
|
@@ -72,35 +76,43 @@ module ActiveSupport
|
|
|
72
76
|
local_cache_key)
|
|
73
77
|
end
|
|
74
78
|
|
|
75
|
-
def clear(
|
|
79
|
+
def clear(options = nil) # :nodoc:
|
|
76
80
|
return super unless cache = local_cache
|
|
77
81
|
cache.clear(options)
|
|
78
82
|
super
|
|
79
83
|
end
|
|
80
84
|
|
|
81
|
-
def cleanup(
|
|
85
|
+
def cleanup(options = nil) # :nodoc:
|
|
82
86
|
return super unless cache = local_cache
|
|
83
|
-
cache.clear
|
|
87
|
+
cache.clear(options)
|
|
84
88
|
super
|
|
85
89
|
end
|
|
86
90
|
|
|
87
91
|
def delete_matched(matcher, options = nil) # :nodoc:
|
|
88
92
|
return super unless cache = local_cache
|
|
89
|
-
cache.clear
|
|
93
|
+
cache.clear(options)
|
|
90
94
|
super
|
|
91
95
|
end
|
|
92
96
|
|
|
93
|
-
def increment(name, amount = 1,
|
|
97
|
+
def increment(name, amount = 1, options = nil) # :nodoc:
|
|
94
98
|
return super unless local_cache
|
|
95
99
|
value = bypass_local_cache { super }
|
|
96
|
-
|
|
100
|
+
if options
|
|
101
|
+
write_cache_value(name, value, raw: true, **options)
|
|
102
|
+
else
|
|
103
|
+
write_cache_value(name, value, raw: true)
|
|
104
|
+
end
|
|
97
105
|
value
|
|
98
106
|
end
|
|
99
107
|
|
|
100
|
-
def decrement(name, amount = 1,
|
|
108
|
+
def decrement(name, amount = 1, options = nil) # :nodoc:
|
|
101
109
|
return super unless local_cache
|
|
102
110
|
value = bypass_local_cache { super }
|
|
103
|
-
|
|
111
|
+
if options
|
|
112
|
+
write_cache_value(name, value, raw: true, **options)
|
|
113
|
+
else
|
|
114
|
+
write_cache_value(name, value, raw: true)
|
|
115
|
+
end
|
|
104
116
|
value
|
|
105
117
|
end
|
|
106
118
|
|