activesupport 7.0.8 → 7.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +142 -428
- 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 +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 +251 -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 +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +94 -128
- data/lib/active_support/cache/memory_store.rb +80 -25
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +165 -152
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +29 -14
- data/lib/active_support/cache.rb +363 -291
- data/lib/active_support/callbacks.rb +118 -134
- 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/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 +2 -2
- 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 +3 -75
- 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 +0 -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/duplicable.rb +24 -15
- 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/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.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +1 -5
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- 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 +1 -1
- 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 +16 -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 +53 -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 +160 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +4 -5
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- 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 +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 +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 +220 -89
- data/lib/active_support/message_verifiers.rb +135 -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 +4 -2
- 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 +3 -0
- 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/strict_warnings.rb +43 -0
- 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 +12 -3
- data/lib/active_support.rb +15 -3
- metadata +145 -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,36 @@ 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
|
+
]
|
28
39
|
|
29
40
|
# Mapping of canonical option names to aliases that a store will recognize.
|
30
41
|
OPTION_ALIASES = {
|
31
42
|
expires_in: [:expire_in, :expired_in]
|
32
43
|
}.freeze
|
33
44
|
|
45
|
+
DEFAULT_COMPRESS_LIMIT = 1.kilobyte
|
46
|
+
|
47
|
+
# Raised by coders when the cache entry can't be deserialized.
|
48
|
+
# This error is treated as a cache miss.
|
49
|
+
DeserializationError = Class.new(StandardError)
|
50
|
+
|
34
51
|
module Strategy
|
35
52
|
autoload :LocalCache, "active_support/cache/strategy/local_cache"
|
36
53
|
end
|
37
54
|
|
38
|
-
@format_version =
|
55
|
+
@format_version = 7.0
|
39
56
|
|
40
57
|
class << self
|
41
58
|
attr_accessor :format_version
|
@@ -69,13 +86,7 @@ module ActiveSupport
|
|
69
86
|
case store
|
70
87
|
when Symbol
|
71
88
|
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
|
89
|
+
retrieve_store_class(store).new(*parameters, **options)
|
79
90
|
when Array
|
80
91
|
lookup_store(*store)
|
81
92
|
when nil
|
@@ -132,6 +143,8 @@ module ActiveSupport
|
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
146
|
+
# = Active Support \Cache \Store
|
147
|
+
#
|
135
148
|
# An abstract cache store class. There are multiple cache store
|
136
149
|
# implementations, each having its own additional features. See the classes
|
137
150
|
# under the ActiveSupport::Cache module, e.g.
|
@@ -141,13 +154,13 @@ module ActiveSupport
|
|
141
154
|
# Some implementations may not support all methods beyond the basic cache
|
142
155
|
# methods of #fetch, #write, #read, #exist?, and #delete.
|
143
156
|
#
|
144
|
-
# ActiveSupport::Cache::Store can store any Ruby object that is supported
|
145
|
-
# its +coder+'s +dump+ and +load+ methods.
|
157
|
+
# +ActiveSupport::Cache::Store+ can store any Ruby object that is supported
|
158
|
+
# by its +coder+'s +dump+ and +load+ methods.
|
146
159
|
#
|
147
160
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
148
161
|
#
|
149
162
|
# cache.read('city') # => nil
|
150
|
-
# cache.write('city', "Duckburgh")
|
163
|
+
# cache.write('city', "Duckburgh") # => true
|
151
164
|
# cache.read('city') # => "Duckburgh"
|
152
165
|
#
|
153
166
|
# cache.write('not serializable', Proc.new {}) # => TypeError
|
@@ -174,24 +187,37 @@ module ActiveSupport
|
|
174
187
|
#
|
175
188
|
class Store
|
176
189
|
cattr_accessor :logger, instance_writer: true
|
190
|
+
cattr_accessor :raise_on_invalid_cache_expiration_time, default: false
|
177
191
|
|
178
192
|
attr_reader :silence, :options
|
179
193
|
alias :silence? :silence
|
180
194
|
|
181
195
|
class << self
|
182
196
|
private
|
197
|
+
DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
|
198
|
+
private_constant :DEFAULT_POOL_OPTIONS
|
199
|
+
|
183
200
|
def retrieve_pool_options(options)
|
184
|
-
|
185
|
-
pool_options
|
186
|
-
|
201
|
+
if options.key?(:pool)
|
202
|
+
pool_options = options.delete(:pool)
|
203
|
+
else
|
204
|
+
pool_options = true
|
187
205
|
end
|
188
|
-
end
|
189
206
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
207
|
+
case pool_options
|
208
|
+
when false, nil
|
209
|
+
return false
|
210
|
+
when true
|
211
|
+
pool_options = DEFAULT_POOL_OPTIONS
|
212
|
+
when Hash
|
213
|
+
pool_options[:size] = Integer(pool_options[:size]) if pool_options.key?(:size)
|
214
|
+
pool_options[:timeout] = Float(pool_options[:timeout]) if pool_options.key?(:timeout)
|
215
|
+
pool_options = DEFAULT_POOL_OPTIONS.merge(pool_options)
|
216
|
+
else
|
217
|
+
raise TypeError, "Invalid :pool argument, expected Hash, got: #{pool_options.inspect}"
|
218
|
+
end
|
219
|
+
|
220
|
+
pool_options unless pool_options.empty?
|
195
221
|
end
|
196
222
|
end
|
197
223
|
|
@@ -199,21 +225,90 @@ module ActiveSupport
|
|
199
225
|
#
|
200
226
|
# ==== Options
|
201
227
|
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
# applications.
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
228
|
+
# [+:namespace+]
|
229
|
+
# Sets the namespace for the cache. This option is especially useful if
|
230
|
+
# your application shares a cache with other applications.
|
231
|
+
#
|
232
|
+
# [+:serializer+]
|
233
|
+
# The serializer for cached values. Must respond to +dump+ and +load+.
|
234
|
+
#
|
235
|
+
# The default serializer depends on the cache format version (set via
|
236
|
+
# +config.active_support.cache_format_version+ when using Rails). The
|
237
|
+
# default serializer for each format version includes a fallback
|
238
|
+
# mechanism to deserialize values from any format version. This behavior
|
239
|
+
# makes it easy to migrate between format versions without invalidating
|
240
|
+
# the entire cache.
|
241
|
+
#
|
242
|
+
# You can also specify <tt>serializer: :message_pack</tt> to use a
|
243
|
+
# preconfigured serializer based on ActiveSupport::MessagePack. The
|
244
|
+
# +:message_pack+ serializer includes the same deserialization fallback
|
245
|
+
# mechanism, allowing easy migration from (or to) the default
|
246
|
+
# serializer. The +:message_pack+ serializer may improve performance,
|
247
|
+
# but it requires the +msgpack+ gem.
|
248
|
+
#
|
249
|
+
# [+:compressor+]
|
250
|
+
# The compressor for serialized cache values. Must respond to +deflate+
|
251
|
+
# and +inflate+.
|
252
|
+
#
|
253
|
+
# The default compressor is +Zlib+. To define a new custom compressor
|
254
|
+
# that also decompresses old cache entries, you can check compressed
|
255
|
+
# values for Zlib's <tt>"\x78"</tt> signature:
|
256
|
+
#
|
257
|
+
# module MyCompressor
|
258
|
+
# def self.deflate(dumped)
|
259
|
+
# # compression logic... (make sure result does not start with "\x78"!)
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# def self.inflate(compressed)
|
263
|
+
# if compressed.start_with?("\x78")
|
264
|
+
# Zlib.inflate(compressed)
|
265
|
+
# else
|
266
|
+
# # decompression logic...
|
267
|
+
# end
|
268
|
+
# end
|
269
|
+
# end
|
270
|
+
#
|
271
|
+
# ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor)
|
272
|
+
#
|
273
|
+
# [+:coder+]
|
274
|
+
# The coder for serializing and (optionally) compressing cache entries.
|
275
|
+
# Must respond to +dump+ and +load+.
|
276
|
+
#
|
277
|
+
# The default coder composes the serializer and compressor, and includes
|
278
|
+
# some performance optimizations. If you only need to override the
|
279
|
+
# serializer or compressor, you should specify the +:serializer+ or
|
280
|
+
# +:compressor+ options instead.
|
281
|
+
#
|
282
|
+
# If the store can handle cache entries directly, you may also specify
|
283
|
+
# <tt>coder: nil</tt> to omit the serializer, compressor, and coder. For
|
284
|
+
# example, if you are using ActiveSupport::Cache::MemoryStore and can
|
285
|
+
# guarantee that cache values will not be mutated, you can specify
|
286
|
+
# <tt>coder: nil</tt> to avoid the overhead of safeguarding against
|
287
|
+
# mutation.
|
288
|
+
#
|
289
|
+
# The +:coder+ option is mutally exclusive with the +:serializer+ and
|
290
|
+
# +:compressor+ options. Specifying them together will raise an
|
291
|
+
# +ArgumentError+.
|
208
292
|
#
|
209
293
|
# Any other specified options are treated as default options for the
|
210
294
|
# relevant cache operations, such as #read, #write, and #fetch.
|
211
295
|
def initialize(options = nil)
|
212
|
-
@options = options ? normalize_options(options) : {}
|
296
|
+
@options = options ? validate_options(normalize_options(options)) : {}
|
297
|
+
|
213
298
|
@options[:compress] = true unless @options.key?(:compress)
|
214
|
-
@options[:compress_threshold]
|
299
|
+
@options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
|
300
|
+
|
301
|
+
@coder = @options.delete(:coder) do
|
302
|
+
legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
|
303
|
+
serializer = @options.delete(:serializer) || default_serializer
|
304
|
+
serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol)
|
305
|
+
compressor = @options.delete(:compressor) { Zlib }
|
306
|
+
|
307
|
+
Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer)
|
308
|
+
end
|
309
|
+
|
310
|
+
@coder ||= Cache::SerializerWithFallback[:passthrough]
|
215
311
|
|
216
|
-
@coder = @options.delete(:coder) { default_coder } || NullCoder
|
217
312
|
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
|
218
313
|
end
|
219
314
|
|
@@ -225,7 +320,7 @@ module ActiveSupport
|
|
225
320
|
|
226
321
|
# Silences the logger within a block.
|
227
322
|
def mute
|
228
|
-
previous_silence, @silence =
|
323
|
+
previous_silence, @silence = @silence, true
|
229
324
|
yield
|
230
325
|
ensure
|
231
326
|
@silence = previous_silence
|
@@ -251,8 +346,8 @@ module ActiveSupport
|
|
251
346
|
#
|
252
347
|
# ==== Options
|
253
348
|
#
|
254
|
-
# Internally, +fetch+ calls
|
255
|
-
# miss. Thus, +fetch+ supports the same options as #read and #write.
|
349
|
+
# Internally, +fetch+ calls +read_entry+, and calls +write_entry+ on a
|
350
|
+
# cache miss. Thus, +fetch+ supports the same options as #read and #write.
|
256
351
|
# Additionally, +fetch+ supports the following options:
|
257
352
|
#
|
258
353
|
# * <tt>force: true</tt> - Forces a cache "miss," meaning we treat the
|
@@ -292,31 +387,59 @@ module ActiveSupport
|
|
292
387
|
# has elapsed.
|
293
388
|
#
|
294
389
|
# # Set all values to expire after one minute.
|
295
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1
|
390
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
|
296
391
|
#
|
297
|
-
# cache.write(
|
392
|
+
# cache.write("foo", "original value")
|
298
393
|
# val_1 = nil
|
299
394
|
# val_2 = nil
|
300
|
-
#
|
395
|
+
# p cache.read("foo") # => "original value"
|
301
396
|
#
|
302
|
-
#
|
303
|
-
#
|
397
|
+
# sleep 1 # wait until the cache expires
|
398
|
+
#
|
399
|
+
# t1 = Thread.new do
|
400
|
+
# # fetch does the following:
|
401
|
+
# # 1. gets an recent expired entry
|
402
|
+
# # 2. extends the expiry by 2 seconds (race_condition_ttl)
|
403
|
+
# # 3. regenerates the new value
|
404
|
+
# val_1 = cache.fetch("foo", race_condition_ttl: 2) do
|
304
405
|
# sleep 1
|
305
|
-
#
|
406
|
+
# "new value 1"
|
306
407
|
# end
|
307
408
|
# end
|
308
409
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
410
|
+
# # Wait until t1 extends the expiry of the entry
|
411
|
+
# # but before generating the new value
|
412
|
+
# sleep 0.1
|
413
|
+
#
|
414
|
+
# val_2 = cache.fetch("foo", race_condition_ttl: 2) do
|
415
|
+
# # This block won't be executed because t1 extended the expiry
|
416
|
+
# "new value 2"
|
313
417
|
# end
|
314
418
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
419
|
+
# t1.join
|
420
|
+
#
|
421
|
+
# p val_1 # => "new value 1"
|
422
|
+
# p val_2 # => "oritinal value"
|
423
|
+
# p cache.fetch("foo") # => "new value 1"
|
424
|
+
#
|
425
|
+
# # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
|
426
|
+
# # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1
|
427
|
+
# # more second to see the entry expire.
|
428
|
+
# sleep 1
|
429
|
+
#
|
430
|
+
# p cache.fetch("foo") # => nil
|
431
|
+
#
|
432
|
+
# ==== Dynamic Options
|
433
|
+
#
|
434
|
+
# In some cases it may be necessary to dynamically compute options based
|
435
|
+
# on the cached value. To support this, an ActiveSupport::Cache::WriteOptions
|
436
|
+
# instance is passed as the second argument to the block. For example:
|
437
|
+
#
|
438
|
+
# cache.fetch("authentication-token:#{user.id}") do |key, options|
|
439
|
+
# token = authenticate_to_service
|
440
|
+
# options.expires_at = token.expires_at
|
441
|
+
# token
|
442
|
+
# end
|
320
443
|
#
|
321
444
|
def fetch(name, options = nil, &block)
|
322
445
|
if block_given?
|
@@ -324,18 +447,30 @@ module ActiveSupport
|
|
324
447
|
key = normalize_key(name, options)
|
325
448
|
|
326
449
|
entry = nil
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
450
|
+
unless options[:force]
|
451
|
+
instrument(:read, key, options) do |payload|
|
452
|
+
cached_entry = read_entry(key, **options, event: payload)
|
453
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
454
|
+
if entry
|
455
|
+
if entry.mismatched?(normalize_version(name, options))
|
456
|
+
entry = nil
|
457
|
+
else
|
458
|
+
begin
|
459
|
+
entry.value
|
460
|
+
rescue DeserializationError
|
461
|
+
entry = nil
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
payload[:super_operation] = :fetch if payload
|
466
|
+
payload[:hit] = !!entry if payload
|
467
|
+
end
|
333
468
|
end
|
334
469
|
|
335
470
|
if entry
|
336
471
|
get_entry_value(entry, name, options)
|
337
472
|
else
|
338
|
-
save_block_result_to_cache(name, options, &block)
|
473
|
+
save_block_result_to_cache(name, key, options, &block)
|
339
474
|
end
|
340
475
|
elsif options && options[:force]
|
341
476
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -354,6 +489,7 @@ module ActiveSupport
|
|
354
489
|
#
|
355
490
|
# ==== Options
|
356
491
|
#
|
492
|
+
# * +:namespace+ - Replace the store namespace for this call.
|
357
493
|
# * +:version+ - Specifies a version for the cache entry. If the cached
|
358
494
|
# version does not match the requested version, the read will be treated
|
359
495
|
# as a cache miss. This feature is used to support recyclable cache keys.
|
@@ -364,7 +500,7 @@ module ActiveSupport
|
|
364
500
|
key = normalize_key(name, options)
|
365
501
|
version = normalize_version(name, options)
|
366
502
|
|
367
|
-
instrument(:read,
|
503
|
+
instrument(:read, key, options) do |payload|
|
368
504
|
entry = read_entry(key, **options, event: payload)
|
369
505
|
|
370
506
|
if entry
|
@@ -377,7 +513,12 @@ module ActiveSupport
|
|
377
513
|
nil
|
378
514
|
else
|
379
515
|
payload[:hit] = true if payload
|
380
|
-
|
516
|
+
begin
|
517
|
+
entry.value
|
518
|
+
rescue DeserializationError
|
519
|
+
payload[:hit] = false
|
520
|
+
nil
|
521
|
+
end
|
381
522
|
end
|
382
523
|
else
|
383
524
|
payload[:hit] = false if payload
|
@@ -393,10 +534,12 @@ module ActiveSupport
|
|
393
534
|
#
|
394
535
|
# Returns a hash mapping the names provided to the values found.
|
395
536
|
def read_multi(*names)
|
537
|
+
return {} if names.empty?
|
538
|
+
|
396
539
|
options = names.extract_options!
|
397
540
|
options = merged_options(options)
|
398
541
|
|
399
|
-
|
542
|
+
instrument_multi :read_multi, names, options do |payload|
|
400
543
|
read_multi_entries(names, **options, event: payload).tap do |results|
|
401
544
|
payload[:hits] = results.keys
|
402
545
|
end
|
@@ -405,9 +548,11 @@ module ActiveSupport
|
|
405
548
|
|
406
549
|
# Cache Storage API to write multiple values at once.
|
407
550
|
def write_multi(hash, options = nil)
|
551
|
+
return hash if hash.empty?
|
552
|
+
|
408
553
|
options = merged_options(options)
|
409
554
|
|
410
|
-
|
555
|
+
instrument_multi :write_multi, hash, options do |payload|
|
411
556
|
entries = hash.each_with_object({}) do |(name, value), memo|
|
412
557
|
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
413
558
|
end
|
@@ -433,7 +578,8 @@ module ActiveSupport
|
|
433
578
|
# # => { "bim" => "bam",
|
434
579
|
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
435
580
|
#
|
436
|
-
#
|
581
|
+
# You may also specify additional options via the +options+ argument. See #fetch for details.
|
582
|
+
# Other options are passed to the underlying cache implementation. For example:
|
437
583
|
#
|
438
584
|
# cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
|
439
585
|
# "buzz"
|
@@ -446,29 +592,41 @@ module ActiveSupport
|
|
446
592
|
# # => nil
|
447
593
|
def fetch_multi(*names)
|
448
594
|
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
|
595
|
+
return {} if names.empty?
|
449
596
|
|
450
597
|
options = names.extract_options!
|
451
598
|
options = merged_options(options)
|
452
599
|
|
453
|
-
|
454
|
-
|
455
|
-
|
600
|
+
writes = {}
|
601
|
+
ordered = instrument_multi :read_multi, names, options do |payload|
|
602
|
+
if options[:force]
|
603
|
+
reads = {}
|
604
|
+
else
|
605
|
+
reads = read_multi_entries(names, **options)
|
606
|
+
end
|
607
|
+
|
456
608
|
ordered = names.index_with do |name|
|
457
609
|
reads.fetch(name) { writes[name] = yield(name) }
|
458
610
|
end
|
611
|
+
writes.compact! if options[:skip_nil]
|
459
612
|
|
460
613
|
payload[:hits] = reads.keys
|
461
614
|
payload[:super_operation] = :fetch_multi
|
462
615
|
|
463
|
-
write_multi(writes, options)
|
464
|
-
|
465
616
|
ordered
|
466
617
|
end
|
618
|
+
|
619
|
+
write_multi(writes, options)
|
620
|
+
|
621
|
+
ordered
|
467
622
|
end
|
468
623
|
|
469
624
|
# Writes the value to the cache with the key. The value must be supported
|
470
625
|
# by the +coder+'s +dump+ and +load+ methods.
|
471
626
|
#
|
627
|
+
# Returns +true+ if the write succeeded, +nil+ if there was an error talking
|
628
|
+
# to the cache backend, or +false+ if the write failed for another reason.
|
629
|
+
#
|
472
630
|
# By default, cache entries larger than 1kB are compressed. Compression
|
473
631
|
# allows more data to be stored in the same memory footprint, leading to
|
474
632
|
# fewer cache evictions and higher hit rates.
|
@@ -501,32 +659,38 @@ module ActiveSupport
|
|
501
659
|
# Other options will be handled by the specific cache store implementation.
|
502
660
|
def write(name, value, options = nil)
|
503
661
|
options = merged_options(options)
|
662
|
+
key = normalize_key(name, options)
|
504
663
|
|
505
|
-
instrument(:write,
|
664
|
+
instrument(:write, key, options) do
|
506
665
|
entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
507
|
-
write_entry(
|
666
|
+
write_entry(key, entry, **options)
|
508
667
|
end
|
509
668
|
end
|
510
669
|
|
511
|
-
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
670
|
+
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
671
|
+
# and +false+ otherwise.
|
512
672
|
#
|
513
673
|
# Options are passed to the underlying cache implementation.
|
514
674
|
def delete(name, options = nil)
|
515
675
|
options = merged_options(options)
|
676
|
+
key = normalize_key(name, options)
|
516
677
|
|
517
|
-
instrument(:delete,
|
518
|
-
delete_entry(
|
678
|
+
instrument(:delete, key, options) do
|
679
|
+
delete_entry(key, **options)
|
519
680
|
end
|
520
681
|
end
|
521
682
|
|
522
|
-
# Deletes multiple entries in the cache.
|
683
|
+
# Deletes multiple entries in the cache. Returns the number of deleted
|
684
|
+
# entries.
|
523
685
|
#
|
524
686
|
# Options are passed to the underlying cache implementation.
|
525
687
|
def delete_multi(names, options = nil)
|
688
|
+
return 0 if names.empty?
|
689
|
+
|
526
690
|
options = merged_options(options)
|
527
691
|
names.map! { |key| normalize_key(key, options) }
|
528
692
|
|
529
|
-
|
693
|
+
instrument_multi(:delete_multi, names, options) do
|
530
694
|
delete_multi_entries(names, **options)
|
531
695
|
end
|
532
696
|
end
|
@@ -536,9 +700,10 @@ module ActiveSupport
|
|
536
700
|
# Options are passed to the underlying cache implementation.
|
537
701
|
def exist?(name, options = nil)
|
538
702
|
options = merged_options(options)
|
703
|
+
key = normalize_key(name, options)
|
539
704
|
|
540
|
-
instrument(:exist?,
|
541
|
-
entry = read_entry(
|
705
|
+
instrument(:exist?, key) do |payload|
|
706
|
+
entry = read_entry(key, **options, event: payload)
|
542
707
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
543
708
|
end
|
544
709
|
end
|
@@ -594,8 +759,15 @@ module ActiveSupport
|
|
594
759
|
end
|
595
760
|
|
596
761
|
private
|
597
|
-
def
|
598
|
-
|
762
|
+
def default_serializer
|
763
|
+
case Cache.format_version
|
764
|
+
when 7.0
|
765
|
+
Cache::SerializerWithFallback[:marshal_7_0]
|
766
|
+
when 7.1
|
767
|
+
Cache::SerializerWithFallback[:marshal_7_1]
|
768
|
+
else
|
769
|
+
raise ArgumentError, "Unrecognized ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
|
770
|
+
end
|
599
771
|
end
|
600
772
|
|
601
773
|
# Adds the namespace defined in the options to a pattern designed to
|
@@ -632,14 +804,16 @@ module ActiveSupport
|
|
632
804
|
def serialize_entry(entry, **options)
|
633
805
|
options = merged_options(options)
|
634
806
|
if @coder_supports_compression && options[:compress]
|
635
|
-
@coder.dump_compressed(entry, options[:compress_threshold]
|
807
|
+
@coder.dump_compressed(entry, options[:compress_threshold])
|
636
808
|
else
|
637
809
|
@coder.dump(entry)
|
638
810
|
end
|
639
811
|
end
|
640
812
|
|
641
|
-
def deserialize_entry(payload)
|
813
|
+
def deserialize_entry(payload, **)
|
642
814
|
payload.nil? ? nil : @coder.load(payload)
|
815
|
+
rescue DeserializationError
|
816
|
+
nil
|
643
817
|
end
|
644
818
|
|
645
819
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
@@ -685,6 +859,22 @@ module ActiveSupport
|
|
685
859
|
def merged_options(call_options)
|
686
860
|
if call_options
|
687
861
|
call_options = normalize_options(call_options)
|
862
|
+
if call_options.key?(:expires_in) && call_options.key?(:expires_at)
|
863
|
+
raise ArgumentError, "Either :expires_in or :expires_at can be supplied, but not both"
|
864
|
+
end
|
865
|
+
|
866
|
+
expires_at = call_options.delete(:expires_at)
|
867
|
+
call_options[:expires_in] = (expires_at - Time.now) if expires_at
|
868
|
+
|
869
|
+
if call_options[:expires_in].is_a?(Time)
|
870
|
+
expires_in = call_options[:expires_in]
|
871
|
+
raise ArgumentError.new("expires_in parameter should not be a Time. Did you mean to use expires_at? Got: #{expires_in}")
|
872
|
+
end
|
873
|
+
if call_options[:expires_in]&.negative?
|
874
|
+
expires_in = call_options.delete(:expires_in)
|
875
|
+
handle_invalid_expires_in("Cache expiration time is invalid, cannot be negative: #{expires_in}")
|
876
|
+
end
|
877
|
+
|
688
878
|
if options.empty?
|
689
879
|
call_options
|
690
880
|
else
|
@@ -695,6 +885,16 @@ module ActiveSupport
|
|
695
885
|
end
|
696
886
|
end
|
697
887
|
|
888
|
+
def handle_invalid_expires_in(message)
|
889
|
+
error = ArgumentError.new(message)
|
890
|
+
if ActiveSupport::Cache::Store.raise_on_invalid_cache_expiration_time
|
891
|
+
raise error
|
892
|
+
else
|
893
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
894
|
+
logger.error("#{error.class}: #{error.message}") if logger
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
698
898
|
# Normalize aliased options to their canonical form
|
699
899
|
def normalize_options(options)
|
700
900
|
options = options.dup
|
@@ -707,10 +907,31 @@ module ActiveSupport
|
|
707
907
|
options
|
708
908
|
end
|
709
909
|
|
710
|
-
|
711
|
-
|
910
|
+
def validate_options(options)
|
911
|
+
if options.key?(:coder) && options[:serializer]
|
912
|
+
raise ArgumentError, "Cannot specify :serializer and :coder options together"
|
913
|
+
end
|
914
|
+
|
915
|
+
if options.key?(:coder) && options[:compressor]
|
916
|
+
raise ArgumentError, "Cannot specify :compressor and :coder options together"
|
917
|
+
end
|
918
|
+
|
919
|
+
if Cache.format_version < 7.1 && !options[:serializer] && options[:compressor]
|
920
|
+
raise ArgumentError, "Cannot specify :compressor option when using" \
|
921
|
+
" default serializer and cache format version is < 7.1"
|
922
|
+
end
|
923
|
+
|
924
|
+
options
|
925
|
+
end
|
926
|
+
|
927
|
+
# Expands and namespaces the cache key.
|
928
|
+
# Raises an exception when the key is +nil+ or an empty string.
|
929
|
+
# May be overridden by cache stores to do additional normalization.
|
712
930
|
def normalize_key(key, options = nil)
|
713
|
-
|
931
|
+
str_key = expanded_key(key)
|
932
|
+
raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
|
933
|
+
|
934
|
+
namespace_key str_key, options
|
714
935
|
end
|
715
936
|
|
716
937
|
# Prefix the key with a namespace string:
|
@@ -773,14 +994,33 @@ module ActiveSupport
|
|
773
994
|
end
|
774
995
|
end
|
775
996
|
|
776
|
-
def instrument(operation, key, options = nil)
|
997
|
+
def instrument(operation, key, options = nil, &block)
|
998
|
+
_instrument(operation, key: key, options: options, &block)
|
999
|
+
end
|
1000
|
+
|
1001
|
+
def instrument_multi(operation, keys, options = nil, &block)
|
1002
|
+
_instrument(operation, multi: true, key: keys, options: options, &block)
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def _instrument(operation, multi: false, options: nil, **payload, &block)
|
777
1006
|
if logger && logger.debug? && !silence?
|
778
|
-
|
1007
|
+
debug_key =
|
1008
|
+
if multi
|
1009
|
+
": #{payload[:key].size} key(s) specified"
|
1010
|
+
elsif payload[:key]
|
1011
|
+
": #{payload[:key]}"
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
debug_options = " (#{options.inspect})" unless options.blank?
|
1015
|
+
|
1016
|
+
logger.debug "Cache #{operation}#{debug_key}#{debug_options}"
|
779
1017
|
end
|
780
1018
|
|
781
|
-
payload =
|
1019
|
+
payload[:store] = self.class.name
|
782
1020
|
payload.merge!(options) if options.is_a?(Hash)
|
783
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload)
|
1021
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
|
1022
|
+
block&.call(payload)
|
1023
|
+
end
|
784
1024
|
end
|
785
1025
|
|
786
1026
|
def handle_expired_entry(entry, key, options)
|
@@ -790,7 +1030,8 @@ module ActiveSupport
|
|
790
1030
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
791
1031
|
# for a brief period while the entry is being recalculated.
|
792
1032
|
entry.expires_at = Time.now.to_f + race_ttl
|
793
|
-
|
1033
|
+
options[:expires_in] = race_ttl * 2
|
1034
|
+
write_entry(key, entry, **options)
|
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
|