activesupport 7.0.3.1 → 7.2.3.1
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 +261 -246
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- 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 +114 -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 +175 -154
- 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 +455 -378
- 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/calculations.rb +15 -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/calculations.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 +25 -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/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +7 -7
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- 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/duplicable.rb +25 -16
- 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/to_query.rb +0 -2
- 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 +9 -9
- 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 -9
- data/lib/active_support/core_ext/string/multibyte.rb +3 -3
- data/lib/active_support/core_ext/string/output_safety.rb +41 -178
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +40 -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 +7 -8
- 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 +64 -41
- 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 +6 -8
- 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 +63 -11
- data/lib/active_support/encrypted_file.rb +29 -13
- 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 +17 -3
- 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 +2 -2
- 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 +28 -18
- 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 +22 -7
- 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 -59
- 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 +36 -29
- 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_delimited_converter.rb +17 -2
- 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 -317
- data/lib/active_support/option_merger.rb +4 -4
- 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 +103 -84
- 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 +10 -8
- 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 -40
- data/lib/active_support/test_case.rb +160 -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 +46 -33
- 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 +18 -44
- 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 +148 -24
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
- 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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module Cache
|
|
7
|
+
# This class is used to represent cache entries. Cache entries have a value, an optional
|
|
8
|
+
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
|
9
|
+
# on the cache. The version is used to support the :version option on the cache for rejecting
|
|
10
|
+
# mismatches.
|
|
11
|
+
#
|
|
12
|
+
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
|
13
|
+
# using short instance variable names that are lazily defined.
|
|
14
|
+
class Entry # :nodoc:
|
|
15
|
+
class << self
|
|
16
|
+
def unpack(members)
|
|
17
|
+
new(members[0], expires_at: members[1], version: members[2])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :version
|
|
22
|
+
|
|
23
|
+
# Creates a new cache entry for the specified value. Options supported are
|
|
24
|
+
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
|
25
|
+
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
|
26
|
+
@value = value
|
|
27
|
+
@version = version
|
|
28
|
+
@created_at = 0.0
|
|
29
|
+
@expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
|
|
30
|
+
@compressed = true if compressed
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def value
|
|
34
|
+
compressed? ? uncompress(@value) : @value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def mismatched?(version)
|
|
38
|
+
@version && version && @version != version
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Checks if the entry is expired. The +expires_in+ parameter can override
|
|
42
|
+
# the value set when the entry was created.
|
|
43
|
+
def expired?
|
|
44
|
+
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def expires_at
|
|
48
|
+
@expires_in ? @created_at + @expires_in : nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def expires_at=(value)
|
|
52
|
+
if value
|
|
53
|
+
@expires_in = value.to_f - @created_at
|
|
54
|
+
else
|
|
55
|
+
@expires_in = nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the size of the cached value. This could be less than
|
|
60
|
+
# <tt>value.bytesize</tt> if the data is compressed.
|
|
61
|
+
def bytesize
|
|
62
|
+
case value
|
|
63
|
+
when NilClass
|
|
64
|
+
0
|
|
65
|
+
when String
|
|
66
|
+
@value.bytesize
|
|
67
|
+
else
|
|
68
|
+
@s ||= Marshal.dump(@value).bytesize
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def compressed? # :nodoc:
|
|
73
|
+
defined?(@compressed)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def compressed(compress_threshold)
|
|
77
|
+
return self if compressed?
|
|
78
|
+
|
|
79
|
+
case @value
|
|
80
|
+
when nil, true, false, Numeric
|
|
81
|
+
uncompressed_size = 0
|
|
82
|
+
when String
|
|
83
|
+
uncompressed_size = @value.bytesize
|
|
84
|
+
else
|
|
85
|
+
serialized = Marshal.dump(@value)
|
|
86
|
+
uncompressed_size = serialized.bytesize
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if uncompressed_size >= compress_threshold
|
|
90
|
+
serialized ||= Marshal.dump(@value)
|
|
91
|
+
compressed = Zlib::Deflate.deflate(serialized)
|
|
92
|
+
|
|
93
|
+
if compressed.bytesize < uncompressed_size
|
|
94
|
+
return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def local?
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
|
105
|
+
# serialize entries to protect against accidental cache modifications.
|
|
106
|
+
def dup_value!
|
|
107
|
+
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
|
108
|
+
if @value.is_a?(String)
|
|
109
|
+
@value = @value.dup
|
|
110
|
+
else
|
|
111
|
+
@value = Marshal.load(Marshal.dump(@value))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def pack
|
|
117
|
+
members = [value, expires_at, version]
|
|
118
|
+
members.pop while !members.empty? && members.last.nil?
|
|
119
|
+
members
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
def uncompress(value)
|
|
124
|
+
marshal_load(Zlib::Inflate.inflate(value))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def marshal_load(payload)
|
|
128
|
+
Marshal.load(payload)
|
|
129
|
+
rescue ArgumentError => error
|
|
130
|
+
raise Cache::DeserializationError, error.message
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -6,10 +6,9 @@ require "uri/common"
|
|
|
6
6
|
|
|
7
7
|
module ActiveSupport
|
|
8
8
|
module Cache
|
|
9
|
-
#
|
|
9
|
+
# = \File \Cache \Store
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
# an in-memory cache inside of a block.
|
|
11
|
+
# A cache store implementation which stores everything on the filesystem.
|
|
13
12
|
class FileStore < Store
|
|
14
13
|
attr_reader :cache_path
|
|
15
14
|
|
|
@@ -46,22 +45,42 @@ module ActiveSupport
|
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
|
|
48
|
+
# Increment a cached integer value. Returns the updated value.
|
|
49
|
+
#
|
|
50
|
+
# If the key is unset, it starts from +0+:
|
|
51
|
+
#
|
|
52
|
+
# cache.increment("foo") # => 1
|
|
53
|
+
# cache.increment("bar", 100) # => 100
|
|
54
|
+
#
|
|
55
|
+
# To set a specific value, call #write:
|
|
56
|
+
#
|
|
57
|
+
# cache.write("baz", 5)
|
|
58
|
+
# cache.increment("baz") # => 6
|
|
59
|
+
#
|
|
60
|
+
def increment(name, amount = 1, **options)
|
|
52
61
|
modify_value(name, amount, options)
|
|
53
62
|
end
|
|
54
63
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
64
|
+
# Decrement a cached integer value. Returns the updated value.
|
|
65
|
+
#
|
|
66
|
+
# If the key is unset, it will be set to +-amount+.
|
|
67
|
+
#
|
|
68
|
+
# cache.decrement("foo") # => -1
|
|
69
|
+
#
|
|
70
|
+
# To set a specific value, call #write:
|
|
71
|
+
#
|
|
72
|
+
# cache.write("baz", 5)
|
|
73
|
+
# cache.decrement("baz") # => 4
|
|
74
|
+
#
|
|
75
|
+
def decrement(name, amount = 1, **options)
|
|
58
76
|
modify_value(name, -amount, options)
|
|
59
77
|
end
|
|
60
78
|
|
|
61
79
|
def delete_matched(matcher, options = nil)
|
|
62
80
|
options = merged_options(options)
|
|
81
|
+
matcher = key_matcher(matcher, options)
|
|
82
|
+
|
|
63
83
|
instrument(:delete_matched, matcher.inspect) do
|
|
64
|
-
matcher = key_matcher(matcher, options)
|
|
65
84
|
search_dir(cache_path) do |path|
|
|
66
85
|
key = file_path_key(path)
|
|
67
86
|
delete_entry(path, **options) if key.match(matcher)
|
|
@@ -69,6 +88,10 @@ module ActiveSupport
|
|
|
69
88
|
end
|
|
70
89
|
end
|
|
71
90
|
|
|
91
|
+
def inspect # :nodoc:
|
|
92
|
+
"#<#{self.class.name} cache_path=#{@cache_path}, options=#{@options.inspect}>"
|
|
93
|
+
end
|
|
94
|
+
|
|
72
95
|
private
|
|
73
96
|
def read_entry(key, **options)
|
|
74
97
|
if payload = read_serialized_entry(key, **options)
|
|
@@ -106,6 +129,8 @@ module ActiveSupport
|
|
|
106
129
|
raise if File.exist?(key)
|
|
107
130
|
false
|
|
108
131
|
end
|
|
132
|
+
else
|
|
133
|
+
false
|
|
109
134
|
end
|
|
110
135
|
end
|
|
111
136
|
|
|
@@ -152,7 +177,7 @@ module ActiveSupport
|
|
|
152
177
|
|
|
153
178
|
# Translate a file path into a key.
|
|
154
179
|
def file_path_key(path)
|
|
155
|
-
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
|
|
180
|
+
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
|
|
156
181
|
URI.decode_www_form_component(fname, Encoding::UTF_8)
|
|
157
182
|
end
|
|
158
183
|
|
|
@@ -182,17 +207,24 @@ module ActiveSupport
|
|
|
182
207
|
end
|
|
183
208
|
end
|
|
184
209
|
|
|
185
|
-
# Modifies the amount of an
|
|
186
|
-
# If the key is not found
|
|
210
|
+
# Modifies the amount of an integer value that is stored in the cache.
|
|
211
|
+
# If the key is not found it is created and set to +amount+.
|
|
187
212
|
def modify_value(name, amount, options)
|
|
188
|
-
|
|
213
|
+
options = merged_options(options)
|
|
214
|
+
key = normalize_key(name, options)
|
|
215
|
+
version = normalize_version(name, options)
|
|
216
|
+
amount = Integer(amount)
|
|
189
217
|
|
|
190
|
-
lock_file(
|
|
191
|
-
|
|
218
|
+
lock_file(key) do
|
|
219
|
+
entry = read_entry(key, **options)
|
|
192
220
|
|
|
193
|
-
if
|
|
194
|
-
|
|
195
|
-
|
|
221
|
+
if !entry || entry.expired? || entry.mismatched?(version)
|
|
222
|
+
write(name, amount, options)
|
|
223
|
+
amount
|
|
224
|
+
else
|
|
225
|
+
num = entry.value.to_i + amount
|
|
226
|
+
entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
|
|
227
|
+
write_entry(key, entry)
|
|
196
228
|
num
|
|
197
229
|
end
|
|
198
230
|
end
|
|
@@ -3,16 +3,20 @@
|
|
|
3
3
|
begin
|
|
4
4
|
require "dalli"
|
|
5
5
|
rescue LoadError => e
|
|
6
|
-
|
|
6
|
+
warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
|
|
7
7
|
raise e
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
require "connection_pool"
|
|
10
11
|
require "delegate"
|
|
11
12
|
require "active_support/core_ext/enumerable"
|
|
12
13
|
require "active_support/core_ext/array/extract_options"
|
|
14
|
+
require "active_support/core_ext/numeric/time"
|
|
13
15
|
|
|
14
16
|
module ActiveSupport
|
|
15
17
|
module Cache
|
|
18
|
+
# = Memcached \Cache \Store
|
|
19
|
+
#
|
|
16
20
|
# A cache store implementation which stores data in Memcached:
|
|
17
21
|
# https://memcached.org
|
|
18
22
|
#
|
|
@@ -20,12 +24,16 @@ module ActiveSupport
|
|
|
20
24
|
#
|
|
21
25
|
# Special features:
|
|
22
26
|
# - Clustering and load balancing. One can specify multiple memcached servers,
|
|
23
|
-
# and MemCacheStore will load balance between all available servers. If a
|
|
24
|
-
# server goes down, then MemCacheStore will ignore it until it comes back up.
|
|
27
|
+
# and +MemCacheStore+ will load balance between all available servers. If a
|
|
28
|
+
# server goes down, then +MemCacheStore+ will ignore it until it comes back up.
|
|
25
29
|
#
|
|
26
|
-
# MemCacheStore implements the Strategy::LocalCache strategy which
|
|
27
|
-
# an in-memory cache inside of a block.
|
|
30
|
+
# +MemCacheStore+ implements the Strategy::LocalCache strategy which
|
|
31
|
+
# implements an in-memory cache inside of a block.
|
|
28
32
|
class MemCacheStore < Store
|
|
33
|
+
# These options represent behavior overridden by this implementation and should
|
|
34
|
+
# not be allowed to get down to the Dalli client
|
|
35
|
+
OVERRIDDEN_OPTIONS = UNIVERSAL_OPTIONS
|
|
36
|
+
|
|
29
37
|
# Advertise cache versioning support.
|
|
30
38
|
def self.supports_cache_versioning?
|
|
31
39
|
true
|
|
@@ -33,46 +41,7 @@ module ActiveSupport
|
|
|
33
41
|
|
|
34
42
|
prepend Strategy::LocalCache
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore)
|
|
38
|
-
def write_entry(_key, entry)
|
|
39
|
-
if entry.is_a?(Entry)
|
|
40
|
-
entry.dup_value!
|
|
41
|
-
end
|
|
42
|
-
super
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def fetch_entry(key)
|
|
46
|
-
entry = super do
|
|
47
|
-
new_entry = yield
|
|
48
|
-
if entry.is_a?(Entry)
|
|
49
|
-
new_entry.dup_value!
|
|
50
|
-
end
|
|
51
|
-
new_entry
|
|
52
|
-
end
|
|
53
|
-
entry = entry.dup
|
|
54
|
-
|
|
55
|
-
if entry.is_a?(Entry)
|
|
56
|
-
entry.dup_value!
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
entry
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
def local_cache
|
|
65
|
-
if ActiveSupport::Cache.format_version == 6.1
|
|
66
|
-
if local_cache = super
|
|
67
|
-
DupLocalStore.new(local_cache)
|
|
68
|
-
end
|
|
69
|
-
else
|
|
70
|
-
super
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
prepend DupLocalCache
|
|
75
|
-
|
|
44
|
+
KEY_MAX_SIZE = 250
|
|
76
45
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
|
77
46
|
|
|
78
47
|
# Creates a new Dalli::Client instance with specified addresses and options.
|
|
@@ -90,22 +59,21 @@ module ActiveSupport
|
|
|
90
59
|
addresses = nil if addresses.compact.empty?
|
|
91
60
|
pool_options = retrieve_pool_options(options)
|
|
92
61
|
|
|
93
|
-
if pool_options
|
|
94
|
-
Dalli::Client.new(addresses, options)
|
|
95
|
-
else
|
|
96
|
-
ensure_connection_pool_added!
|
|
62
|
+
if pool_options
|
|
97
63
|
ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
|
|
64
|
+
else
|
|
65
|
+
Dalli::Client.new(addresses, options)
|
|
98
66
|
end
|
|
99
67
|
end
|
|
100
68
|
|
|
101
|
-
# Creates a new MemCacheStore object, with the given memcached server
|
|
69
|
+
# Creates a new +MemCacheStore+ object, with the given memcached server
|
|
102
70
|
# addresses. Each address is either a host name, or a host-with-port string
|
|
103
71
|
# in the form of "host_name:port". For example:
|
|
104
72
|
#
|
|
105
73
|
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
|
106
74
|
#
|
|
107
75
|
# If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
|
|
108
|
-
# MemCacheStore will connect to localhost:11211 (the default memcached port).
|
|
76
|
+
# +MemCacheStore+ will connect to localhost:11211 (the default memcached port).
|
|
109
77
|
def initialize(*addresses)
|
|
110
78
|
addresses = addresses.flatten
|
|
111
79
|
options = addresses.extract_options!
|
|
@@ -115,41 +83,85 @@ module ActiveSupport
|
|
|
115
83
|
super(options)
|
|
116
84
|
|
|
117
85
|
unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
|
|
118
|
-
raise ArgumentError, "First argument must be an empty array,
|
|
119
|
-
end
|
|
120
|
-
if addresses.first.is_a?(Dalli::Client)
|
|
121
|
-
@data = addresses.first
|
|
122
|
-
else
|
|
123
|
-
mem_cache_options = options.dup
|
|
124
|
-
# The value "compress: false" prevents duplicate compression within Dalli.
|
|
125
|
-
mem_cache_options[:compress] = false
|
|
126
|
-
(UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) }
|
|
127
|
-
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
|
86
|
+
raise ArgumentError, "First argument must be an empty array, address, or array of addresses."
|
|
128
87
|
end
|
|
88
|
+
|
|
89
|
+
@mem_cache_options = options.dup
|
|
90
|
+
# The value "compress: false" prevents duplicate compression within Dalli.
|
|
91
|
+
@mem_cache_options[:compress] = false
|
|
92
|
+
(OVERRIDDEN_OPTIONS - %i(compress)).each { |name| @mem_cache_options.delete(name) }
|
|
93
|
+
@data = self.class.build_mem_cache(*(addresses + [@mem_cache_options]))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def inspect
|
|
97
|
+
instance = @data || @mem_cache_options
|
|
98
|
+
"#<#{self.class} options=#{options.inspect} mem_cache=#{instance.inspect}>"
|
|
129
99
|
end
|
|
130
100
|
|
|
131
|
-
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
101
|
+
##
|
|
102
|
+
# :method: write
|
|
103
|
+
# :call-seq: write(name, value, options = nil)
|
|
104
|
+
#
|
|
105
|
+
# Behaves the same as ActiveSupport::Cache::Store#write, but supports
|
|
106
|
+
# additional options specific to memcached.
|
|
107
|
+
#
|
|
108
|
+
# ==== Additional Options
|
|
109
|
+
#
|
|
110
|
+
# * <tt>raw: true</tt> - Sends the value directly to the server as raw
|
|
111
|
+
# bytes. The value must be a string or number. You can use memcached
|
|
112
|
+
# direct operations like +increment+ and +decrement+ only on raw values.
|
|
113
|
+
#
|
|
114
|
+
# * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
|
|
115
|
+
# entry.
|
|
116
|
+
|
|
117
|
+
# Increment a cached integer value using the memcached incr atomic operator.
|
|
118
|
+
# Returns the updated value.
|
|
119
|
+
#
|
|
120
|
+
# If the key is unset or has expired, it will be set to +amount+:
|
|
121
|
+
#
|
|
122
|
+
# cache.increment("foo") # => 1
|
|
123
|
+
# cache.increment("bar", 100) # => 100
|
|
124
|
+
#
|
|
125
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
126
|
+
#
|
|
127
|
+
# cache.write("baz", 5, raw: true)
|
|
128
|
+
# cache.increment("baz") # => 6
|
|
129
|
+
#
|
|
130
|
+
# Incrementing a non-numeric value, or a value written without
|
|
131
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
135
132
|
def increment(name, amount = 1, options = nil)
|
|
136
133
|
options = merged_options(options)
|
|
137
|
-
|
|
134
|
+
key = normalize_key(name, options)
|
|
135
|
+
|
|
136
|
+
instrument(:increment, key, amount: amount) do
|
|
138
137
|
rescue_error_with nil do
|
|
139
|
-
@data.with { |c| c.incr(
|
|
138
|
+
@data.with { |c| c.incr(key, amount, options[:expires_in], amount) }
|
|
140
139
|
end
|
|
141
140
|
end
|
|
142
141
|
end
|
|
143
142
|
|
|
144
|
-
# Decrement a cached value
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
# to
|
|
143
|
+
# Decrement a cached integer value using the memcached decr atomic operator.
|
|
144
|
+
# Returns the updated value.
|
|
145
|
+
#
|
|
146
|
+
# If the key is unset or has expired, it will be set to 0. Memcached
|
|
147
|
+
# does not support negative counters.
|
|
148
|
+
#
|
|
149
|
+
# cache.decrement("foo") # => 0
|
|
150
|
+
#
|
|
151
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
152
|
+
#
|
|
153
|
+
# cache.write("baz", 5, raw: true)
|
|
154
|
+
# cache.decrement("baz") # => 4
|
|
155
|
+
#
|
|
156
|
+
# Decrementing a non-numeric value, or a value written without
|
|
157
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
148
158
|
def decrement(name, amount = 1, options = nil)
|
|
149
159
|
options = merged_options(options)
|
|
150
|
-
|
|
160
|
+
key = normalize_key(name, options)
|
|
161
|
+
|
|
162
|
+
instrument(:decrement, key, amount: amount) do
|
|
151
163
|
rescue_error_with nil do
|
|
152
|
-
@data.with { |c| c.decr(
|
|
164
|
+
@data.with { |c| c.decr(key, amount, options[:expires_in], 0) }
|
|
153
165
|
end
|
|
154
166
|
end
|
|
155
167
|
end
|
|
@@ -166,54 +178,6 @@ module ActiveSupport
|
|
|
166
178
|
end
|
|
167
179
|
|
|
168
180
|
private
|
|
169
|
-
module Coders # :nodoc:
|
|
170
|
-
class << self
|
|
171
|
-
def [](version)
|
|
172
|
-
case version
|
|
173
|
-
when 6.1
|
|
174
|
-
Rails61Coder
|
|
175
|
-
when 7.0
|
|
176
|
-
Rails70Coder
|
|
177
|
-
else
|
|
178
|
-
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
module Loader
|
|
184
|
-
def load(payload)
|
|
185
|
-
if payload.is_a?(Entry)
|
|
186
|
-
payload
|
|
187
|
-
else
|
|
188
|
-
Cache::Coders::Loader.load(payload)
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
module Rails61Coder
|
|
194
|
-
include Loader
|
|
195
|
-
extend self
|
|
196
|
-
|
|
197
|
-
def dump(entry)
|
|
198
|
-
entry
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def dump_compressed(entry, threshold)
|
|
202
|
-
entry.compressed(threshold)
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
module Rails70Coder
|
|
207
|
-
include Cache::Coders::Rails70Coder
|
|
208
|
-
include Loader
|
|
209
|
-
extend self
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def default_coder
|
|
214
|
-
Coders[Cache.format_version]
|
|
215
|
-
end
|
|
216
|
-
|
|
217
181
|
# Read an entry from the cache.
|
|
218
182
|
def read_entry(key, **options)
|
|
219
183
|
deserialize_entry(read_serialized_entry(key, **options), **options)
|
|
@@ -237,10 +201,10 @@ module ActiveSupport
|
|
|
237
201
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
|
238
202
|
expires_in += 5.minutes
|
|
239
203
|
end
|
|
240
|
-
rescue_error_with
|
|
204
|
+
rescue_error_with nil do
|
|
241
205
|
# Don't pass compress option to Dalli since we are already dealing with compression.
|
|
242
206
|
options.delete(:compress)
|
|
243
|
-
@data.with { |c| c.send(method, key, payload, expires_in, **options) }
|
|
207
|
+
@data.with { |c| !!c.send(method, key, payload, expires_in, **options) }
|
|
244
208
|
end
|
|
245
209
|
end
|
|
246
210
|
|
|
@@ -248,18 +212,24 @@ module ActiveSupport
|
|
|
248
212
|
def read_multi_entries(names, **options)
|
|
249
213
|
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
|
250
214
|
|
|
251
|
-
|
|
252
|
-
|
|
215
|
+
rescue_error_with({}) do
|
|
216
|
+
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
|
253
217
|
|
|
254
|
-
|
|
255
|
-
entry = deserialize_entry(value, raw: options[:raw])
|
|
218
|
+
values = {}
|
|
256
219
|
|
|
257
|
-
|
|
258
|
-
|
|
220
|
+
raw_values.each do |key, value|
|
|
221
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
|
222
|
+
|
|
223
|
+
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
|
224
|
+
begin
|
|
225
|
+
values[keys_to_names[key]] = entry.value
|
|
226
|
+
rescue DeserializationError
|
|
227
|
+
end
|
|
228
|
+
end
|
|
259
229
|
end
|
|
260
|
-
end
|
|
261
230
|
|
|
262
|
-
|
|
231
|
+
values
|
|
232
|
+
end
|
|
263
233
|
end
|
|
264
234
|
|
|
265
235
|
# Delete an entry from the cache.
|
|
@@ -283,7 +253,13 @@ module ActiveSupport
|
|
|
283
253
|
if key
|
|
284
254
|
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
|
285
255
|
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
|
286
|
-
|
|
256
|
+
|
|
257
|
+
if key.size > KEY_MAX_SIZE
|
|
258
|
+
key_separator = ":hash:"
|
|
259
|
+
key_hash = ActiveSupport::Digest.hexdigest(key)
|
|
260
|
+
key_trim_size = KEY_MAX_SIZE - key_separator.size - key_hash.size
|
|
261
|
+
key = "#{key[0, key_trim_size]}#{key_separator}#{key_hash}"
|
|
262
|
+
end
|
|
287
263
|
end
|
|
288
264
|
key
|
|
289
265
|
end
|
|
@@ -298,9 +274,13 @@ module ActiveSupport
|
|
|
298
274
|
|
|
299
275
|
def rescue_error_with(fallback)
|
|
300
276
|
yield
|
|
301
|
-
rescue Dalli::DalliError => error
|
|
302
|
-
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
|
277
|
+
rescue Dalli::DalliError, ConnectionPool::Error, ConnectionPool::TimeoutError => error
|
|
303
278
|
logger.error("DalliError (#{error}): #{error.message}") if logger
|
|
279
|
+
ActiveSupport.error_reporter&.report(
|
|
280
|
+
error,
|
|
281
|
+
severity: :warning,
|
|
282
|
+
source: "mem_cache_store.active_support",
|
|
283
|
+
)
|
|
304
284
|
fallback
|
|
305
285
|
end
|
|
306
286
|
end
|