1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
|
# frozen_string_literal: true
require 'English'
module Dalli
module Protocol
##
# Dalli::Protocol::ValueCompressor compartmentalizes the logic for managing
# compression and decompression of stored values. It manages interpreting
# relevant options from both client and request, determining whether to
# compress/decompress on store/retrieve, and processes bitflags as necessary.
##
class ValueCompressor
DEFAULTS = {
compress: true,
compressor: ::Dalli::Compressor,
# min byte size to attempt compression
compression_min_size: 4 * 1024 # 4K
}.freeze
OPTIONS = DEFAULTS.keys.freeze
# https://www.hjp.at/zettel/m/memcached_flags.rxml
# Looks like most clients use bit 1 to indicate gzip compression.
FLAG_COMPRESSED = 0x2
def initialize(client_options)
# Support the deprecated compression option, but don't allow it to override
# an explicit compress
# Remove this with 4.0
if client_options.key?(:compression) && !client_options.key?(:compress)
Dalli.logger.warn "DEPRECATED: Dalli's :compression option is now just 'compress: true'. " \
'Please update your configuration.'
client_options[:compress] = client_options.delete(:compression)
end
@compression_options =
DEFAULTS.merge(client_options.select { |k, _| OPTIONS.include?(k) })
end
def store(value, req_options, bitflags)
do_compress = compress_value?(value, req_options)
store_value = do_compress ? compressor.compress(value) : value
bitflags |= FLAG_COMPRESSED if do_compress
[store_value, bitflags]
end
def retrieve(value, bitflags)
compressed = (bitflags & FLAG_COMPRESSED) != 0
compressed ? compressor.decompress(value) : value
# TODO: We likely want to move this rescue into the Dalli::Compressor / Dalli::GzipCompressor
# itself, since not all compressors necessarily use Zlib. For now keep it here, so the behavior
# of custom compressors doesn't change.
rescue Zlib::Error
raise UnmarshalError, "Unable to uncompress value: #{$ERROR_INFO.message}"
end
def compress_by_default?
@compression_options[:compress]
end
def compressor
@compression_options[:compressor]
end
def compression_min_size
@compression_options[:compression_min_size]
end
# Checks whether we should apply compression when serializing a value
# based on the specified options. Returns false unless the value
# is greater than the minimum compression size. Otherwise returns
# based on a method-level option if specified, falling back to the
# server default.
def compress_value?(value, req_options)
return false unless value.bytesize >= compression_min_size
return compress_by_default? unless req_options && !req_options[:compress].nil?
req_options[:compress]
end
end
end
end
|