activesupport 6.0.0 → 6.1.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +381 -349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_support.rb +13 -1
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -4
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +101 -59
- data/lib/active_support/cache/file_store.rb +11 -11
- data/lib/active_support/cache/mem_cache_store.rb +34 -33
- data/lib/active_support/cache/memory_store.rb +52 -31
- data/lib/active_support/cache/null_store.rb +3 -3
- data/lib/active_support/cache/redis_cache_store.rb +38 -33
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +65 -59
- data/lib/active_support/concern.rb +46 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +76 -4
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +46 -29
- data/lib/active_support/core_ext/module/introspection.rb +2 -25
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +13 -2
- data/lib/active_support/core_ext/object/try.rb +4 -2
- data/lib/active_support/core_ext/range/compare_range.rb +15 -3
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/inflections.rb +38 -4
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +12 -11
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +27 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/current_attributes.rb +7 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +42 -20
- data/lib/active_support/dependencies/zeitwerk_integration.rb +9 -2
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +15 -2
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +13 -6
- data/lib/active_support/deprecation/proxy_wrappers.rb +6 -2
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +6 -3
- data/lib/active_support/duration.rb +86 -35
- data/lib/active_support/duration/iso8601_parser.rb +0 -1
- data/lib/active_support/duration/iso8601_serializer.rb +15 -10
- data/lib/active_support/encrypted_file.rb +20 -3
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +69 -134
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +43 -24
- data/lib/active_support/i18n_railtie.rb +15 -16
- data/lib/active_support/inflector/inflections.rb +1 -3
- data/lib/active_support/inflector/methods.rb +36 -33
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/json/decoding.rb +4 -5
- data/lib/active_support/json/encoding.rb +5 -1
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/lazy_load_hooks.rb +0 -1
- data/lib/active_support/locale/en.rb +4 -2
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +8 -1
- data/lib/active_support/logger.rb +2 -2
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -12
- data/lib/active_support/message_encryptor.rb +5 -8
- data/lib/active_support/message_verifier.rb +7 -7
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +5 -44
- data/lib/active_support/multibyte/unicode.rb +9 -84
- data/lib/active_support/notifications.rb +32 -5
- data/lib/active_support/notifications/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +7 -16
- data/lib/active_support/number_helper.rb +33 -14
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -7
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +0 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -2
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -2
- data/lib/active_support/number_helper/number_to_phone_converter.rb +0 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -4
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +17 -13
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +29 -4
- data/lib/active_support/testing/assertions.rb +18 -11
- data/lib/active_support/testing/parallelization.rb +12 -89
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/stream.rb +0 -1
- data/lib/active_support/testing/time_helpers.rb +40 -5
- data/lib/active_support/time_with_zone.rb +67 -43
- data/lib/active_support/values/time_zone.rb +20 -10
- data/lib/active_support/xml_mini.rb +0 -1
- data/lib/active_support/xml_mini/jdom.rb +0 -1
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- metadata +39 -38
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
attr_reader :cache_path
|
17
17
|
|
18
18
|
DIR_FORMATTER = "%03X"
|
19
|
-
FILENAME_MAX_SIZE =
|
19
|
+
FILENAME_MAX_SIZE = 226 # max filename size on file system is 255, minus room for timestamp, pid, and random characters appended by Tempfile (used by atomic write)
|
20
20
|
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
|
21
21
|
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
|
22
22
|
|
@@ -36,15 +36,15 @@ module ActiveSupport
|
|
36
36
|
def clear(options = nil)
|
37
37
|
root_dirs = (Dir.children(cache_path) - GITKEEP_FILES)
|
38
38
|
FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
|
39
|
-
rescue Errno::ENOENT
|
39
|
+
rescue Errno::ENOENT, Errno::ENOTEMPTY
|
40
40
|
end
|
41
41
|
|
42
42
|
# Preemptively iterates through all stored keys and removes the ones which have expired.
|
43
43
|
def cleanup(options = nil)
|
44
44
|
options = merged_options(options)
|
45
45
|
search_dir(cache_path) do |fname|
|
46
|
-
entry = read_entry(fname, options)
|
47
|
-
delete_entry(fname, options) if entry && entry.expired?
|
46
|
+
entry = read_entry(fname, **options)
|
47
|
+
delete_entry(fname, **options) if entry && entry.expired?
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -66,30 +66,30 @@ module ActiveSupport
|
|
66
66
|
matcher = key_matcher(matcher, options)
|
67
67
|
search_dir(cache_path) do |path|
|
68
68
|
key = file_path_key(path)
|
69
|
-
delete_entry(path, options) if key.match(matcher)
|
69
|
+
delete_entry(path, **options) if key.match(matcher)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
74
|
private
|
75
|
-
|
76
|
-
def read_entry(key, options)
|
75
|
+
def read_entry(key, **options)
|
77
76
|
if File.exist?(key)
|
78
|
-
File.open(key) { |f|
|
77
|
+
entry = File.open(key) { |f| deserialize_entry(f.read) }
|
78
|
+
entry if entry.is_a?(Cache::Entry)
|
79
79
|
end
|
80
80
|
rescue => e
|
81
81
|
logger.error("FileStoreError (#{e}): #{e.message}") if logger
|
82
82
|
nil
|
83
83
|
end
|
84
84
|
|
85
|
-
def write_entry(key, entry, options)
|
85
|
+
def write_entry(key, entry, **options)
|
86
86
|
return false if options[:unless_exist] && File.exist?(key)
|
87
87
|
ensure_cache_path(File.dirname(key))
|
88
|
-
File.atomic_write(key, cache_path) { |f|
|
88
|
+
File.atomic_write(key, cache_path) { |f| f.write(serialize_entry(entry)) }
|
89
89
|
true
|
90
90
|
end
|
91
91
|
|
92
|
-
def delete_entry(key, options)
|
92
|
+
def delete_entry(key, **options)
|
93
93
|
if File.exist?(key)
|
94
94
|
begin
|
95
95
|
File.delete(key)
|
@@ -7,6 +7,7 @@ rescue LoadError => e
|
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
+
require "active_support/core_ext/enumerable"
|
10
11
|
require "active_support/core_ext/marshal"
|
11
12
|
require "active_support/core_ext/array/extract_options"
|
12
13
|
|
@@ -25,22 +26,16 @@ module ActiveSupport
|
|
25
26
|
# MemCacheStore implements the Strategy::LocalCache strategy which implements
|
26
27
|
# an in-memory cache inside of a block.
|
27
28
|
class MemCacheStore < Store
|
29
|
+
DEFAULT_CODER = NullCoder # Dalli automatically Marshal values
|
30
|
+
|
28
31
|
# Provide support for raw values in the local cache strategy.
|
29
32
|
module LocalCacheWithRaw # :nodoc:
|
30
33
|
private
|
31
|
-
def
|
32
|
-
entry = super
|
33
|
-
if options[:raw] && local_cache && entry
|
34
|
-
entry = deserialize_entry(entry.value)
|
35
|
-
end
|
36
|
-
entry
|
37
|
-
end
|
38
|
-
|
39
|
-
def write_entry(key, entry, options)
|
34
|
+
def write_entry(key, entry, **options)
|
40
35
|
if options[:raw] && local_cache
|
41
36
|
raw_entry = Entry.new(entry.value.to_s)
|
42
37
|
raw_entry.expires_at = entry.expires_at
|
43
|
-
super(key, raw_entry, options)
|
38
|
+
super(key, raw_entry, **options)
|
44
39
|
else
|
45
40
|
super
|
46
41
|
end
|
@@ -58,16 +53,18 @@ module ActiveSupport
|
|
58
53
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
59
54
|
|
60
55
|
# Creates a new Dalli::Client instance with specified addresses and options.
|
61
|
-
#
|
56
|
+
# If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
|
57
|
+
# - ENV["MEMCACHE_SERVERS"] (if defined)
|
58
|
+
# - "127.0.0.1:11211" (otherwise)
|
62
59
|
#
|
63
60
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache
|
64
|
-
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["
|
61
|
+
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
|
65
62
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
|
66
63
|
# # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
|
67
64
|
def self.build_mem_cache(*addresses) # :nodoc:
|
68
65
|
addresses = addresses.flatten
|
69
66
|
options = addresses.extract_options!
|
70
|
-
addresses =
|
67
|
+
addresses = nil if addresses.compact.empty?
|
71
68
|
pool_options = retrieve_pool_options(options)
|
72
69
|
|
73
70
|
if pool_options.empty?
|
@@ -84,8 +81,8 @@ module ActiveSupport
|
|
84
81
|
#
|
85
82
|
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
86
83
|
#
|
87
|
-
# If no addresses are
|
88
|
-
#
|
84
|
+
# If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
|
85
|
+
# MemCacheStore will connect to localhost:11211 (the default memcached port).
|
89
86
|
def initialize(*addresses)
|
90
87
|
addresses = addresses.flatten
|
91
88
|
options = addresses.extract_options!
|
@@ -142,27 +139,28 @@ module ActiveSupport
|
|
142
139
|
|
143
140
|
private
|
144
141
|
# Read an entry from the cache.
|
145
|
-
def read_entry(key, options)
|
142
|
+
def read_entry(key, **options)
|
146
143
|
rescue_error_with(nil) { deserialize_entry(@data.with { |c| c.get(key, options) }) }
|
147
144
|
end
|
148
145
|
|
149
146
|
# Write an entry to the cache.
|
150
|
-
def write_entry(key, entry, options)
|
151
|
-
method = options
|
152
|
-
value = options[:raw] ? entry.value.to_s : entry
|
147
|
+
def write_entry(key, entry, **options)
|
148
|
+
method = options[:unless_exist] ? :add : :set
|
149
|
+
value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
|
153
150
|
expires_in = options[:expires_in].to_i
|
154
|
-
if expires_in > 0 && !options[:raw]
|
151
|
+
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
155
152
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
156
153
|
expires_in += 5.minutes
|
157
154
|
end
|
158
155
|
rescue_error_with false do
|
159
|
-
|
156
|
+
# The value "compress: false" prevents duplicate compression within Dalli.
|
157
|
+
@data.with { |c| c.send(method, key, value, expires_in, **options, compress: false) }
|
160
158
|
end
|
161
159
|
end
|
162
160
|
|
163
161
|
# Reads multiple entries from the cache implementation.
|
164
|
-
def read_multi_entries(names, options)
|
165
|
-
keys_to_names =
|
162
|
+
def read_multi_entries(names, **options)
|
163
|
+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
166
164
|
|
167
165
|
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
168
166
|
values = {}
|
@@ -179,7 +177,7 @@ module ActiveSupport
|
|
179
177
|
end
|
180
178
|
|
181
179
|
# Delete an entry from the cache.
|
182
|
-
def delete_entry(key, options)
|
180
|
+
def delete_entry(key, **options)
|
183
181
|
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
184
182
|
end
|
185
183
|
|
@@ -187,18 +185,21 @@ module ActiveSupport
|
|
187
185
|
# before applying the regular expression to ensure we are escaping all
|
188
186
|
# characters properly.
|
189
187
|
def normalize_key(key, options)
|
190
|
-
key = super
|
191
|
-
|
192
|
-
|
193
|
-
|
188
|
+
key = super
|
189
|
+
|
190
|
+
if key
|
191
|
+
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
192
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
193
|
+
key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
194
|
+
end
|
195
|
+
|
194
196
|
key
|
195
197
|
end
|
196
198
|
|
197
|
-
def deserialize_entry(
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
199
|
+
def deserialize_entry(payload)
|
200
|
+
entry = super
|
201
|
+
entry = Entry.new(entry, compress: false) if entry && !entry.is_a?(Entry)
|
202
|
+
entry
|
202
203
|
end
|
203
204
|
|
204
205
|
def rescue_error_with(fallback)
|
@@ -16,13 +16,37 @@ module ActiveSupport
|
|
16
16
|
# a cleanup will occur which tries to prune the cache down to three quarters
|
17
17
|
# of the maximum size by removing the least recently used entries.
|
18
18
|
#
|
19
|
+
# Unlike other Cache store implementations, MemoryStore does not compress
|
20
|
+
# values by default. MemoryStore does not benefit from compression as much
|
21
|
+
# as other Store implementations, as it does not send data over a network.
|
22
|
+
# However, when compression is enabled, it still pays the full cost of
|
23
|
+
# compression in terms of cpu use.
|
24
|
+
#
|
19
25
|
# MemoryStore is thread-safe.
|
20
26
|
class MemoryStore < Store
|
27
|
+
module DupCoder # :nodoc:
|
28
|
+
class << self
|
29
|
+
def load(entry)
|
30
|
+
entry = entry.dup
|
31
|
+
entry.dup_value!
|
32
|
+
entry
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump(entry)
|
36
|
+
entry.dup_value!
|
37
|
+
entry
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
DEFAULT_CODER = DupCoder
|
43
|
+
|
21
44
|
def initialize(options = nil)
|
22
45
|
options ||= {}
|
46
|
+
# Disable compression by default.
|
47
|
+
options[:compress] ||= false
|
23
48
|
super(options)
|
24
49
|
@data = {}
|
25
|
-
@key_access = {}
|
26
50
|
@max_size = options[:size] || 32.megabytes
|
27
51
|
@max_prune_time = options[:max_prune_time] || 2
|
28
52
|
@cache_size = 0
|
@@ -39,7 +63,6 @@ module ActiveSupport
|
|
39
63
|
def clear(options = nil)
|
40
64
|
synchronize do
|
41
65
|
@data.clear
|
42
|
-
@key_access.clear
|
43
66
|
@cache_size = 0
|
44
67
|
end
|
45
68
|
end
|
@@ -51,7 +74,7 @@ module ActiveSupport
|
|
51
74
|
keys = synchronize { @data.keys }
|
52
75
|
keys.each do |key|
|
53
76
|
entry = @data[key]
|
54
|
-
delete_entry(key, options) if entry && entry.expired?
|
77
|
+
delete_entry(key, **options) if entry && entry.expired?
|
55
78
|
end
|
56
79
|
end
|
57
80
|
end
|
@@ -65,9 +88,9 @@ module ActiveSupport
|
|
65
88
|
start_time = Concurrent.monotonic_time
|
66
89
|
cleanup
|
67
90
|
instrument(:prune, target_size, from: @cache_size) do
|
68
|
-
keys = synchronize { @
|
91
|
+
keys = synchronize { @data.keys }
|
69
92
|
keys.each do |key|
|
70
|
-
delete_entry(key, options)
|
93
|
+
delete_entry(key, **options)
|
71
94
|
return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
|
72
95
|
end
|
73
96
|
end
|
@@ -98,13 +121,13 @@ module ActiveSupport
|
|
98
121
|
matcher = key_matcher(matcher, options)
|
99
122
|
keys = synchronize { @data.keys }
|
100
123
|
keys.each do |key|
|
101
|
-
delete_entry(key, options) if key.match(matcher)
|
124
|
+
delete_entry(key, **options) if key.match(matcher)
|
102
125
|
end
|
103
126
|
end
|
104
127
|
end
|
105
128
|
|
106
129
|
def inspect # :nodoc:
|
107
|
-
"
|
130
|
+
"#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
|
108
131
|
end
|
109
132
|
|
110
133
|
# Synchronize calls to the cache. This should be called wherever the underlying cache implementation
|
@@ -114,54 +137,52 @@ module ActiveSupport
|
|
114
137
|
end
|
115
138
|
|
116
139
|
private
|
117
|
-
|
118
140
|
PER_ENTRY_OVERHEAD = 240
|
119
141
|
|
120
|
-
def cached_size(key,
|
121
|
-
key.to_s.bytesize +
|
142
|
+
def cached_size(key, payload)
|
143
|
+
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
122
144
|
end
|
123
145
|
|
124
|
-
def read_entry(key, options)
|
125
|
-
entry =
|
146
|
+
def read_entry(key, **options)
|
147
|
+
entry = nil
|
126
148
|
synchronize do
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
149
|
+
payload = @data.delete(key)
|
150
|
+
if payload
|
151
|
+
@data[key] = payload
|
152
|
+
entry = deserialize_entry(payload)
|
131
153
|
end
|
132
154
|
end
|
133
155
|
entry
|
134
156
|
end
|
135
157
|
|
136
|
-
def write_entry(key, entry, options)
|
137
|
-
entry
|
158
|
+
def write_entry(key, entry, **options)
|
159
|
+
payload = serialize_entry(entry)
|
138
160
|
synchronize do
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
161
|
+
return false if options[:unless_exist] && @data.key?(key)
|
162
|
+
|
163
|
+
old_payload = @data[key]
|
164
|
+
if old_payload
|
165
|
+
@cache_size -= (old_payload.bytesize - payload.bytesize)
|
143
166
|
else
|
144
|
-
@cache_size += cached_size(key,
|
167
|
+
@cache_size += cached_size(key, payload)
|
145
168
|
end
|
146
|
-
@
|
147
|
-
@data[key] = entry
|
169
|
+
@data[key] = payload
|
148
170
|
prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
|
149
171
|
true
|
150
172
|
end
|
151
173
|
end
|
152
174
|
|
153
|
-
def delete_entry(key, options)
|
175
|
+
def delete_entry(key, **options)
|
154
176
|
synchronize do
|
155
|
-
@
|
156
|
-
|
157
|
-
|
158
|
-
!!entry
|
177
|
+
payload = @data.delete(key)
|
178
|
+
@cache_size -= cached_size(key, payload) if payload
|
179
|
+
!!payload
|
159
180
|
end
|
160
181
|
end
|
161
182
|
|
162
183
|
def modify_value(name, amount, options)
|
184
|
+
options = merged_options(options)
|
163
185
|
synchronize do
|
164
|
-
options = merged_options(options)
|
165
186
|
if num = read(name, options)
|
166
187
|
num = num.to_i + amount
|
167
188
|
write(name, num, options)
|
@@ -33,14 +33,14 @@ module ActiveSupport
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
-
def read_entry(key, options)
|
36
|
+
def read_entry(key, **options)
|
37
37
|
end
|
38
38
|
|
39
|
-
def write_entry(key, entry, options)
|
39
|
+
def write_entry(key, entry, **options)
|
40
40
|
true
|
41
41
|
end
|
42
42
|
|
43
|
-
def delete_entry(key, options)
|
43
|
+
def delete_entry(key, **options)
|
44
44
|
false
|
45
45
|
end
|
46
46
|
end
|
@@ -74,32 +74,24 @@ module ActiveSupport
|
|
74
74
|
# Support raw values in the local cache strategy.
|
75
75
|
module LocalCacheWithRaw # :nodoc:
|
76
76
|
private
|
77
|
-
def
|
78
|
-
entry = super
|
79
|
-
if options[:raw] && local_cache && entry
|
80
|
-
entry = deserialize_entry(entry.value)
|
81
|
-
end
|
82
|
-
entry
|
83
|
-
end
|
84
|
-
|
85
|
-
def write_entry(key, entry, options)
|
77
|
+
def write_entry(key, entry, **options)
|
86
78
|
if options[:raw] && local_cache
|
87
79
|
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
88
80
|
raw_entry.expires_at = entry.expires_at
|
89
|
-
super(key, raw_entry, options)
|
81
|
+
super(key, raw_entry, **options)
|
90
82
|
else
|
91
83
|
super
|
92
84
|
end
|
93
85
|
end
|
94
86
|
|
95
|
-
def write_multi_entries(entries, options)
|
87
|
+
def write_multi_entries(entries, **options)
|
96
88
|
if options[:raw] && local_cache
|
97
89
|
raw_entries = entries.map do |key, entry|
|
98
90
|
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
99
91
|
raw_entry.expires_at = entry.expires_at
|
100
92
|
end.to_h
|
101
93
|
|
102
|
-
super(raw_entries, options)
|
94
|
+
super(raw_entries, **options)
|
103
95
|
else
|
104
96
|
super
|
105
97
|
end
|
@@ -177,7 +169,7 @@ module ActiveSupport
|
|
177
169
|
# Race condition TTL is not set by default. This can be used to avoid
|
178
170
|
# "thundering herd" cache writes when hot cache entries are expired.
|
179
171
|
# See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
|
180
|
-
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
172
|
+
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder: DEFAULT_CODER, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
181
173
|
@redis_options = redis_options
|
182
174
|
|
183
175
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
@@ -185,7 +177,8 @@ module ActiveSupport
|
|
185
177
|
|
186
178
|
super namespace: namespace,
|
187
179
|
compress: compress, compress_threshold: compress_threshold,
|
188
|
-
expires_in: expires_in, race_condition_ttl: race_condition_ttl
|
180
|
+
expires_in: expires_in, race_condition_ttl: race_condition_ttl,
|
181
|
+
coder: coder
|
189
182
|
end
|
190
183
|
|
191
184
|
def redis
|
@@ -203,7 +196,7 @@ module ActiveSupport
|
|
203
196
|
|
204
197
|
def inspect
|
205
198
|
instance = @redis || @redis_options
|
206
|
-
"
|
199
|
+
"#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
|
207
200
|
end
|
208
201
|
|
209
202
|
# Cache Store API implementation.
|
@@ -246,10 +239,14 @@ module ActiveSupport
|
|
246
239
|
pattern = namespace_key(matcher, options)
|
247
240
|
cursor = "0"
|
248
241
|
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
242
|
+
nodes = c.respond_to?(:nodes) ? c.nodes : [c]
|
243
|
+
|
244
|
+
nodes.each do |node|
|
245
|
+
begin
|
246
|
+
cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
|
247
|
+
node.del(*keys) unless keys.empty?
|
248
|
+
end until cursor == "0"
|
249
|
+
end
|
253
250
|
end
|
254
251
|
end
|
255
252
|
end
|
@@ -346,15 +343,16 @@ module ActiveSupport
|
|
346
343
|
|
347
344
|
# Store provider interface:
|
348
345
|
# Read an entry from the cache.
|
349
|
-
def read_entry(key, options
|
346
|
+
def read_entry(key, **options)
|
350
347
|
failsafe :read_entry do
|
351
|
-
|
348
|
+
raw = options&.fetch(:raw, false)
|
349
|
+
deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
|
352
350
|
end
|
353
351
|
end
|
354
352
|
|
355
|
-
def read_multi_entries(names,
|
353
|
+
def read_multi_entries(names, **options)
|
356
354
|
if mget_capable?
|
357
|
-
read_multi_mget(*names)
|
355
|
+
read_multi_mget(*names, **options)
|
358
356
|
else
|
359
357
|
super
|
360
358
|
end
|
@@ -364,6 +362,7 @@ module ActiveSupport
|
|
364
362
|
options = names.extract_options!
|
365
363
|
options = merged_options(options)
|
366
364
|
return {} if names == []
|
365
|
+
raw = options&.fetch(:raw, false)
|
367
366
|
|
368
367
|
keys = names.map { |name| normalize_key(name, options) }
|
369
368
|
|
@@ -373,7 +372,7 @@ module ActiveSupport
|
|
373
372
|
|
374
373
|
names.zip(values).each_with_object({}) do |(name, value), results|
|
375
374
|
if value
|
376
|
-
entry = deserialize_entry(value)
|
375
|
+
entry = deserialize_entry(value, raw: raw)
|
377
376
|
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
|
378
377
|
results[name] = entry.value
|
379
378
|
end
|
@@ -400,7 +399,7 @@ module ActiveSupport
|
|
400
399
|
modifiers[:nx] = unless_exist
|
401
400
|
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
402
401
|
|
403
|
-
redis.with { |c| c.set key, serialized_entry, modifiers }
|
402
|
+
redis.with { |c| c.set key, serialized_entry, **modifiers }
|
404
403
|
else
|
405
404
|
redis.with { |c| c.set key, serialized_entry }
|
406
405
|
end
|
@@ -420,6 +419,11 @@ module ActiveSupport
|
|
420
419
|
end
|
421
420
|
end
|
422
421
|
|
422
|
+
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
423
|
+
def delete_multi_entries(entries, **_options)
|
424
|
+
redis.with { |c| c.del(entries) }
|
425
|
+
end
|
426
|
+
|
423
427
|
# Nonstandard store provider API to write multiple values at once.
|
424
428
|
def write_multi_entries(entries, expires_in: nil, **options)
|
425
429
|
if entries.any?
|
@@ -435,11 +439,11 @@ module ActiveSupport
|
|
435
439
|
|
436
440
|
# Truncate keys that exceed 1kB.
|
437
441
|
def normalize_key(key, options)
|
438
|
-
truncate_key super
|
442
|
+
truncate_key super&.b
|
439
443
|
end
|
440
444
|
|
441
445
|
def truncate_key(key)
|
442
|
-
if key.bytesize > max_key_bytesize
|
446
|
+
if key && key.bytesize > max_key_bytesize
|
443
447
|
suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
|
444
448
|
truncate_at = max_key_bytesize - suffix.bytesize
|
445
449
|
"#{key.byteslice(0, truncate_at)}#{suffix}"
|
@@ -448,10 +452,11 @@ module ActiveSupport
|
|
448
452
|
end
|
449
453
|
end
|
450
454
|
|
451
|
-
def deserialize_entry(
|
452
|
-
if
|
453
|
-
|
454
|
-
|
455
|
+
def deserialize_entry(payload, raw:)
|
456
|
+
if payload && raw
|
457
|
+
Entry.new(payload, compress: false)
|
458
|
+
else
|
459
|
+
super(payload)
|
455
460
|
end
|
456
461
|
end
|
457
462
|
|
@@ -459,7 +464,7 @@ module ActiveSupport
|
|
459
464
|
if raw
|
460
465
|
entry.value.to_s
|
461
466
|
else
|
462
|
-
|
467
|
+
super(entry)
|
463
468
|
end
|
464
469
|
end
|
465
470
|
|
@@ -471,7 +476,7 @@ module ActiveSupport
|
|
471
476
|
|
472
477
|
def failsafe(method, returning: nil)
|
473
478
|
yield
|
474
|
-
rescue ::Redis::
|
479
|
+
rescue ::Redis::BaseError => e
|
475
480
|
handle_exception exception: e, method: method, returning: returning
|
476
481
|
returning
|
477
482
|
end
|