activesupport 1.2.4 → 8.1.2
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 +7 -0
- data/CHANGELOG.md +505 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +40 -0
- data/lib/active_support/actionable_error.rb +50 -0
- data/lib/active_support/all.rb +5 -0
- data/lib/active_support/array_inquirer.rb +50 -0
- data/lib/active_support/backtrace_cleaner.rb +234 -0
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +53 -0
- data/lib/active_support/broadcast_logger.rb +238 -0
- data/lib/active_support/builder.rb +8 -0
- 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 +244 -0
- data/lib/active_support/cache/mem_cache_store.rb +288 -0
- data/lib/active_support/cache/memory_store.rb +264 -0
- data/lib/active_support/cache/null_store.rb +62 -0
- data/lib/active_support/cache/redis_cache_store.rb +498 -0
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +246 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +1170 -0
- data/lib/active_support/callbacks.rb +960 -0
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +79 -0
- data/lib/active_support/concern.rb +217 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +225 -0
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +193 -0
- data/lib/active_support/configuration_file.rb +60 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/access.rb +100 -0
- data/lib/active_support/core_ext/array/conversions.rb +209 -26
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +31 -0
- data/lib/active_support/core_ext/array/grouping.rb +109 -0
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/core_ext/array.rb +8 -4
- data/lib/active_support/core_ext/benchmark.rb +6 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/big_decimal.rb +3 -0
- data/lib/active_support/core_ext/class/attribute.rb +137 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/class/subclasses.rb +24 -0
- data/lib/active_support/core_ext/class.rb +4 -0
- data/lib/active_support/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/core_ext/date/blank.rb +18 -0
- data/lib/active_support/core_ext/date/calculations.rb +161 -0
- data/lib/active_support/core_ext/date/conversions.rb +95 -28
- data/lib/active_support/core_ext/date/zones.rb +8 -0
- data/lib/active_support/core_ext/date.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
- data/lib/active_support/core_ext/date_time/blank.rb +18 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
- data/lib/active_support/core_ext/date_time.rb +7 -0
- data/lib/active_support/core_ext/digest/uuid.rb +76 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +277 -7
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +72 -0
- data/lib/active_support/core_ext/file.rb +3 -0
- data/lib/active_support/core_ext/hash/conversions.rb +262 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +12 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
- data/lib/active_support/core_ext/hash/keys.rb +134 -44
- data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
- data/lib/active_support/core_ext/hash/slice.rb +27 -0
- data/lib/active_support/core_ext/hash.rb +9 -8
- data/lib/active_support/core_ext/integer/inflections.rb +29 -13
- data/lib/active_support/core_ext/integer/multiple.rb +12 -0
- data/lib/active_support/core_ext/integer/time.rb +22 -0
- data/lib/active_support/core_ext/integer.rb +4 -6
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/active_support/core_ext/kernel.rb +4 -78
- data/lib/active_support/core_ext/load_error.rb +6 -35
- data/lib/active_support/core_ext/module/aliasing.rb +31 -0
- data/lib/active_support/core_ext/module/anonymous.rb +30 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +225 -0
- data/lib/active_support/core_ext/module/deprecation.rb +25 -0
- data/lib/active_support/core_ext/module/introspection.rb +65 -0
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +17 -0
- data/lib/active_support/core_ext/module.rb +13 -0
- data/lib/active_support/core_ext/name_error.rb +59 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
- data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
- data/lib/active_support/core_ext/numeric/time.rb +64 -57
- data/lib/active_support/core_ext/numeric.rb +4 -6
- data/lib/active_support/core_ext/object/acts_like.rb +45 -0
- data/lib/active_support/core_ext/object/blank.rb +199 -0
- data/lib/active_support/core_ext/object/conversions.rb +6 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
- data/lib/active_support/core_ext/object/duplicable.rb +69 -0
- data/lib/active_support/core_ext/object/inclusion.rb +37 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
- data/lib/active_support/core_ext/object/json.rb +267 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -0
- data/lib/active_support/core_ext/object/to_query.rb +93 -0
- data/lib/active_support/core_ext/object/try.rb +158 -0
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +101 -0
- data/lib/active_support/core_ext/object.rb +17 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +58 -17
- 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 +5 -4
- data/lib/active_support/core_ext/regexp.rb +14 -0
- data/lib/active_support/core_ext/securerandom.rb +57 -0
- data/lib/active_support/core_ext/string/access.rb +93 -56
- data/lib/active_support/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/core_ext/string/conversions.rb +57 -16
- data/lib/active_support/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/core_ext/string/filters.rb +151 -0
- data/lib/active_support/core_ext/string/indent.rb +45 -0
- data/lib/active_support/core_ext/string/inflections.rb +297 -54
- data/lib/active_support/core_ext/string/inquiry.rb +16 -0
- data/lib/active_support/core_ext/string/multibyte.rb +67 -0
- data/lib/active_support/core_ext/string/output_safety.rb +235 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
- data/lib/active_support/core_ext/string/strip.rb +27 -0
- data/lib/active_support/core_ext/string/zones.rb +16 -0
- data/lib/active_support/core_ext/string.rb +14 -10
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
- data/lib/active_support/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/core_ext/time/calculations.rb +358 -153
- data/lib/active_support/core_ext/time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/time/conversions.rb +69 -30
- data/lib/active_support/core_ext/time/zones.rb +97 -0
- data/lib/active_support/core_ext/time.rb +6 -6
- data/lib/active_support/core_ext.rb +5 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +243 -0
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +183 -0
- data/lib/active_support/dependencies/autoload.rb +72 -0
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +84 -222
- data/lib/active_support/deprecation/behaviors.rb +148 -0
- data/lib/active_support/deprecation/constant_accessor.rb +74 -0
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +54 -0
- data/lib/active_support/deprecation/method_wrappers.rb +68 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
- data/lib/active_support/deprecation/reporting.rb +162 -0
- data/lib/active_support/deprecation.rb +81 -0
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +112 -0
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +64 -0
- data/lib/active_support/duration.rb +524 -0
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +126 -0
- data/lib/active_support/encrypted_file.rb +133 -0
- data/lib/active_support/environment_inquirer.rb +40 -0
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +318 -0
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +185 -0
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +110 -0
- data/lib/active_support/execution_wrapper.rb +150 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +166 -0
- data/lib/active_support/fork_tracker.rb +43 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +41 -0
- data/lib/active_support/hash_with_indifferent_access.rb +464 -0
- data/lib/active_support/html_safe_translation.rb +56 -0
- data/lib/active_support/i18n.rb +17 -0
- data/lib/active_support/i18n_railtie.rb +140 -0
- data/lib/active_support/inflections.rb +68 -49
- data/lib/active_support/inflector/inflections.rb +290 -0
- data/lib/active_support/inflector/methods.rb +387 -0
- data/lib/active_support/inflector/transliterate.rb +147 -0
- data/lib/active_support/inflector.rb +7 -164
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +78 -0
- data/lib/active_support/json/encoding.rb +256 -0
- data/lib/active_support/json.rb +4 -0
- data/lib/active_support/key_generator.rb +66 -0
- data/lib/active_support/lazy_load_hooks.rb +107 -0
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +141 -0
- data/lib/active_support/log_subscriber/test_helper.rb +106 -0
- data/lib/active_support/log_subscriber.rb +188 -0
- data/lib/active_support/logger.rb +55 -0
- data/lib/active_support/logger_silence.rb +21 -0
- data/lib/active_support/logger_thread_safe_level.rb +50 -0
- data/lib/active_support/message_encryptor.rb +374 -0
- data/lib/active_support/message_encryptors.rb +193 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +310 -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 +377 -0
- data/lib/active_support/message_verifiers.rb +189 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +146 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotation_coordinator.rb +102 -0
- data/lib/active_support/messages/rotator.rb +69 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +188 -0
- data/lib/active_support/multibyte/unicode.rb +42 -0
- data/lib/active_support/multibyte.rb +27 -0
- data/lib/active_support/notifications/fanout.rb +467 -0
- data/lib/active_support/notifications/instrumenter.rb +240 -0
- data/lib/active_support/notifications.rb +281 -0
- data/lib/active_support/number_helper/number_converter.rb +190 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +479 -0
- data/lib/active_support/option_merger.rb +38 -0
- data/lib/active_support/ordered_hash.rb +50 -0
- data/lib/active_support/ordered_options.rb +141 -25
- data/lib/active_support/parameter_filter.rb +157 -0
- data/lib/active_support/rails.rb +26 -0
- data/lib/active_support/railtie.rb +180 -0
- data/lib/active_support/reloader.rb +138 -0
- data/lib/active_support/rescuable.rb +176 -0
- data/lib/active_support/secure_compare_rotator.rb +58 -0
- data/lib/active_support/security_utils.rb +38 -0
- data/lib/active_support/string_inquirer.rb +35 -0
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +141 -0
- data/lib/active_support/syntax_error_proxy.rb +67 -0
- data/lib/active_support/tagged_logging.rb +157 -0
- data/lib/active_support/test_case.rb +365 -0
- data/lib/active_support/testing/assertions.rb +369 -0
- data/lib/active_support/testing/autorun.rb +10 -0
- data/lib/active_support/testing/constant_lookup.rb +51 -0
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/declarative.rb +28 -0
- data/lib/active_support/testing/deprecation.rb +82 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +121 -0
- data/lib/active_support/testing/method_call_assertions.rb +69 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +98 -0
- data/lib/active_support/testing/parallelization/worker.rb +107 -0
- data/lib/active_support/testing/parallelization.rb +79 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +57 -0
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +27 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +273 -0
- data/lib/active_support/time.rb +20 -0
- data/lib/active_support/time_with_zone.rb +613 -0
- data/lib/active_support/values/time_zone.rb +599 -158
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +175 -0
- data/lib/active_support/xml_mini/libxml.rb +80 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
- data/lib/active_support/xml_mini/nokogiri.rb +83 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
- data/lib/active_support/xml_mini/rexml.rb +137 -0
- data/lib/active_support/xml_mini.rb +212 -0
- data/lib/active_support.rb +122 -10
- metadata +524 -93
- data/CHANGELOG +0 -283
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -523
- data/lib/active_support/class_attribute_accessors.rb +0 -57
- data/lib/active_support/class_inheritable_attributes.rb +0 -117
- data/lib/active_support/clean_logger.rb +0 -36
- data/lib/active_support/core_ext/blank.rb +0 -38
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
- data/lib/active_support/core_ext/cgi.rb +0 -5
- data/lib/active_support/core_ext/exception.rb +0 -29
- data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
- data/lib/active_support/core_ext/object_and_class.rb +0 -44
- data/lib/active_support/module_attribute_accessors.rb +0 -57
- data/lib/active_support/whiny_nil.rb +0 -38
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
gem "redis", ">= 4.0.1"
|
|
5
|
+
require "redis"
|
|
6
|
+
require "redis/distributed"
|
|
7
|
+
rescue LoadError
|
|
8
|
+
warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \">= 4.0.1\"`"
|
|
9
|
+
raise
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "connection_pool"
|
|
13
|
+
require "active_support/core_ext/array/wrap"
|
|
14
|
+
require "active_support/core_ext/hash/slice"
|
|
15
|
+
require "active_support/core_ext/numeric/time"
|
|
16
|
+
require "active_support/digest"
|
|
17
|
+
|
|
18
|
+
module ActiveSupport
|
|
19
|
+
module Cache
|
|
20
|
+
# = Redis \Cache \Store
|
|
21
|
+
#
|
|
22
|
+
# Deployment note: Take care to use a <b>dedicated Redis cache</b> rather
|
|
23
|
+
# than pointing this at a persistent Redis server (for example, one used as
|
|
24
|
+
# an Active Job queue). Redis won't cope well with mixed usage patterns and it
|
|
25
|
+
# won't expire cache entries by default.
|
|
26
|
+
#
|
|
27
|
+
# Redis cache server setup guide: https://redis.io/topics/lru-cache
|
|
28
|
+
#
|
|
29
|
+
# * Supports vanilla Redis, hiredis, and +Redis::Distributed+.
|
|
30
|
+
# * Supports Memcached-like sharding across Redises with +Redis::Distributed+.
|
|
31
|
+
# * Fault tolerant. If the Redis server is unavailable, no exceptions are
|
|
32
|
+
# raised. Cache fetches are all misses and writes are dropped.
|
|
33
|
+
# * Local cache. Hot in-memory primary cache within block/middleware scope.
|
|
34
|
+
# * +read_multi+ and +write_multi+ support for Redis mget/mset. Use
|
|
35
|
+
# +Redis::Distributed+ 4.0.1+ for distributed mget support.
|
|
36
|
+
# * +delete_matched+ support for Redis KEYS globs.
|
|
37
|
+
class RedisCacheStore < Store
|
|
38
|
+
DEFAULT_REDIS_OPTIONS = {
|
|
39
|
+
connect_timeout: 1,
|
|
40
|
+
read_timeout: 1,
|
|
41
|
+
write_timeout: 1,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do
|
|
45
|
+
if logger
|
|
46
|
+
logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
|
|
47
|
+
end
|
|
48
|
+
ActiveSupport.error_reporter&.report(
|
|
49
|
+
exception,
|
|
50
|
+
severity: :warning,
|
|
51
|
+
source: "redis_cache_store.active_support",
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The maximum number of entries to receive per SCAN call.
|
|
56
|
+
SCAN_BATCH_SIZE = 1000
|
|
57
|
+
private_constant :SCAN_BATCH_SIZE
|
|
58
|
+
|
|
59
|
+
# Advertise cache versioning support.
|
|
60
|
+
def self.supports_cache_versioning?
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
prepend Strategy::LocalCache
|
|
65
|
+
|
|
66
|
+
class << self
|
|
67
|
+
# Factory method to create a new Redis instance.
|
|
68
|
+
#
|
|
69
|
+
# Handles four options: :redis block, :redis instance, single :url
|
|
70
|
+
# string, and multiple :url strings.
|
|
71
|
+
#
|
|
72
|
+
# Option Class Result
|
|
73
|
+
# :redis Proc -> options[:redis].call
|
|
74
|
+
# :redis Object -> options[:redis]
|
|
75
|
+
# :url String -> Redis.new(url: …)
|
|
76
|
+
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
|
77
|
+
#
|
|
78
|
+
def build_redis(redis: nil, url: nil, **redis_options) # :nodoc:
|
|
79
|
+
urls = Array(url)
|
|
80
|
+
|
|
81
|
+
if redis.is_a?(Proc)
|
|
82
|
+
redis.call
|
|
83
|
+
elsif redis
|
|
84
|
+
redis
|
|
85
|
+
elsif urls.size > 1
|
|
86
|
+
build_redis_distributed_client(urls: urls, **redis_options)
|
|
87
|
+
elsif urls.empty?
|
|
88
|
+
build_redis_client(**redis_options)
|
|
89
|
+
else
|
|
90
|
+
build_redis_client(url: urls.first, **redis_options)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
def build_redis_distributed_client(urls:, **redis_options)
|
|
96
|
+
::Redis::Distributed.new([], DEFAULT_REDIS_OPTIONS.merge(redis_options)).tap do |dist|
|
|
97
|
+
urls.each { |u| dist.add_node url: u }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_redis_client(**redis_options)
|
|
102
|
+
::Redis.new(DEFAULT_REDIS_OPTIONS.merge(redis_options))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
attr_reader :redis
|
|
107
|
+
|
|
108
|
+
# Creates a new Redis cache store.
|
|
109
|
+
#
|
|
110
|
+
# There are a few ways to provide the Redis client used by the cache:
|
|
111
|
+
#
|
|
112
|
+
# 1. The +:redis+ param can be:
|
|
113
|
+
# - A Redis instance.
|
|
114
|
+
# - A +ConnectionPool+ instance wrapping a Redis instance.
|
|
115
|
+
# - A block that returns a Redis instance.
|
|
116
|
+
#
|
|
117
|
+
# 2. The +:url+ param can be:
|
|
118
|
+
# - A string used to create a Redis instance.
|
|
119
|
+
# - An array of strings used to create a +Redis::Distributed+ instance.
|
|
120
|
+
#
|
|
121
|
+
# If the final Redis instance is not already a +ConnectionPool+, it will
|
|
122
|
+
# be wrapped in one using +ActiveSupport::Cache::Store::DEFAULT_POOL_OPTIONS+.
|
|
123
|
+
# These options can be overridden with the +:pool+ param, or the pool can be
|
|
124
|
+
# disabled with +:pool: false+.
|
|
125
|
+
#
|
|
126
|
+
# Option Class Result
|
|
127
|
+
# :redis Object -> options[:redis]
|
|
128
|
+
# :redis Proc -> options[:redis].call
|
|
129
|
+
# :url String -> Redis.new(url: …)
|
|
130
|
+
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
|
131
|
+
#
|
|
132
|
+
# No namespace is set by default. Provide one if the Redis cache
|
|
133
|
+
# server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
|
|
134
|
+
#
|
|
135
|
+
# Compression is enabled by default with a 1kB threshold, so cached
|
|
136
|
+
# values larger than 1kB are automatically compressed. Disable by
|
|
137
|
+
# passing <tt>compress: false</tt> or change the threshold by passing
|
|
138
|
+
# <tt>compress_threshold: 4.kilobytes</tt>.
|
|
139
|
+
#
|
|
140
|
+
# No expiry is set on cache entries by default. Redis is expected to
|
|
141
|
+
# be configured with an eviction policy that automatically deletes
|
|
142
|
+
# least-recently or -frequently used keys when it reaches max memory.
|
|
143
|
+
# See https://redis.io/topics/lru-cache for cache server setup.
|
|
144
|
+
#
|
|
145
|
+
# Race condition TTL is not set by default. This can be used to avoid
|
|
146
|
+
# "thundering herd" cache writes when hot cache entries are expired.
|
|
147
|
+
# See ActiveSupport::Cache::Store#fetch for more.
|
|
148
|
+
#
|
|
149
|
+
# Setting <tt>skip_nil: true</tt> will not cache nil results:
|
|
150
|
+
#
|
|
151
|
+
# cache.fetch('foo') { nil }
|
|
152
|
+
# cache.fetch('bar', skip_nil: true) { nil }
|
|
153
|
+
# cache.exist?('foo') # => true
|
|
154
|
+
# cache.exist?('bar') # => false
|
|
155
|
+
def initialize(error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
|
156
|
+
universal_options = redis_options.extract!(*UNIVERSAL_OPTIONS)
|
|
157
|
+
redis = redis_options[:redis]
|
|
158
|
+
|
|
159
|
+
already_pool = redis.instance_of?(::ConnectionPool) ||
|
|
160
|
+
(redis.respond_to?(:wrapped_pool) && redis.wrapped_pool.instance_of?(::ConnectionPool))
|
|
161
|
+
|
|
162
|
+
if !already_pool && pool_options = self.class.send(:retrieve_pool_options, redis_options)
|
|
163
|
+
@redis = ::ConnectionPool.new(**pool_options) { self.class.build_redis(**redis_options) }
|
|
164
|
+
else
|
|
165
|
+
@redis = self.class.build_redis(**redis_options)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
@error_handler = error_handler
|
|
169
|
+
|
|
170
|
+
super(universal_options)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def inspect
|
|
174
|
+
"#<#{self.class} options=#{options.inspect} redis=#{redis.inspect}>"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Cache Store API implementation.
|
|
178
|
+
#
|
|
179
|
+
# Read multiple values at once. Returns a hash of requested keys ->
|
|
180
|
+
# fetched values.
|
|
181
|
+
def read_multi(*names)
|
|
182
|
+
return {} if names.empty?
|
|
183
|
+
|
|
184
|
+
options = names.extract_options!
|
|
185
|
+
options = merged_options(options)
|
|
186
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
187
|
+
|
|
188
|
+
instrument_multi(:read_multi, keys, options) do |payload|
|
|
189
|
+
read_multi_entries(names, **options).tap do |results|
|
|
190
|
+
payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Cache Store API implementation.
|
|
196
|
+
#
|
|
197
|
+
# Supports Redis KEYS glob patterns:
|
|
198
|
+
#
|
|
199
|
+
# h?llo matches hello, hallo and hxllo
|
|
200
|
+
# h*llo matches hllo and heeeello
|
|
201
|
+
# h[ae]llo matches hello and hallo, but not hillo
|
|
202
|
+
# h[^e]llo matches hallo, hbllo, ... but not hello
|
|
203
|
+
# h[a-b]llo matches hallo and hbllo
|
|
204
|
+
#
|
|
205
|
+
# Use \ to escape special characters if you want to match them verbatim.
|
|
206
|
+
#
|
|
207
|
+
# See https://redis.io/commands/KEYS for more.
|
|
208
|
+
#
|
|
209
|
+
# Failsafe: Raises errors.
|
|
210
|
+
def delete_matched(matcher, options = nil)
|
|
211
|
+
unless String === matcher
|
|
212
|
+
raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
|
|
213
|
+
end
|
|
214
|
+
pattern = namespace_key(matcher, options)
|
|
215
|
+
|
|
216
|
+
instrument :delete_matched, pattern do
|
|
217
|
+
redis.then do |c|
|
|
218
|
+
cursor = "0"
|
|
219
|
+
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
|
|
220
|
+
nodes = c.respond_to?(:nodes) ? c.nodes : [c]
|
|
221
|
+
|
|
222
|
+
nodes.each do |node|
|
|
223
|
+
begin
|
|
224
|
+
cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
|
|
225
|
+
node.unlink(*keys) unless keys.empty?
|
|
226
|
+
end until cursor == "0"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Increment a cached integer value using the Redis incrby atomic operator.
|
|
233
|
+
# Returns the updated value.
|
|
234
|
+
#
|
|
235
|
+
# If the key is unset or has expired, it will be set to +amount+:
|
|
236
|
+
#
|
|
237
|
+
# cache.increment("foo") # => 1
|
|
238
|
+
# cache.increment("bar", 100) # => 100
|
|
239
|
+
#
|
|
240
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
241
|
+
#
|
|
242
|
+
# cache.write("baz", 5, raw: true)
|
|
243
|
+
# cache.increment("baz") # => 6
|
|
244
|
+
#
|
|
245
|
+
# Incrementing a non-numeric value, or a value written without
|
|
246
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
247
|
+
#
|
|
248
|
+
# To read the value later, call #read_counter:
|
|
249
|
+
#
|
|
250
|
+
# cache.increment("baz") # => 7
|
|
251
|
+
# cache.read_counter("baz") # 7
|
|
252
|
+
#
|
|
253
|
+
# Failsafe: Raises errors.
|
|
254
|
+
def increment(name, amount = 1, options = nil)
|
|
255
|
+
options = merged_options(options)
|
|
256
|
+
key = normalize_key(name, options)
|
|
257
|
+
|
|
258
|
+
instrument :increment, key, amount: amount do
|
|
259
|
+
failsafe :increment do
|
|
260
|
+
change_counter(key, amount, options)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Decrement a cached integer value using the Redis decrby atomic operator.
|
|
266
|
+
# Returns the updated value.
|
|
267
|
+
#
|
|
268
|
+
# If the key is unset or has expired, it will be set to +-amount+:
|
|
269
|
+
#
|
|
270
|
+
# cache.decrement("foo") # => -1
|
|
271
|
+
#
|
|
272
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
|
273
|
+
#
|
|
274
|
+
# cache.write("baz", 5, raw: true)
|
|
275
|
+
# cache.decrement("baz") # => 4
|
|
276
|
+
#
|
|
277
|
+
# Decrementing a non-numeric value, or a value written without
|
|
278
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
|
279
|
+
#
|
|
280
|
+
# To read the value later, call #read_counter:
|
|
281
|
+
#
|
|
282
|
+
# cache.decrement("baz") # => 3
|
|
283
|
+
# cache.read_counter("baz") # 3
|
|
284
|
+
#
|
|
285
|
+
# Failsafe: Raises errors.
|
|
286
|
+
def decrement(name, amount = 1, options = nil)
|
|
287
|
+
options = merged_options(options)
|
|
288
|
+
key = normalize_key(name, options)
|
|
289
|
+
|
|
290
|
+
instrument :decrement, key, amount: amount do
|
|
291
|
+
failsafe :decrement do
|
|
292
|
+
change_counter(key, -amount, options)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Cache Store API implementation.
|
|
298
|
+
#
|
|
299
|
+
# Removes expired entries. Handled natively by Redis least-recently-/
|
|
300
|
+
# least-frequently-used expiry, so manual cleanup is not supported.
|
|
301
|
+
def cleanup(options = nil)
|
|
302
|
+
super
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Clear the entire cache on all Redis servers. Safe to use on
|
|
306
|
+
# shared servers if the cache is namespaced.
|
|
307
|
+
#
|
|
308
|
+
# Failsafe: Raises errors.
|
|
309
|
+
def clear(options = nil)
|
|
310
|
+
failsafe :clear do
|
|
311
|
+
if namespace = merged_options(options)[:namespace]
|
|
312
|
+
delete_matched "*", namespace: namespace
|
|
313
|
+
else
|
|
314
|
+
redis.then { |c| c.flushdb }
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Get info from redis servers.
|
|
320
|
+
def stats
|
|
321
|
+
redis.then { |c| c.info }
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
private
|
|
325
|
+
def pipeline_entries(entries, &block)
|
|
326
|
+
redis.then { |c|
|
|
327
|
+
if c.is_a?(Redis::Distributed)
|
|
328
|
+
entries.group_by { |k, _v| c.node_for(k) }.each do |node, sub_entries|
|
|
329
|
+
node.pipelined { |pipe| yield(pipe, sub_entries) }
|
|
330
|
+
end
|
|
331
|
+
else
|
|
332
|
+
c.pipelined { |pipe| yield(pipe, entries) }
|
|
333
|
+
end
|
|
334
|
+
}
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Store provider interface:
|
|
338
|
+
# Read an entry from the cache.
|
|
339
|
+
def read_entry(key, **options)
|
|
340
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def read_serialized_entry(key, raw: false, **options)
|
|
344
|
+
failsafe :read_entry do
|
|
345
|
+
redis.then { |c| c.get(key) }
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def read_multi_entries(names, **options)
|
|
350
|
+
options = merged_options(options)
|
|
351
|
+
return {} if names == []
|
|
352
|
+
raw = options&.fetch(:raw, false)
|
|
353
|
+
|
|
354
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
355
|
+
|
|
356
|
+
values = failsafe(:read_multi_entries, returning: {}) do
|
|
357
|
+
redis.then { |c| c.mget(*keys) }
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
names.zip(values).each_with_object({}) do |(name, value), results|
|
|
361
|
+
if value
|
|
362
|
+
entry = deserialize_entry(value, raw: raw)
|
|
363
|
+
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
|
|
364
|
+
begin
|
|
365
|
+
results[name] = entry.value
|
|
366
|
+
rescue DeserializationError
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Write an entry to the cache.
|
|
374
|
+
#
|
|
375
|
+
# Requires Redis 2.6.12+ for extended SET options.
|
|
376
|
+
def write_entry(key, entry, raw: false, **options)
|
|
377
|
+
write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, pipeline: nil, **options)
|
|
381
|
+
# If race condition TTL is in use, ensure that cache entries
|
|
382
|
+
# stick around a bit longer after they would have expired
|
|
383
|
+
# so we can purposefully serve stale entries.
|
|
384
|
+
if race_condition_ttl && expires_in && expires_in > 0 && !raw
|
|
385
|
+
expires_in += 5.minutes
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
modifiers = {}
|
|
389
|
+
if unless_exist || expires_in
|
|
390
|
+
modifiers[:nx] = unless_exist
|
|
391
|
+
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
if pipeline
|
|
395
|
+
pipeline.set(key, payload, **modifiers)
|
|
396
|
+
else
|
|
397
|
+
failsafe :write_entry, returning: nil do
|
|
398
|
+
redis.then { |c| !!c.set(key, payload, **modifiers) }
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Delete an entry from the cache.
|
|
404
|
+
def delete_entry(key, **options)
|
|
405
|
+
failsafe :delete_entry, returning: false do
|
|
406
|
+
redis.then { |c| c.unlink(key) == 1 }
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
|
411
|
+
def delete_multi_entries(entries, **_options)
|
|
412
|
+
return 0 if entries.empty?
|
|
413
|
+
|
|
414
|
+
failsafe :delete_multi_entries, returning: 0 do
|
|
415
|
+
redis.then { |c| c.unlink(*entries) }
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Nonstandard store provider API to write multiple values at once.
|
|
420
|
+
def write_multi_entries(entries, **options)
|
|
421
|
+
return if entries.empty?
|
|
422
|
+
|
|
423
|
+
failsafe :write_multi_entries do
|
|
424
|
+
pipeline_entries(entries) do |pipeline, sharded_entries|
|
|
425
|
+
options = options.dup
|
|
426
|
+
options[:pipeline] = pipeline
|
|
427
|
+
sharded_entries.each do |key, entry|
|
|
428
|
+
write_entry key, entry, **options
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def deserialize_entry(payload, raw: false, **)
|
|
435
|
+
if raw && !payload.nil?
|
|
436
|
+
Entry.new(payload)
|
|
437
|
+
else
|
|
438
|
+
super(payload)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def serialize_entry(entry, raw: false, **options)
|
|
443
|
+
if raw
|
|
444
|
+
entry.value.to_s
|
|
445
|
+
else
|
|
446
|
+
super(entry, raw: raw, **options)
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def serialize_entries(entries, **options)
|
|
451
|
+
entries.transform_values do |entry|
|
|
452
|
+
serialize_entry(entry, **options)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def change_counter(key, amount, options)
|
|
457
|
+
redis.then do |c|
|
|
458
|
+
c = c.node_for(key) if c.is_a?(Redis::Distributed)
|
|
459
|
+
|
|
460
|
+
expires_in = options[:expires_in]
|
|
461
|
+
|
|
462
|
+
if expires_in
|
|
463
|
+
if supports_expire_nx?
|
|
464
|
+
count, _ = c.pipelined do |pipeline|
|
|
465
|
+
pipeline.incrby(key, amount)
|
|
466
|
+
pipeline.call(:expire, key, expires_in.to_i, "NX")
|
|
467
|
+
end
|
|
468
|
+
else
|
|
469
|
+
count, ttl = c.pipelined do |pipeline|
|
|
470
|
+
pipeline.incrby(key, amount)
|
|
471
|
+
pipeline.ttl(key)
|
|
472
|
+
end
|
|
473
|
+
c.expire(key, expires_in.to_i) if ttl < 0
|
|
474
|
+
end
|
|
475
|
+
else
|
|
476
|
+
count = c.incrby(key, amount)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
count
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def supports_expire_nx?
|
|
484
|
+
return @supports_expire_nx if defined?(@supports_expire_nx)
|
|
485
|
+
|
|
486
|
+
redis_versions = redis.then { |c| Array.wrap(c.info("server")).pluck("redis_version") }
|
|
487
|
+
@supports_expire_nx = redis_versions.all? { |v| Gem::Version.new(v) >= Gem::Version.new("7.0.0") }
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def failsafe(method, returning: nil)
|
|
491
|
+
yield
|
|
492
|
+
rescue ::Redis::BaseError, ConnectionPool::Error, ConnectionPool::TimeoutError => error
|
|
493
|
+
@error_handler&.call(method: method, exception: error, returning: returning)
|
|
494
|
+
returning
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
require "active_support/core_ext/kernel/reporting"
|
|
5
|
+
|
|
6
|
+
module ActiveSupport
|
|
7
|
+
module Cache
|
|
8
|
+
module SerializerWithFallback # :nodoc:
|
|
9
|
+
def self.[](format)
|
|
10
|
+
if format.to_s.include?("message_pack") && !defined?(ActiveSupport::MessagePack)
|
|
11
|
+
require "active_support/message_pack"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
SERIALIZERS.fetch(format)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def load(dumped)
|
|
18
|
+
if dumped.is_a?(String)
|
|
19
|
+
case
|
|
20
|
+
when MessagePackWithFallback.dumped?(dumped)
|
|
21
|
+
MessagePackWithFallback._load(dumped)
|
|
22
|
+
when Marshal71WithFallback.dumped?(dumped)
|
|
23
|
+
Marshal71WithFallback._load(dumped)
|
|
24
|
+
when Marshal70WithFallback.dumped?(dumped)
|
|
25
|
+
Marshal70WithFallback._load(dumped)
|
|
26
|
+
else
|
|
27
|
+
Cache::Store.logger&.warn("Unrecognized payload prefix #{dumped.byteslice(0).inspect}; deserializing as nil")
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
elsif PassthroughWithFallback.dumped?(dumped)
|
|
31
|
+
PassthroughWithFallback._load(dumped)
|
|
32
|
+
else
|
|
33
|
+
Cache::Store.logger&.warn("Unrecognized payload class #{dumped.class}; deserializing as nil")
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def marshal_load(payload)
|
|
40
|
+
Marshal.load(payload)
|
|
41
|
+
rescue ArgumentError => error
|
|
42
|
+
raise Cache::DeserializationError, error.message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module PassthroughWithFallback
|
|
46
|
+
include SerializerWithFallback
|
|
47
|
+
extend self
|
|
48
|
+
|
|
49
|
+
def dump(entry)
|
|
50
|
+
entry
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def dump_compressed(entry, threshold)
|
|
54
|
+
entry.compressed(threshold)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _load(entry)
|
|
58
|
+
entry
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def dumped?(dumped)
|
|
62
|
+
dumped.is_a?(Cache::Entry)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module Marshal70WithFallback
|
|
67
|
+
include SerializerWithFallback
|
|
68
|
+
extend self
|
|
69
|
+
|
|
70
|
+
MARK_UNCOMPRESSED = "\x00".b.freeze
|
|
71
|
+
MARK_COMPRESSED = "\x01".b.freeze
|
|
72
|
+
|
|
73
|
+
def dump(entry)
|
|
74
|
+
MARK_UNCOMPRESSED + Marshal.dump(entry.pack)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def dump_compressed(entry, threshold)
|
|
78
|
+
dumped = Marshal.dump(entry.pack)
|
|
79
|
+
|
|
80
|
+
if dumped.bytesize >= threshold
|
|
81
|
+
compressed = Zlib::Deflate.deflate(dumped)
|
|
82
|
+
return MARK_COMPRESSED + compressed if compressed.bytesize < dumped.bytesize
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
MARK_UNCOMPRESSED + dumped
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def _load(marked)
|
|
89
|
+
dumped = marked.byteslice(1..-1)
|
|
90
|
+
dumped = Zlib::Inflate.inflate(dumped) if marked.start_with?(MARK_COMPRESSED)
|
|
91
|
+
Cache::Entry.unpack(marshal_load(dumped))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def dumped?(dumped)
|
|
95
|
+
dumped.start_with?(MARK_UNCOMPRESSED, MARK_COMPRESSED)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
module Marshal71WithFallback
|
|
100
|
+
include SerializerWithFallback
|
|
101
|
+
extend self
|
|
102
|
+
|
|
103
|
+
MARSHAL_SIGNATURE = "\x04\x08".b.freeze
|
|
104
|
+
|
|
105
|
+
def dump(value)
|
|
106
|
+
Marshal.dump(value)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def _load(dumped)
|
|
110
|
+
marshal_load(dumped)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def dumped?(dumped)
|
|
114
|
+
dumped.start_with?(MARSHAL_SIGNATURE)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
module MessagePackWithFallback
|
|
119
|
+
include SerializerWithFallback
|
|
120
|
+
extend self
|
|
121
|
+
|
|
122
|
+
def dump(value)
|
|
123
|
+
ActiveSupport::MessagePack::CacheSerializer.dump(value)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def _load(dumped)
|
|
127
|
+
ActiveSupport::MessagePack::CacheSerializer.load(dumped)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def dumped?(dumped)
|
|
131
|
+
available? && ActiveSupport::MessagePack.signature?(dumped)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
def available?
|
|
136
|
+
return @available if defined?(@available)
|
|
137
|
+
silence_warnings { require "active_support/message_pack" }
|
|
138
|
+
@available = true
|
|
139
|
+
rescue LoadError
|
|
140
|
+
@available = false
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
SERIALIZERS = {
|
|
145
|
+
passthrough: PassthroughWithFallback,
|
|
146
|
+
marshal_7_0: Marshal70WithFallback,
|
|
147
|
+
marshal_7_1: Marshal71WithFallback,
|
|
148
|
+
message_pack: MessagePackWithFallback,
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|