Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ActiveSupport::Cache::DalliStore] #fetch patch #16

Merged
merged 3 commits into from
Oct 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.3.1] - 2018-10-08
### Added
- patch interface `AnyCache.enable_patch!(:patch_series_name)`:
- `ActiveSupport::Cache::DalliStore` patch: now the `#fetch` method provides
a cache key attribute to the fallback proc (can be enabled via `.enable(:dalli_store)`);

## [0.3.0] - 2018-10-06
### Added
- support for `ActiveSupport::Cache::DalliStore` client;
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ require 'any_cache'
- [Persist](#persist)
- [Existence](#existence)
- [Clear](#clear)
- [Roadmap](#roadmap)

---

Expand Down Expand Up @@ -195,6 +196,8 @@ require 'dalli'
require 'active_support'
require 'any_cache'

AnyCache.enable_patch!(:dalli_store)

AnyCache.configure do |conf|
conf.driver = :as_dalli_store
conf.as_dalli_store.servers = ... # string or array of strings
Expand Down
4 changes: 3 additions & 1 deletion lib/any_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class AnyCache
require_relative 'any_cache/adapters'
require_relative 'any_cache/logging'
require_relative 'any_cache/delegation'
require_relative 'any_cache/patches'

# @since 0.2.0
include Qonfig::Configurable

# @since 0.3.0
include Delegation
# @since 0.3.1
extend Patches::InterfaceAccessMixin

# @since 0.2.0
# rubocop:disable Metrics/BlockLength
Expand Down
3 changes: 3 additions & 0 deletions lib/any_cache/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ class AnyCache

# @since 0.1.0
UnsupportedDriverError = Class.new(Error)

# @since 0.3.1
NonexistentPatchError = Class.new(ArgumentError)
end
41 changes: 41 additions & 0 deletions lib/any_cache/patches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

# @api private
# @since 0.3.1
module AnyCache::Patches
# @since 0.3.1
require_relative 'patches/dalli_store'

class << self
# @param patch_series [Symbol, String]
# @return [void]
#
# @raise [AnyCache::NonexistentPatchError]
#
# @api private
# @since 0.3.1
def enable!(patch_series)
case patch_series
when :dalli_store, 'dalli_store'
AnyCache::Patches::DalliStore.enable!
else
raise AnyCache::NonexistentPatchError, "Can't enable nonexistnet patch! (#{patch_series})"
end
end
end

# @api private
# @since 0.3.1
module InterfaceAccessMixin
# @param patch_series [Symbol, String]
# @return [void]
#
# @see AnyCache::Patches#enable!
#
# @api private
# @since 0.3.1
def enable_patch!(patch_series)
AnyCache::Patches.enable!(patch_series)
end
end
end
61 changes: 61 additions & 0 deletions lib/any_cache/patches/dalli_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

# @api private
# @since 0.3.1
module AnyCache::Patches::DalliStore
class << self
# @return [void]
#
# @api private
# @since 0.3.1
def enable!
defined?(::Dalli) &&
defined?(::ActiveSupport::Cache::DalliStore) &&
Gem::Version.new(::Dalli::VERSION) <= Gem::Version.new('2.7.8') &&
::ActiveSupport::Cache::DalliStore.prepend(ActiveSupportFetchWithKey)
end
end

# @api private
# @since 0.3.1
module ActiveSupportFetchWithKey
# NOTE: original fetch implementation with my own little fix (see #43 line of code below)
# rubocop:disable all
def fetch(name, options=nil)
options ||= {}
options[:cache_nils] = true if @options[:cache_nils]
namespaced_name = namespaced_key(name, options)
not_found = options[:cache_nils] ? Dalli::Server::NOT_FOUND : nil
if block_given?
entry = not_found
unless options[:force]
entry = instrument_with_log(:read, namespaced_name, options) do |payload|
read_entry(namespaced_name, options).tap do |result|
if payload
payload[:super_operation] = :fetch
payload[:hit] = not_found != result
end
end
end
end

if not_found == entry
result = instrument_with_log(:generate, namespaced_name, options) do |payload|
# FIX: https://github.com/petergoldstein/dalli/pull/701
yield(name)
# FIX: https://github.com/petergoldstein/dalli/pull/701
end
write(name, result, options)
result
else
instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
entry
end
else
read(name, options)
end
end
# rubocop:enable all
end
end

3 changes: 3 additions & 0 deletions lib/any_cache/version.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

class AnyCache
# @return [String]
#
# @api public
# @since 0.1.0
VERSION = '0.3.0'
end
46 changes: 12 additions & 34 deletions spec/features/fetch_multi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,12 @@
"#{key}-#{data_stub}"
end

unless test_as_dalli_store_cache? # TODO: remove this condition in future
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end

if test_as_dalli_store_cache? # TODO: remove this block of code in future
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)

# NOTE: force rewrite
data_stub = SecureRandom.hex(4)
Expand All @@ -73,23 +62,12 @@
"#{key}-#{data_stub}"
end

unless test_as_dalli_store_cache? # TODO: remove this condition in future
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "#{entry_1[:key]}-#{data_stub}",
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end

if test_as_dalli_store_cache? # TODO: remove this block of code in future
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "-#{data_stub}",
entry_2[:key] => "-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "#{entry_1[:key]}-#{data_stub}",
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)

sleep(expires_in + 1)

Expand Down
13 changes: 13 additions & 0 deletions spec/features/patches_interface_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

describe 'Patches interface' do
describe 'patch activation' do
it 'fails when the required patch does not exist' do
random_nonexistent_patch_name = SecureRandom.hex(rand(1..4)).to_sym

expect do
AnyCache.enable_patch!(random_nonexistent_patch_name)
end.to raise_error(AnyCache::NonexistentPatchError)
end
end
end
5 changes: 5 additions & 0 deletions spec/support/spec_support/cache/active_support_dalli_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class CacheStore < AnyCache
class << self
def build
load_dependencies!
enable_patches!
build_cache_store
end

Expand All @@ -24,6 +25,10 @@ def load_dependencies!
require 'active_support/cache/dalli_store'
end

def enable_patches!
AnyCache.enable_patch!(:dalli_store)
end

def build_cache_store
CacheStore.build
end
Expand Down