Skip to content

Commit

Permalink
Rails 7.0 support and drop older railses
Browse files Browse the repository at this point in the history
  • Loading branch information
byroot committed Jul 22, 2021
1 parent d74ed76 commit cdf5262
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 737 deletions.
16 changes: 3 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,10 @@ jobs:
strategy:
matrix:
entry:
- name: 'Minimum supported'
ruby: '2.6'
gemfile: "Gemfile.min"
- name: 'Latest released'
ruby: '3.0'
gemfile: "Gemfile"
- name: 'Rails edge'
ruby: '3.0'
gemfile: "Gemfile.edge"
- ruby: '2.7'
- ruby: '3.0'

name: ${{ matrix.entry.name }}

env:
BUNDLE_GEMFILE: ${{ matrix.entry.gemfile }}
name: "Ruby ${{ matrix.entry.ruby }}"

services:
memcached:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ source 'https://rubygems.org'

gemspec

gem "snappy"
gem "rails", github: "rails/rails", branch: "main"
7 changes: 0 additions & 7 deletions Gemfile.min

This file was deleted.

45 changes: 0 additions & 45 deletions lib/active_support/cache/memcached_snappy_store.rb

This file was deleted.

186 changes: 41 additions & 145 deletions lib/active_support/cache/memcached_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,23 @@ module Cache
class MemcachedStore < Store
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n

class Codec
# use dalli compatible flags
SERIALIZED_FLAG = 0x1
COMPRESSED_FLAG = 0x2

# Older versions of this gem would use 0 for the flags whether or not
# the value was marshal dumped. By setting this flag, we can tell if
# it were set with an older version for backwards compatible decoding.
RAW_FLAG = 0x10

def initialize(serializer: Marshal, compressor: nil)
@serializer = serializer
@compressor = compressor
end
attr_accessor :swallow_exceptions

def encode(_key, value, flags)
unless value.is_a?(String)
flags |= SERIALIZED_FLAG
value = @serializer.dump(value)
end
if @compressor
flags |= COMPRESSED_FLAG
value = @compressor.compress(value)
end
flags |= RAW_FLAG if flags == 0
module RawCodec
def self.encode(_key, value, flags)
[value, flags]
end

def decode(_key, value, flags)
if (flags & COMPRESSED_FLAG) != 0
value = @compressor.decompress(value)
end

if (flags & SERIALIZED_FLAG) != 0
@serializer.load(value)
elsif flags == 0 # legacy cache value
@serializer.load(value) rescue value
else
value
end
def self.decode(_key, value, _flags)
value
end
end

attr_accessor :read_only, :swallow_exceptions

prepend(Strategy::LocalCache)

def initialize(*addresses, **options)
addresses = addresses.flatten
options[:codec] ||= Codec.new
@swallow_exceptions = true
@swallow_exceptions = options.delete(:swallow_exceptions) if options.key?(:swallow_exceptions)
options[:codec] ||= RawCodec

super(options)

Expand All @@ -81,7 +46,6 @@ def initialize(*addresses, **options)
end

def append(name, value, options = nil)
return true if read_only
options = merged_options(options)
normalized_key = normalize_key(name, options)

Expand All @@ -93,16 +57,6 @@ def append(name, value, options = nil)
end
end

def write(*)
return true if read_only
super
end

def delete(*)
return true if read_only
super
end

def read_multi(*names)
options = names.extract_options!
return {} if names.empty?
Expand All @@ -115,7 +69,7 @@ def read_multi(*names)
instrument(:read_multi, names, options) do
if raw_values = @connection.get(keys_to_names.keys)
raw_values.each do |key, value|
entry = deserialize_entry(value)
entry = deserialize_entry(value, **options)
values[keys_to_names[key]] = entry.value unless entry.expired?
end
end
Expand All @@ -133,8 +87,7 @@ def cas(name, options = nil)
@connection.cas(key, expiration(options)) do |raw_value|
entry = deserialize_entry(raw_value)
value = yield entry.value
break true if read_only
serialize_entry(Entry.new(value, **options), options)
serialize_entry(Entry.new(value, **options), **options)
end
end
true
Expand All @@ -159,10 +112,8 @@ def cas_multi(*names, **options)

values = yield values

break true if read_only

serialized_values = values.map do |name, value|
[normalize_key(name, options), serialize_entry(Entry.new(value, **options), options)]
[normalize_key(name, options), serialize_entry(Entry.new(value, **options), **options)]
end

Hash[serialized_values]
Expand Down Expand Up @@ -214,113 +165,61 @@ def reset #:nodoc:

private

if private_method_defined?(:read_serialized_entry)
class LocalStore < Strategy::LocalCache::LocalStore
def write_entry(_key, entry)
if entry.is_a?(Entry)
entry.dup_value!
end
super
end

def fetch_entry(key)
entry = @data.fetch(key) do
new_entry = yield
if entry.is_a?(Entry)
new_entry.dup_value!
end
@data[key] = new_entry
end
entry = entry.dup

if entry.is_a?(Entry)
entry.dup_value!
end

entry
end
end

module LocalCacheDup
def with_local_cache
use_temporary_local_cache(LocalStore.new) { yield }
end
end
prepend LocalCacheDup

def read_entry(key, **options) # :nodoc:
deserialize_entry(read_serialized_entry(key, **options))
end

def read_serialized_entry(key, **)
handle_exceptions(return_value_on_error: nil) do
@connection.get(key)
end
end

def write_entry(key, entry, **options) # :nodoc:
return true if read_only
def read_entry(key, **options) # :nodoc:
deserialize_entry(read_serialized_entry(key, **options), **options)
end

write_serialized_entry(key, serialize_entry(entry, **options), **options)
def read_serialized_entry(key, **)
handle_exceptions(return_value_on_error: nil) do
@connection.get(key)
end
end

def write_serialized_entry(key, value, **options)
method = options && options[:unless_exist] ? :add : :set
expires_in = expiration(options)
handle_exceptions(return_value_on_error: false) do
@connection.send(method, key, value, expires_in)
true
end
end
else
def read_entry(key, _options) # :nodoc:
handle_exceptions(return_value_on_error: nil) do
deserialize_entry(@connection.get(key))
end
end
def write_entry(key, entry, **options) # :nodoc:
write_serialized_entry(key, serialize_entry(entry, **options), **options)
end

def write_entry(key, entry, options) # :nodoc:
return true if read_only
method = options && options[:unless_exist] ? :add : :set
expires_in = expiration(options)
value = serialize_entry(entry, options)
handle_exceptions(return_value_on_error: false) do
@connection.send(method, key, value, expires_in)
true
end
def write_serialized_entry(key, value, **options)
method = options && options[:unless_exist] ? :add : :set
expires_in = expiration(options)
handle_exceptions(return_value_on_error: false) do
@connection.send(method, key, value, expires_in)
true
end
end

def delete_entry(key, _options) # :nodoc:
return true if read_only
handle_exceptions(return_value_on_error: false, on_miss: true) do
@connection.delete(key)
true
end
end

private

def normalize_key(key, options)
key = super.dup
key = key.force_encoding(Encoding::ASCII_8BIT)
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
# When we remove support to Rails 5.1 we can change the code to use ActiveSupport::Digest
key = "#{key[0, 213]}:md5:#{::Digest::MD5.hexdigest(key)}" if key.size > 250
key
end

def deserialize_entry(value)
unless value.nil?
value.is_a?(Entry) ? value : Entry.new(value, compress: false)
def deserialize_entry(payload, raw: false, **)
if !payload.nil? && raw
if payload.is_a?(Entry)
payload
else
Entry.new(payload, compress: false)
end
else
super(payload)
end
end

def serialize_entry(entry, options)
if options[:raw]
def serialize_entry(entry, raw: false, **)
if raw
entry.value.to_s
else
entry
super(entry)
end
end

Expand All @@ -336,19 +235,16 @@ def handle_exceptions(return_value_on_error:, on_miss: return_value_on_error, mi
yield
rescue Memcached::NotFound, Memcached::ConnectionDataExists, *miss_exceptions
on_miss
rescue Memcached::NotStored
return_value_on_error
rescue Memcached::Error => e
log_warning(e)
raise unless @swallow_exceptions
return_value_on_error
end

def log_warning(err)
return unless logger
return if err.is_a?(Memcached::NotStored) && @swallow_exceptions

logger.warn(
"[MEMCACHED_ERROR] swallowed=#{@swallow_exceptions}" \
" exception_class=#{err.class} exception_message=#{err.message}"
logger&.warn(
"[MEMCACHED_ERROR] exception_class=#{err.class} exception_message=#{err.message}"
)
end

Expand Down
4 changes: 2 additions & 2 deletions memcached_store.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
spec.files = Dir["lib/**/*.rb", "README.md", "LICENSE"]
spec.require_paths = ["lib"]

spec.required_ruby_version = ">= 2.6.0"
spec.required_ruby_version = ">= 2.7.0"

spec.add_runtime_dependency "activesupport", ">= 6"
spec.add_runtime_dependency "activesupport", ">= 7.0.0.alpha"
spec.add_runtime_dependency "memcached", "~> 1.8"

spec.add_development_dependency "rake"
Expand Down
Loading

0 comments on commit cdf5262

Please sign in to comment.