Skip to content

Provide option to limit number of cached entries per key #121

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Provide option to limit number of cached entries per key
Related #112
  • Loading branch information
Jeff Wallace committed Oct 1, 2020
commit 6d2f119d769001c7486e776d4236c8be77a83316
5 changes: 3 additions & 2 deletions lib/faraday/http_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class HttpCache < Faraday::Middleware
# :instrumenter - An instrumentation object that should respond to 'instrument'.
# :instrument_name - The String name of the instrument being reported on (optional).
# :logger - A logger object.
# :max_entries - The maximum number of entries to store per cache key.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better name would be max_entries_per_(cache_)key?

#
# Examples:
#
Expand All @@ -99,14 +100,14 @@ class HttpCache < Faraday::Middleware
# # Initialize the middleware with a MemoryStore and logger
# store = ActiveSupport::Cache.lookup_store
# Faraday::HttpCache.new(app, store: store, logger: my_logger)
def initialize(app, store: nil, serializer: nil, shared_cache: true, instrumenter: nil, instrument_name: EVENT_NAME, logger: nil) # rubocop:disable Metrics/ParameterLists
def initialize(app, store: nil, serializer: nil, shared_cache: true, instrumenter: nil, instrument_name: EVENT_NAME, logger: nil, max_entries: nil) # rubocop:disable Metrics/ParameterLists
super(app)

@logger = logger
@shared_cache = shared_cache
@instrumenter = instrumenter
@instrument_name = instrument_name
@storage = Storage.new(store: store, serializer: serializer, logger: logger)
@storage = Storage.new(store: store, serializer: serializer, logger: logger, max_entries: max_entries)
end

# Public: Process the request into a duplicate of this instance to
Expand Down
16 changes: 10 additions & 6 deletions lib/faraday/http_cache/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ class Storage

# Internal: Initialize a new Storage object with a cache backend.
#
# :logger - A Logger object to be used to emit warnings.
# :store - An cache store object that should respond to 'read',
# 'write', and 'delete'.
# :serializer - A serializer object that should respond to 'dump'
# and 'load'.
def initialize(store: nil, serializer: nil, logger: nil)
# :logger - A Logger object to be used to emit warnings.
# :store - An cache store object that should respond to 'read',
# 'write', and 'delete'.
# :serializer - A serializer object that should respond to 'dump'
# and 'load'.
# :max_entries - The maximum number of entries per cache key.
def initialize(store: nil, serializer: nil, logger: nil, max_entries: nil)
@cache = store || MemoryStore.new
@serializer = serializer || JSON
@logger = logger
@max_entries = max_entries
assert_valid_store!
end

Expand All @@ -55,6 +57,8 @@ def write(request, response)

entries << entry

entries = entries.last(@max_entries) unless @max_entries.nil?

cache.write(key, entries)
rescue ::Encoding::UndefinedConversionError => e
warn "Response could not be serialized: #{e.message}. Try using Marshal to serialize."
Expand Down
28 changes: 26 additions & 2 deletions spec/storage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
let(:response) { double(serializable_hash: { response_headers: {} }) }

let(:cache) { Faraday::HttpCache::MemoryStore.new }
let(:serializer) { nil }
let(:max_entries) { nil }
let(:storage) { Faraday::HttpCache::Storage.new(store: cache, serializer: serializer, max_entries: max_entries) }

let(:storage) { Faraday::HttpCache::Storage.new(store: cache) }
subject { storage }

describe 'Cache configuration' do
Expand All @@ -37,10 +39,33 @@
expect(cache).to receive(:write).with(cache_key, [entry])
subject.write(request, response)
end

context 'with max_entries set' do
let(:max_entries) { 2 }
let(:response) { double(serializable_hash: { response_headers: { 'Vary' => 'X-Foo' } }) }

it 'limits the number of cached entries' do
requests = Array.new(max_entries + 1) do |i|
env = { method: :get, url: 'http://test/index', headers: { 'X-Foo' => i } }
double(env.merge(serializable_hash: env))
end

requests.each { |request| subject.write(request, response) }

expect(subject.cache.read(cache_key).count).to eq(max_entries)

expect(subject.read(requests.first)).to eq(nil)

requests.last(max_entries).each do |request|
expect(subject.read(request)).not_to be_nil
end
end
end
end

context 'with the JSON serializer' do
let(:serializer) { JSON }

it_behaves_like 'A storage with serialization'

context 'when ASCII characters in response cannot be converted to UTF-8' do
Expand All @@ -66,7 +91,6 @@
context 'with the Marshal serializer' do
let(:cache_key) { '337d1e9c6c92423dd1c48a23054139058f97be40' }
let(:serializer) { Marshal }
let(:storage) { Faraday::HttpCache::Storage.new(store: cache, serializer: Marshal) }

it_behaves_like 'A storage with serialization'
end
Expand Down