activesupport 7.0.8.7 → 7.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +229 -397
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +238 -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 +134 -0
- data/lib/active_support/cache/file_store.rb +51 -19
- data/lib/active_support/cache/mem_cache_store.rb +98 -134
- data/lib/active_support/cache/memory_store.rb +85 -30
- data/lib/active_support/cache/null_store.rb +8 -2
- data/lib/active_support/cache/redis_cache_store.rb +166 -153
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +64 -13
- data/lib/active_support/cache.rb +364 -292
- data/lib/active_support/callbacks.rb +121 -136
- data/lib/active_support/code_generator.rb +15 -10
- 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 +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/benchmark.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +2 -2
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- 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_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +6 -4
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +20 -80
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- 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 +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +3 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/object/with.rb +46 -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 +20 -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 +40 -0
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +2 -2
- data/lib/active_support/core_ext/securerandom.rb +24 -12
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +24 -18
- 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/multibyte.rb +3 -3
- data/lib/active_support/core_ext/string/output_safety.rb +34 -177
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +36 -30
- data/lib/active_support/core_ext/time/compatibility.rb +24 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- 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/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +60 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +9 -4
- 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 +163 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +5 -6
- data/lib/active_support/file_update_checker.rb +6 -4
- data/lib/active_support/fork_tracker.rb +4 -32
- 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 +50 -30
- data/lib/active_support/html_safe_translation.rb +19 -6
- 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 +3 -2
- data/lib/active_support/json/encoding.rb +48 -48
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +7 -5
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -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 +229 -89
- data/lib/active_support/message_verifiers.rb +137 -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 +38 -31
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +8 -3
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +29 -28
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- 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 +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- 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 +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +156 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +21 -9
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +18 -2
- data/lib/active_support/testing/parallelization/worker.rb +2 -2
- data/lib/active_support/testing/parallelization.rb +12 -1
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +12 -18
- data/lib/active_support/values/time_zone.rb +25 -14
- 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 +14 -3
- data/lib/active_support.rb +15 -3
- metadata +142 -24
- 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 -10
- 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/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
data/lib/active_support/cache.rb
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
require "zlib"
|
|
4
4
|
require "active_support/core_ext/array/extract_options"
|
|
5
|
-
require "active_support/core_ext/array/wrap"
|
|
6
5
|
require "active_support/core_ext/enumerable"
|
|
7
6
|
require "active_support/core_ext/module/attribute_accessors"
|
|
8
7
|
require "active_support/core_ext/numeric/bytes"
|
|
9
|
-
require "active_support/core_ext/numeric/time"
|
|
10
8
|
require "active_support/core_ext/object/to_param"
|
|
11
9
|
require "active_support/core_ext/object/try"
|
|
12
10
|
require "active_support/core_ext/string/inflections"
|
|
11
|
+
require_relative "cache/coder"
|
|
12
|
+
require_relative "cache/entry"
|
|
13
|
+
require_relative "cache/serializer_with_fallback"
|
|
13
14
|
|
|
14
15
|
module ActiveSupport
|
|
15
16
|
# See ActiveSupport::Cache::Store for documentation.
|
|
@@ -22,20 +23,37 @@ module ActiveSupport
|
|
|
22
23
|
|
|
23
24
|
# These options mean something to all cache implementations. Individual cache
|
|
24
25
|
# implementations may support additional options.
|
|
25
|
-
UNIVERSAL_OPTIONS = [
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
UNIVERSAL_OPTIONS = [
|
|
27
|
+
:coder,
|
|
28
|
+
:compress,
|
|
29
|
+
:compress_threshold,
|
|
30
|
+
:compressor,
|
|
31
|
+
:expire_in,
|
|
32
|
+
:expired_in,
|
|
33
|
+
:expires_in,
|
|
34
|
+
:namespace,
|
|
35
|
+
:race_condition_ttl,
|
|
36
|
+
:serializer,
|
|
37
|
+
:skip_nil,
|
|
38
|
+
:raw,
|
|
39
|
+
]
|
|
28
40
|
|
|
29
41
|
# Mapping of canonical option names to aliases that a store will recognize.
|
|
30
42
|
OPTION_ALIASES = {
|
|
31
43
|
expires_in: [:expire_in, :expired_in]
|
|
32
44
|
}.freeze
|
|
33
45
|
|
|
46
|
+
DEFAULT_COMPRESS_LIMIT = 1.kilobyte
|
|
47
|
+
|
|
48
|
+
# Raised by coders when the cache entry can't be deserialized.
|
|
49
|
+
# This error is treated as a cache miss.
|
|
50
|
+
DeserializationError = Class.new(StandardError)
|
|
51
|
+
|
|
34
52
|
module Strategy
|
|
35
53
|
autoload :LocalCache, "active_support/cache/strategy/local_cache"
|
|
36
54
|
end
|
|
37
55
|
|
|
38
|
-
@format_version =
|
|
56
|
+
@format_version = 7.0
|
|
39
57
|
|
|
40
58
|
class << self
|
|
41
59
|
attr_accessor :format_version
|
|
@@ -69,13 +87,7 @@ module ActiveSupport
|
|
|
69
87
|
case store
|
|
70
88
|
when Symbol
|
|
71
89
|
options = parameters.extract_options!
|
|
72
|
-
|
|
73
|
-
# see https://github.com/rails/rails/pull/41522#discussion_r581186602
|
|
74
|
-
if options.empty?
|
|
75
|
-
retrieve_store_class(store).new(*parameters)
|
|
76
|
-
else
|
|
77
|
-
retrieve_store_class(store).new(*parameters, **options)
|
|
78
|
-
end
|
|
90
|
+
retrieve_store_class(store).new(*parameters, **options)
|
|
79
91
|
when Array
|
|
80
92
|
lookup_store(*store)
|
|
81
93
|
when nil
|
|
@@ -132,6 +144,8 @@ module ActiveSupport
|
|
|
132
144
|
end
|
|
133
145
|
end
|
|
134
146
|
|
|
147
|
+
# = Active Support \Cache \Store
|
|
148
|
+
#
|
|
135
149
|
# An abstract cache store class. There are multiple cache store
|
|
136
150
|
# implementations, each having its own additional features. See the classes
|
|
137
151
|
# under the ActiveSupport::Cache module, e.g.
|
|
@@ -141,13 +155,13 @@ module ActiveSupport
|
|
|
141
155
|
# Some implementations may not support all methods beyond the basic cache
|
|
142
156
|
# methods of #fetch, #write, #read, #exist?, and #delete.
|
|
143
157
|
#
|
|
144
|
-
# ActiveSupport::Cache::Store can store any Ruby object that is supported
|
|
145
|
-
# its +coder+'s +dump+ and +load+ methods.
|
|
158
|
+
# +ActiveSupport::Cache::Store+ can store any Ruby object that is supported
|
|
159
|
+
# by its +coder+'s +dump+ and +load+ methods.
|
|
146
160
|
#
|
|
147
161
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
|
148
162
|
#
|
|
149
163
|
# cache.read('city') # => nil
|
|
150
|
-
# cache.write('city', "Duckburgh")
|
|
164
|
+
# cache.write('city', "Duckburgh") # => true
|
|
151
165
|
# cache.read('city') # => "Duckburgh"
|
|
152
166
|
#
|
|
153
167
|
# cache.write('not serializable', Proc.new {}) # => TypeError
|
|
@@ -174,24 +188,37 @@ module ActiveSupport
|
|
|
174
188
|
#
|
|
175
189
|
class Store
|
|
176
190
|
cattr_accessor :logger, instance_writer: true
|
|
191
|
+
cattr_accessor :raise_on_invalid_cache_expiration_time, default: false
|
|
177
192
|
|
|
178
193
|
attr_reader :silence, :options
|
|
179
194
|
alias :silence? :silence
|
|
180
195
|
|
|
181
196
|
class << self
|
|
182
197
|
private
|
|
198
|
+
DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
|
|
199
|
+
private_constant :DEFAULT_POOL_OPTIONS
|
|
200
|
+
|
|
183
201
|
def retrieve_pool_options(options)
|
|
184
|
-
|
|
185
|
-
pool_options
|
|
186
|
-
|
|
202
|
+
if options.key?(:pool)
|
|
203
|
+
pool_options = options.delete(:pool)
|
|
204
|
+
else
|
|
205
|
+
pool_options = true
|
|
187
206
|
end
|
|
188
|
-
end
|
|
189
207
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
208
|
+
case pool_options
|
|
209
|
+
when false, nil
|
|
210
|
+
return false
|
|
211
|
+
when true
|
|
212
|
+
pool_options = DEFAULT_POOL_OPTIONS
|
|
213
|
+
when Hash
|
|
214
|
+
pool_options[:size] = Integer(pool_options[:size]) if pool_options.key?(:size)
|
|
215
|
+
pool_options[:timeout] = Float(pool_options[:timeout]) if pool_options.key?(:timeout)
|
|
216
|
+
pool_options = DEFAULT_POOL_OPTIONS.merge(pool_options)
|
|
217
|
+
else
|
|
218
|
+
raise TypeError, "Invalid :pool argument, expected Hash, got: #{pool_options.inspect}"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
pool_options unless pool_options.empty?
|
|
195
222
|
end
|
|
196
223
|
end
|
|
197
224
|
|
|
@@ -199,21 +226,90 @@ module ActiveSupport
|
|
|
199
226
|
#
|
|
200
227
|
# ==== Options
|
|
201
228
|
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
# applications.
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
#
|
|
229
|
+
# [+:namespace+]
|
|
230
|
+
# Sets the namespace for the cache. This option is especially useful if
|
|
231
|
+
# your application shares a cache with other applications.
|
|
232
|
+
#
|
|
233
|
+
# [+:serializer+]
|
|
234
|
+
# The serializer for cached values. Must respond to +dump+ and +load+.
|
|
235
|
+
#
|
|
236
|
+
# The default serializer depends on the cache format version (set via
|
|
237
|
+
# +config.active_support.cache_format_version+ when using Rails). The
|
|
238
|
+
# default serializer for each format version includes a fallback
|
|
239
|
+
# mechanism to deserialize values from any format version. This behavior
|
|
240
|
+
# makes it easy to migrate between format versions without invalidating
|
|
241
|
+
# the entire cache.
|
|
242
|
+
#
|
|
243
|
+
# You can also specify <tt>serializer: :message_pack</tt> to use a
|
|
244
|
+
# preconfigured serializer based on ActiveSupport::MessagePack. The
|
|
245
|
+
# +:message_pack+ serializer includes the same deserialization fallback
|
|
246
|
+
# mechanism, allowing easy migration from (or to) the default
|
|
247
|
+
# serializer. The +:message_pack+ serializer may improve performance,
|
|
248
|
+
# but it requires the +msgpack+ gem.
|
|
249
|
+
#
|
|
250
|
+
# [+:compressor+]
|
|
251
|
+
# The compressor for serialized cache values. Must respond to +deflate+
|
|
252
|
+
# and +inflate+.
|
|
253
|
+
#
|
|
254
|
+
# The default compressor is +Zlib+. To define a new custom compressor
|
|
255
|
+
# that also decompresses old cache entries, you can check compressed
|
|
256
|
+
# values for Zlib's <tt>"\x78"</tt> signature:
|
|
257
|
+
#
|
|
258
|
+
# module MyCompressor
|
|
259
|
+
# def self.deflate(dumped)
|
|
260
|
+
# # compression logic... (make sure result does not start with "\x78"!)
|
|
261
|
+
# end
|
|
262
|
+
#
|
|
263
|
+
# def self.inflate(compressed)
|
|
264
|
+
# if compressed.start_with?("\x78")
|
|
265
|
+
# Zlib.inflate(compressed)
|
|
266
|
+
# else
|
|
267
|
+
# # decompression logic...
|
|
268
|
+
# end
|
|
269
|
+
# end
|
|
270
|
+
# end
|
|
271
|
+
#
|
|
272
|
+
# ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor)
|
|
273
|
+
#
|
|
274
|
+
# [+:coder+]
|
|
275
|
+
# The coder for serializing and (optionally) compressing cache entries.
|
|
276
|
+
# Must respond to +dump+ and +load+.
|
|
277
|
+
#
|
|
278
|
+
# The default coder composes the serializer and compressor, and includes
|
|
279
|
+
# some performance optimizations. If you only need to override the
|
|
280
|
+
# serializer or compressor, you should specify the +:serializer+ or
|
|
281
|
+
# +:compressor+ options instead.
|
|
282
|
+
#
|
|
283
|
+
# If the store can handle cache entries directly, you may also specify
|
|
284
|
+
# <tt>coder: nil</tt> to omit the serializer, compressor, and coder. For
|
|
285
|
+
# example, if you are using ActiveSupport::Cache::MemoryStore and can
|
|
286
|
+
# guarantee that cache values will not be mutated, you can specify
|
|
287
|
+
# <tt>coder: nil</tt> to avoid the overhead of safeguarding against
|
|
288
|
+
# mutation.
|
|
289
|
+
#
|
|
290
|
+
# The +:coder+ option is mutally exclusive with the +:serializer+ and
|
|
291
|
+
# +:compressor+ options. Specifying them together will raise an
|
|
292
|
+
# +ArgumentError+.
|
|
208
293
|
#
|
|
209
294
|
# Any other specified options are treated as default options for the
|
|
210
295
|
# relevant cache operations, such as #read, #write, and #fetch.
|
|
211
296
|
def initialize(options = nil)
|
|
212
|
-
@options = options ? normalize_options(options) : {}
|
|
297
|
+
@options = options ? validate_options(normalize_options(options)) : {}
|
|
298
|
+
|
|
213
299
|
@options[:compress] = true unless @options.key?(:compress)
|
|
214
|
-
@options[:compress_threshold]
|
|
300
|
+
@options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
|
|
301
|
+
|
|
302
|
+
@coder = @options.delete(:coder) do
|
|
303
|
+
legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
|
|
304
|
+
serializer = @options.delete(:serializer) || default_serializer
|
|
305
|
+
serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol)
|
|
306
|
+
compressor = @options.delete(:compressor) { Zlib }
|
|
307
|
+
|
|
308
|
+
Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
@coder ||= Cache::SerializerWithFallback[:passthrough]
|
|
215
312
|
|
|
216
|
-
@coder = @options.delete(:coder) { default_coder } || NullCoder
|
|
217
313
|
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
|
|
218
314
|
end
|
|
219
315
|
|
|
@@ -225,7 +321,7 @@ module ActiveSupport
|
|
|
225
321
|
|
|
226
322
|
# Silences the logger within a block.
|
|
227
323
|
def mute
|
|
228
|
-
previous_silence, @silence =
|
|
324
|
+
previous_silence, @silence = @silence, true
|
|
229
325
|
yield
|
|
230
326
|
ensure
|
|
231
327
|
@silence = previous_silence
|
|
@@ -251,8 +347,8 @@ module ActiveSupport
|
|
|
251
347
|
#
|
|
252
348
|
# ==== Options
|
|
253
349
|
#
|
|
254
|
-
# Internally, +fetch+ calls
|
|
255
|
-
# miss. Thus, +fetch+ supports the same options as #read and #write.
|
|
350
|
+
# Internally, +fetch+ calls +read_entry+, and calls +write_entry+ on a
|
|
351
|
+
# cache miss. Thus, +fetch+ supports the same options as #read and #write.
|
|
256
352
|
# Additionally, +fetch+ supports the following options:
|
|
257
353
|
#
|
|
258
354
|
# * <tt>force: true</tt> - Forces a cache "miss," meaning we treat the
|
|
@@ -291,32 +387,60 @@ module ActiveSupport
|
|
|
291
387
|
# process can try to generate a new value after the extended time window
|
|
292
388
|
# has elapsed.
|
|
293
389
|
#
|
|
294
|
-
# # Set all values to expire after one
|
|
295
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1
|
|
390
|
+
# # Set all values to expire after one second.
|
|
391
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
|
|
296
392
|
#
|
|
297
|
-
# cache.write(
|
|
393
|
+
# cache.write("foo", "original value")
|
|
298
394
|
# val_1 = nil
|
|
299
395
|
# val_2 = nil
|
|
300
|
-
#
|
|
396
|
+
# p cache.read("foo") # => "original value"
|
|
301
397
|
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
398
|
+
# sleep 1 # wait until the cache expires
|
|
399
|
+
#
|
|
400
|
+
# t1 = Thread.new do
|
|
401
|
+
# # fetch does the following:
|
|
402
|
+
# # 1. gets an recent expired entry
|
|
403
|
+
# # 2. extends the expiry by 2 seconds (race_condition_ttl)
|
|
404
|
+
# # 3. regenerates the new value
|
|
405
|
+
# val_1 = cache.fetch("foo", race_condition_ttl: 2) do
|
|
304
406
|
# sleep 1
|
|
305
|
-
#
|
|
407
|
+
# "new value 1"
|
|
306
408
|
# end
|
|
307
409
|
# end
|
|
308
410
|
#
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
411
|
+
# # Wait until t1 extends the expiry of the entry
|
|
412
|
+
# # but before generating the new value
|
|
413
|
+
# sleep 0.1
|
|
414
|
+
#
|
|
415
|
+
# val_2 = cache.fetch("foo", race_condition_ttl: 2) do
|
|
416
|
+
# # This block won't be executed because t1 extended the expiry
|
|
417
|
+
# "new value 2"
|
|
313
418
|
# end
|
|
314
419
|
#
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
318
|
-
#
|
|
319
|
-
#
|
|
420
|
+
# t1.join
|
|
421
|
+
#
|
|
422
|
+
# p val_1 # => "new value 1"
|
|
423
|
+
# p val_2 # => "oritinal value"
|
|
424
|
+
# p cache.fetch("foo") # => "new value 1"
|
|
425
|
+
#
|
|
426
|
+
# # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
|
|
427
|
+
# # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1
|
|
428
|
+
# # more second to see the entry expire.
|
|
429
|
+
# sleep 1
|
|
430
|
+
#
|
|
431
|
+
# p cache.fetch("foo") # => nil
|
|
432
|
+
#
|
|
433
|
+
# ==== Dynamic Options
|
|
434
|
+
#
|
|
435
|
+
# In some cases it may be necessary to dynamically compute options based
|
|
436
|
+
# on the cached value. To support this, an ActiveSupport::Cache::WriteOptions
|
|
437
|
+
# instance is passed as the second argument to the block. For example:
|
|
438
|
+
#
|
|
439
|
+
# cache.fetch("authentication-token:#{user.id}") do |key, options|
|
|
440
|
+
# token = authenticate_to_service
|
|
441
|
+
# options.expires_at = token.expires_at
|
|
442
|
+
# token
|
|
443
|
+
# end
|
|
320
444
|
#
|
|
321
445
|
def fetch(name, options = nil, &block)
|
|
322
446
|
if block_given?
|
|
@@ -324,18 +448,30 @@ module ActiveSupport
|
|
|
324
448
|
key = normalize_key(name, options)
|
|
325
449
|
|
|
326
450
|
entry = nil
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
451
|
+
unless options[:force]
|
|
452
|
+
instrument(:read, key, options) do |payload|
|
|
453
|
+
cached_entry = read_entry(key, **options, event: payload)
|
|
454
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
|
455
|
+
if entry
|
|
456
|
+
if entry.mismatched?(normalize_version(name, options))
|
|
457
|
+
entry = nil
|
|
458
|
+
else
|
|
459
|
+
begin
|
|
460
|
+
entry.value
|
|
461
|
+
rescue DeserializationError
|
|
462
|
+
entry = nil
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
payload[:super_operation] = :fetch if payload
|
|
467
|
+
payload[:hit] = !!entry if payload
|
|
468
|
+
end
|
|
333
469
|
end
|
|
334
470
|
|
|
335
471
|
if entry
|
|
336
472
|
get_entry_value(entry, name, options)
|
|
337
473
|
else
|
|
338
|
-
save_block_result_to_cache(name, options, &block)
|
|
474
|
+
save_block_result_to_cache(name, key, options, &block)
|
|
339
475
|
end
|
|
340
476
|
elsif options && options[:force]
|
|
341
477
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
|
@@ -354,6 +490,7 @@ module ActiveSupport
|
|
|
354
490
|
#
|
|
355
491
|
# ==== Options
|
|
356
492
|
#
|
|
493
|
+
# * +:namespace+ - Replace the store namespace for this call.
|
|
357
494
|
# * +:version+ - Specifies a version for the cache entry. If the cached
|
|
358
495
|
# version does not match the requested version, the read will be treated
|
|
359
496
|
# as a cache miss. This feature is used to support recyclable cache keys.
|
|
@@ -364,7 +501,7 @@ module ActiveSupport
|
|
|
364
501
|
key = normalize_key(name, options)
|
|
365
502
|
version = normalize_version(name, options)
|
|
366
503
|
|
|
367
|
-
instrument(:read,
|
|
504
|
+
instrument(:read, key, options) do |payload|
|
|
368
505
|
entry = read_entry(key, **options, event: payload)
|
|
369
506
|
|
|
370
507
|
if entry
|
|
@@ -377,7 +514,12 @@ module ActiveSupport
|
|
|
377
514
|
nil
|
|
378
515
|
else
|
|
379
516
|
payload[:hit] = true if payload
|
|
380
|
-
|
|
517
|
+
begin
|
|
518
|
+
entry.value
|
|
519
|
+
rescue DeserializationError
|
|
520
|
+
payload[:hit] = false
|
|
521
|
+
nil
|
|
522
|
+
end
|
|
381
523
|
end
|
|
382
524
|
else
|
|
383
525
|
payload[:hit] = false if payload
|
|
@@ -393,10 +535,12 @@ module ActiveSupport
|
|
|
393
535
|
#
|
|
394
536
|
# Returns a hash mapping the names provided to the values found.
|
|
395
537
|
def read_multi(*names)
|
|
538
|
+
return {} if names.empty?
|
|
539
|
+
|
|
396
540
|
options = names.extract_options!
|
|
397
541
|
options = merged_options(options)
|
|
398
542
|
|
|
399
|
-
|
|
543
|
+
instrument_multi :read_multi, names, options do |payload|
|
|
400
544
|
read_multi_entries(names, **options, event: payload).tap do |results|
|
|
401
545
|
payload[:hits] = results.keys
|
|
402
546
|
end
|
|
@@ -405,9 +549,11 @@ module ActiveSupport
|
|
|
405
549
|
|
|
406
550
|
# Cache Storage API to write multiple values at once.
|
|
407
551
|
def write_multi(hash, options = nil)
|
|
552
|
+
return hash if hash.empty?
|
|
553
|
+
|
|
408
554
|
options = merged_options(options)
|
|
409
555
|
|
|
410
|
-
|
|
556
|
+
instrument_multi :write_multi, hash, options do |payload|
|
|
411
557
|
entries = hash.each_with_object({}) do |(name, value), memo|
|
|
412
558
|
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
|
413
559
|
end
|
|
@@ -433,7 +579,8 @@ module ActiveSupport
|
|
|
433
579
|
# # => { "bim" => "bam",
|
|
434
580
|
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
|
435
581
|
#
|
|
436
|
-
#
|
|
582
|
+
# You may also specify additional options via the +options+ argument. See #fetch for details.
|
|
583
|
+
# Other options are passed to the underlying cache implementation. For example:
|
|
437
584
|
#
|
|
438
585
|
# cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
|
|
439
586
|
# "buzz"
|
|
@@ -446,29 +593,41 @@ module ActiveSupport
|
|
|
446
593
|
# # => nil
|
|
447
594
|
def fetch_multi(*names)
|
|
448
595
|
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
|
|
596
|
+
return {} if names.empty?
|
|
449
597
|
|
|
450
598
|
options = names.extract_options!
|
|
451
599
|
options = merged_options(options)
|
|
452
600
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
601
|
+
writes = {}
|
|
602
|
+
ordered = instrument_multi :read_multi, names, options do |payload|
|
|
603
|
+
if options[:force]
|
|
604
|
+
reads = {}
|
|
605
|
+
else
|
|
606
|
+
reads = read_multi_entries(names, **options)
|
|
607
|
+
end
|
|
608
|
+
|
|
456
609
|
ordered = names.index_with do |name|
|
|
457
610
|
reads.fetch(name) { writes[name] = yield(name) }
|
|
458
611
|
end
|
|
612
|
+
writes.compact! if options[:skip_nil]
|
|
459
613
|
|
|
460
614
|
payload[:hits] = reads.keys
|
|
461
615
|
payload[:super_operation] = :fetch_multi
|
|
462
616
|
|
|
463
|
-
write_multi(writes, options)
|
|
464
|
-
|
|
465
617
|
ordered
|
|
466
618
|
end
|
|
619
|
+
|
|
620
|
+
write_multi(writes, options)
|
|
621
|
+
|
|
622
|
+
ordered
|
|
467
623
|
end
|
|
468
624
|
|
|
469
625
|
# Writes the value to the cache with the key. The value must be supported
|
|
470
626
|
# by the +coder+'s +dump+ and +load+ methods.
|
|
471
627
|
#
|
|
628
|
+
# Returns +true+ if the write succeeded, +nil+ if there was an error talking
|
|
629
|
+
# to the cache backend, or +false+ if the write failed for another reason.
|
|
630
|
+
#
|
|
472
631
|
# By default, cache entries larger than 1kB are compressed. Compression
|
|
473
632
|
# allows more data to be stored in the same memory footprint, leading to
|
|
474
633
|
# fewer cache evictions and higher hit rates.
|
|
@@ -501,32 +660,38 @@ module ActiveSupport
|
|
|
501
660
|
# Other options will be handled by the specific cache store implementation.
|
|
502
661
|
def write(name, value, options = nil)
|
|
503
662
|
options = merged_options(options)
|
|
663
|
+
key = normalize_key(name, options)
|
|
504
664
|
|
|
505
|
-
instrument(:write,
|
|
665
|
+
instrument(:write, key, options) do
|
|
506
666
|
entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
|
507
|
-
write_entry(
|
|
667
|
+
write_entry(key, entry, **options)
|
|
508
668
|
end
|
|
509
669
|
end
|
|
510
670
|
|
|
511
|
-
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
|
671
|
+
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
|
672
|
+
# and +false+ otherwise.
|
|
512
673
|
#
|
|
513
674
|
# Options are passed to the underlying cache implementation.
|
|
514
675
|
def delete(name, options = nil)
|
|
515
676
|
options = merged_options(options)
|
|
677
|
+
key = normalize_key(name, options)
|
|
516
678
|
|
|
517
|
-
instrument(:delete,
|
|
518
|
-
delete_entry(
|
|
679
|
+
instrument(:delete, key, options) do
|
|
680
|
+
delete_entry(key, **options)
|
|
519
681
|
end
|
|
520
682
|
end
|
|
521
683
|
|
|
522
|
-
# Deletes multiple entries in the cache.
|
|
684
|
+
# Deletes multiple entries in the cache. Returns the number of deleted
|
|
685
|
+
# entries.
|
|
523
686
|
#
|
|
524
687
|
# Options are passed to the underlying cache implementation.
|
|
525
688
|
def delete_multi(names, options = nil)
|
|
689
|
+
return 0 if names.empty?
|
|
690
|
+
|
|
526
691
|
options = merged_options(options)
|
|
527
692
|
names.map! { |key| normalize_key(key, options) }
|
|
528
693
|
|
|
529
|
-
|
|
694
|
+
instrument_multi(:delete_multi, names, options) do
|
|
530
695
|
delete_multi_entries(names, **options)
|
|
531
696
|
end
|
|
532
697
|
end
|
|
@@ -536,9 +701,10 @@ module ActiveSupport
|
|
|
536
701
|
# Options are passed to the underlying cache implementation.
|
|
537
702
|
def exist?(name, options = nil)
|
|
538
703
|
options = merged_options(options)
|
|
704
|
+
key = normalize_key(name, options)
|
|
539
705
|
|
|
540
|
-
instrument(:exist?,
|
|
541
|
-
entry = read_entry(
|
|
706
|
+
instrument(:exist?, key) do |payload|
|
|
707
|
+
entry = read_entry(key, **options, event: payload)
|
|
542
708
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
|
543
709
|
end
|
|
544
710
|
end
|
|
@@ -594,8 +760,15 @@ module ActiveSupport
|
|
|
594
760
|
end
|
|
595
761
|
|
|
596
762
|
private
|
|
597
|
-
def
|
|
598
|
-
|
|
763
|
+
def default_serializer
|
|
764
|
+
case Cache.format_version
|
|
765
|
+
when 7.0
|
|
766
|
+
Cache::SerializerWithFallback[:marshal_7_0]
|
|
767
|
+
when 7.1
|
|
768
|
+
Cache::SerializerWithFallback[:marshal_7_1]
|
|
769
|
+
else
|
|
770
|
+
raise ArgumentError, "Unrecognized ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
|
|
771
|
+
end
|
|
599
772
|
end
|
|
600
773
|
|
|
601
774
|
# Adds the namespace defined in the options to a pattern designed to
|
|
@@ -632,14 +805,16 @@ module ActiveSupport
|
|
|
632
805
|
def serialize_entry(entry, **options)
|
|
633
806
|
options = merged_options(options)
|
|
634
807
|
if @coder_supports_compression && options[:compress]
|
|
635
|
-
@coder.dump_compressed(entry, options[:compress_threshold]
|
|
808
|
+
@coder.dump_compressed(entry, options[:compress_threshold])
|
|
636
809
|
else
|
|
637
810
|
@coder.dump(entry)
|
|
638
811
|
end
|
|
639
812
|
end
|
|
640
813
|
|
|
641
|
-
def deserialize_entry(payload)
|
|
814
|
+
def deserialize_entry(payload, **)
|
|
642
815
|
payload.nil? ? nil : @coder.load(payload)
|
|
816
|
+
rescue DeserializationError
|
|
817
|
+
nil
|
|
643
818
|
end
|
|
644
819
|
|
|
645
820
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
|
@@ -685,6 +860,22 @@ module ActiveSupport
|
|
|
685
860
|
def merged_options(call_options)
|
|
686
861
|
if call_options
|
|
687
862
|
call_options = normalize_options(call_options)
|
|
863
|
+
if call_options.key?(:expires_in) && call_options.key?(:expires_at)
|
|
864
|
+
raise ArgumentError, "Either :expires_in or :expires_at can be supplied, but not both"
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
expires_at = call_options.delete(:expires_at)
|
|
868
|
+
call_options[:expires_in] = (expires_at - Time.now) if expires_at
|
|
869
|
+
|
|
870
|
+
if call_options[:expires_in].is_a?(Time)
|
|
871
|
+
expires_in = call_options[:expires_in]
|
|
872
|
+
raise ArgumentError.new("expires_in parameter should not be a Time. Did you mean to use expires_at? Got: #{expires_in}")
|
|
873
|
+
end
|
|
874
|
+
if call_options[:expires_in]&.negative?
|
|
875
|
+
expires_in = call_options.delete(:expires_in)
|
|
876
|
+
handle_invalid_expires_in("Cache expiration time is invalid, cannot be negative: #{expires_in}")
|
|
877
|
+
end
|
|
878
|
+
|
|
688
879
|
if options.empty?
|
|
689
880
|
call_options
|
|
690
881
|
else
|
|
@@ -695,6 +886,16 @@ module ActiveSupport
|
|
|
695
886
|
end
|
|
696
887
|
end
|
|
697
888
|
|
|
889
|
+
def handle_invalid_expires_in(message)
|
|
890
|
+
error = ArgumentError.new(message)
|
|
891
|
+
if ActiveSupport::Cache::Store.raise_on_invalid_cache_expiration_time
|
|
892
|
+
raise error
|
|
893
|
+
else
|
|
894
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
|
895
|
+
logger.error("#{error.class}: #{error.message}") if logger
|
|
896
|
+
end
|
|
897
|
+
end
|
|
898
|
+
|
|
698
899
|
# Normalize aliased options to their canonical form
|
|
699
900
|
def normalize_options(options)
|
|
700
901
|
options = options.dup
|
|
@@ -707,10 +908,31 @@ module ActiveSupport
|
|
|
707
908
|
options
|
|
708
909
|
end
|
|
709
910
|
|
|
710
|
-
|
|
711
|
-
|
|
911
|
+
def validate_options(options)
|
|
912
|
+
if options.key?(:coder) && options[:serializer]
|
|
913
|
+
raise ArgumentError, "Cannot specify :serializer and :coder options together"
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
if options.key?(:coder) && options[:compressor]
|
|
917
|
+
raise ArgumentError, "Cannot specify :compressor and :coder options together"
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
if Cache.format_version < 7.1 && !options[:serializer] && options[:compressor]
|
|
921
|
+
raise ArgumentError, "Cannot specify :compressor option when using" \
|
|
922
|
+
" default serializer and cache format version is < 7.1"
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
options
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
# Expands and namespaces the cache key.
|
|
929
|
+
# Raises an exception when the key is +nil+ or an empty string.
|
|
930
|
+
# May be overridden by cache stores to do additional normalization.
|
|
712
931
|
def normalize_key(key, options = nil)
|
|
713
|
-
|
|
932
|
+
str_key = expanded_key(key)
|
|
933
|
+
raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
|
|
934
|
+
|
|
935
|
+
namespace_key str_key, options
|
|
714
936
|
end
|
|
715
937
|
|
|
716
938
|
# Prefix the key with a namespace string:
|
|
@@ -773,14 +995,33 @@ module ActiveSupport
|
|
|
773
995
|
end
|
|
774
996
|
end
|
|
775
997
|
|
|
776
|
-
def instrument(operation, key, options = nil)
|
|
998
|
+
def instrument(operation, key, options = nil, &block)
|
|
999
|
+
_instrument(operation, key: key, options: options, &block)
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
def instrument_multi(operation, keys, options = nil, &block)
|
|
1003
|
+
_instrument(operation, multi: true, key: keys, options: options, &block)
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
def _instrument(operation, multi: false, options: nil, **payload, &block)
|
|
777
1007
|
if logger && logger.debug? && !silence?
|
|
778
|
-
|
|
1008
|
+
debug_key =
|
|
1009
|
+
if multi
|
|
1010
|
+
": #{payload[:key].size} key(s) specified"
|
|
1011
|
+
elsif payload[:key]
|
|
1012
|
+
": #{payload[:key]}"
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
debug_options = " (#{options.inspect})" unless options.blank?
|
|
1016
|
+
|
|
1017
|
+
logger.debug "Cache #{operation}#{debug_key}#{debug_options}"
|
|
779
1018
|
end
|
|
780
1019
|
|
|
781
|
-
payload =
|
|
1020
|
+
payload[:store] = self.class.name
|
|
782
1021
|
payload.merge!(options) if options.is_a?(Hash)
|
|
783
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload)
|
|
1022
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
|
|
1023
|
+
block&.call(payload)
|
|
1024
|
+
end
|
|
784
1025
|
end
|
|
785
1026
|
|
|
786
1027
|
def handle_expired_entry(entry, key, options)
|
|
@@ -790,7 +1031,7 @@ module ActiveSupport
|
|
|
790
1031
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
|
791
1032
|
# for a brief period while the entry is being recalculated.
|
|
792
1033
|
entry.expires_at = Time.now.to_f + race_ttl
|
|
793
|
-
write_entry(key, entry, expires_in: race_ttl * 2)
|
|
1034
|
+
write_entry(key, entry, **options, expires_in: race_ttl * 2)
|
|
794
1035
|
else
|
|
795
1036
|
delete_entry(key, **options)
|
|
796
1037
|
end
|
|
@@ -800,13 +1041,15 @@ module ActiveSupport
|
|
|
800
1041
|
end
|
|
801
1042
|
|
|
802
1043
|
def get_entry_value(entry, name, options)
|
|
803
|
-
instrument(:fetch_hit, name, options)
|
|
1044
|
+
instrument(:fetch_hit, name, options)
|
|
804
1045
|
entry.value
|
|
805
1046
|
end
|
|
806
1047
|
|
|
807
|
-
def save_block_result_to_cache(name, options)
|
|
808
|
-
|
|
809
|
-
|
|
1048
|
+
def save_block_result_to_cache(name, key, options)
|
|
1049
|
+
options = options.dup
|
|
1050
|
+
|
|
1051
|
+
result = instrument(:generate, key, options) do
|
|
1052
|
+
yield(name, WriteOptions.new(options))
|
|
810
1053
|
end
|
|
811
1054
|
|
|
812
1055
|
write(name, result, options) unless result.nil? && options[:skip_nil]
|
|
@@ -814,217 +1057,46 @@ module ActiveSupport
|
|
|
814
1057
|
end
|
|
815
1058
|
end
|
|
816
1059
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
def dump_compressed(entry, threshold)
|
|
825
|
-
entry.compressed(threshold)
|
|
826
|
-
end
|
|
827
|
-
|
|
828
|
-
def load(payload)
|
|
829
|
-
payload
|
|
830
|
-
end
|
|
831
|
-
end
|
|
832
|
-
|
|
833
|
-
module Coders # :nodoc:
|
|
834
|
-
MARK_61 = "\x04\b".b.freeze # The one set by Marshal.
|
|
835
|
-
MARK_70_UNCOMPRESSED = "\x00".b.freeze
|
|
836
|
-
MARK_70_COMPRESSED = "\x01".b.freeze
|
|
837
|
-
|
|
838
|
-
class << self
|
|
839
|
-
def [](version)
|
|
840
|
-
case version
|
|
841
|
-
when 6.1
|
|
842
|
-
Rails61Coder
|
|
843
|
-
when 7.0
|
|
844
|
-
Rails70Coder
|
|
845
|
-
else
|
|
846
|
-
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
|
|
847
|
-
end
|
|
848
|
-
end
|
|
849
|
-
end
|
|
850
|
-
|
|
851
|
-
module Loader
|
|
852
|
-
extend self
|
|
853
|
-
|
|
854
|
-
def load(payload)
|
|
855
|
-
if !payload.is_a?(String)
|
|
856
|
-
ActiveSupport::Cache::Store.logger&.warn %{Payload wasn't a string, was #{payload.class.name} - couldn't unmarshal, so returning nil."}
|
|
857
|
-
|
|
858
|
-
return nil
|
|
859
|
-
elsif payload.start_with?(MARK_70_UNCOMPRESSED)
|
|
860
|
-
members = Marshal.load(payload.byteslice(1..-1))
|
|
861
|
-
elsif payload.start_with?(MARK_70_COMPRESSED)
|
|
862
|
-
members = Marshal.load(Zlib::Inflate.inflate(payload.byteslice(1..-1)))
|
|
863
|
-
elsif payload.start_with?(MARK_61)
|
|
864
|
-
return Marshal.load(payload)
|
|
865
|
-
else
|
|
866
|
-
ActiveSupport::Cache::Store.logger&.warn %{Invalid cache prefix: #{payload.byteslice(0).inspect}, expected "\\x00" or "\\x01"}
|
|
867
|
-
|
|
868
|
-
return nil
|
|
869
|
-
end
|
|
870
|
-
Entry.unpack(members)
|
|
871
|
-
end
|
|
872
|
-
end
|
|
873
|
-
|
|
874
|
-
module Rails61Coder
|
|
875
|
-
include Loader
|
|
876
|
-
extend self
|
|
877
|
-
|
|
878
|
-
def dump(entry)
|
|
879
|
-
Marshal.dump(entry)
|
|
880
|
-
end
|
|
881
|
-
|
|
882
|
-
def dump_compressed(entry, threshold)
|
|
883
|
-
Marshal.dump(entry.compressed(threshold))
|
|
884
|
-
end
|
|
885
|
-
end
|
|
886
|
-
|
|
887
|
-
module Rails70Coder
|
|
888
|
-
include Loader
|
|
889
|
-
extend self
|
|
890
|
-
|
|
891
|
-
def dump(entry)
|
|
892
|
-
MARK_70_UNCOMPRESSED + Marshal.dump(entry.pack)
|
|
893
|
-
end
|
|
894
|
-
|
|
895
|
-
def dump_compressed(entry, threshold)
|
|
896
|
-
payload = Marshal.dump(entry.pack)
|
|
897
|
-
if payload.bytesize >= threshold
|
|
898
|
-
compressed_payload = Zlib::Deflate.deflate(payload)
|
|
899
|
-
if compressed_payload.bytesize < payload.bytesize
|
|
900
|
-
return MARK_70_COMPRESSED + compressed_payload
|
|
901
|
-
end
|
|
902
|
-
end
|
|
903
|
-
|
|
904
|
-
MARK_70_UNCOMPRESSED + payload
|
|
905
|
-
end
|
|
906
|
-
end
|
|
907
|
-
end
|
|
908
|
-
|
|
909
|
-
# This class is used to represent cache entries. Cache entries have a value, an optional
|
|
910
|
-
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
|
911
|
-
# on the cache. The version is used to support the :version option on the cache for rejecting
|
|
912
|
-
# mismatches.
|
|
913
|
-
#
|
|
914
|
-
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
|
915
|
-
# using short instance variable names that are lazily defined.
|
|
916
|
-
class Entry # :nodoc:
|
|
917
|
-
class << self
|
|
918
|
-
def unpack(members)
|
|
919
|
-
new(members[0], expires_at: members[1], version: members[2])
|
|
920
|
-
end
|
|
1060
|
+
# Enables the dynamic configuration of Cache entry options while ensuring
|
|
1061
|
+
# that conflicting options are not both set. When a block is given to
|
|
1062
|
+
# ActiveSupport::Cache::Store#fetch, the second argument will be an
|
|
1063
|
+
# instance of +WriteOptions+.
|
|
1064
|
+
class WriteOptions
|
|
1065
|
+
def initialize(options) # :nodoc:
|
|
1066
|
+
@options = options
|
|
921
1067
|
end
|
|
922
1068
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
# Creates a new cache entry for the specified value. Options supported are
|
|
926
|
-
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
|
927
|
-
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
|
928
|
-
@value = value
|
|
929
|
-
@version = version
|
|
930
|
-
@created_at = 0.0
|
|
931
|
-
@expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
|
|
932
|
-
@compressed = true if compressed
|
|
1069
|
+
def version
|
|
1070
|
+
@options[:version]
|
|
933
1071
|
end
|
|
934
1072
|
|
|
935
|
-
def
|
|
936
|
-
|
|
1073
|
+
def version=(version)
|
|
1074
|
+
@options[:version] = version
|
|
937
1075
|
end
|
|
938
1076
|
|
|
939
|
-
def
|
|
940
|
-
@
|
|
1077
|
+
def expires_in
|
|
1078
|
+
@options[:expires_in]
|
|
941
1079
|
end
|
|
942
1080
|
|
|
943
|
-
#
|
|
944
|
-
#
|
|
945
|
-
|
|
946
|
-
|
|
1081
|
+
# Sets the Cache entry's +expires_in+ value. If an +expires_at+ option was
|
|
1082
|
+
# previously set, this will unset it since +expires_in+ and +expires_at+
|
|
1083
|
+
# cannot both be set.
|
|
1084
|
+
def expires_in=(expires_in)
|
|
1085
|
+
@options.delete(:expires_at)
|
|
1086
|
+
@options[:expires_in] = expires_in
|
|
947
1087
|
end
|
|
948
1088
|
|
|
949
1089
|
def expires_at
|
|
950
|
-
@
|
|
951
|
-
end
|
|
952
|
-
|
|
953
|
-
def expires_at=(value)
|
|
954
|
-
if value
|
|
955
|
-
@expires_in = value.to_f - @created_at
|
|
956
|
-
else
|
|
957
|
-
@expires_in = nil
|
|
958
|
-
end
|
|
959
|
-
end
|
|
960
|
-
|
|
961
|
-
# Returns the size of the cached value. This could be less than
|
|
962
|
-
# <tt>value.bytesize</tt> if the data is compressed.
|
|
963
|
-
def bytesize
|
|
964
|
-
case value
|
|
965
|
-
when NilClass
|
|
966
|
-
0
|
|
967
|
-
when String
|
|
968
|
-
@value.bytesize
|
|
969
|
-
else
|
|
970
|
-
@s ||= Marshal.dump(@value).bytesize
|
|
971
|
-
end
|
|
1090
|
+
@options[:expires_at]
|
|
972
1091
|
end
|
|
973
1092
|
|
|
974
|
-
|
|
975
|
-
|
|
1093
|
+
# Sets the Cache entry's +expires_at+ value. If an +expires_in+ option was
|
|
1094
|
+
# previously set, this will unset it since +expires_at+ and +expires_in+
|
|
1095
|
+
# cannot both be set.
|
|
1096
|
+
def expires_at=(expires_at)
|
|
1097
|
+
@options.delete(:expires_in)
|
|
1098
|
+
@options[:expires_at] = expires_at
|
|
976
1099
|
end
|
|
977
|
-
|
|
978
|
-
def compressed(compress_threshold)
|
|
979
|
-
return self if compressed?
|
|
980
|
-
|
|
981
|
-
case @value
|
|
982
|
-
when nil, true, false, Numeric
|
|
983
|
-
uncompressed_size = 0
|
|
984
|
-
when String
|
|
985
|
-
uncompressed_size = @value.bytesize
|
|
986
|
-
else
|
|
987
|
-
serialized = Marshal.dump(@value)
|
|
988
|
-
uncompressed_size = serialized.bytesize
|
|
989
|
-
end
|
|
990
|
-
|
|
991
|
-
if uncompressed_size >= compress_threshold
|
|
992
|
-
serialized ||= Marshal.dump(@value)
|
|
993
|
-
compressed = Zlib::Deflate.deflate(serialized)
|
|
994
|
-
|
|
995
|
-
if compressed.bytesize < uncompressed_size
|
|
996
|
-
return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
|
|
997
|
-
end
|
|
998
|
-
end
|
|
999
|
-
self
|
|
1000
|
-
end
|
|
1001
|
-
|
|
1002
|
-
def local?
|
|
1003
|
-
false
|
|
1004
|
-
end
|
|
1005
|
-
|
|
1006
|
-
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
|
1007
|
-
# serialize entries to protect against accidental cache modifications.
|
|
1008
|
-
def dup_value!
|
|
1009
|
-
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
|
1010
|
-
if @value.is_a?(String)
|
|
1011
|
-
@value = @value.dup
|
|
1012
|
-
else
|
|
1013
|
-
@value = Marshal.load(Marshal.dump(@value))
|
|
1014
|
-
end
|
|
1015
|
-
end
|
|
1016
|
-
end
|
|
1017
|
-
|
|
1018
|
-
def pack
|
|
1019
|
-
members = [value, expires_at, version]
|
|
1020
|
-
members.pop while !members.empty? && members.last.nil?
|
|
1021
|
-
members
|
|
1022
|
-
end
|
|
1023
|
-
|
|
1024
|
-
private
|
|
1025
|
-
def uncompress(value)
|
|
1026
|
-
Marshal.load(Zlib::Inflate.inflate(value))
|
|
1027
|
-
end
|
|
1028
1100
|
end
|
|
1029
1101
|
end
|
|
1030
1102
|
end
|