Skip to content

Commit

Permalink
[multi-operations] specs for #delete_matched
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Sep 25, 2018
1 parent dd52004 commit f68bd36
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 19 deletions.
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ end

Log message format:

```
```shell
[AnyCache<CACHER_NAME>/Activity<OPERATION_NAME>]: performed <OPERATION NAME> operation with
params: INSPECTED_ARGUMENTS and options: INSPECTED_OPTIONS
```
Expand Down Expand Up @@ -349,10 +349,10 @@ any_cache.clear

### Fetch

- `AnyCache#fetch(key, [force:], [expires_in:], [&block])`
- `AnyCache#fetch(key, [force:], [expires_in:], [&fallback])`
- works in `ActiveSupport::Cache::Store#fetch`-manner;
- fetches data from the cache using the given key;
- if a block has been passed and data with the given key does not exist - that block
- if a `fallback` block has been passed and data with the given key does not exist - that block
will be called with the given key and the return value will be written to the cache;

```ruby
Expand All @@ -367,21 +367,30 @@ cache_store.fetch("data") # => "new_data"

# --- new entry with expiration time ---
cache_store.fetch("data") # => nil
cache_store.fetch("data", expires_in: 8) { |key| "new_data" } # => "new_data"
cache_store.fetch("data", expires_in: 8) { |key| "new_#{key}" } # => "new_data"
cache_store.fetch("data") # => "new_data"
# ...sleep 8 seconds...
cache_store.fetch("data") # => nil

# --- force update/rewrite ---
cache_store.fetch("data") # => "some_data"
cache_store.fetch("data", expires_in: 8, force: true) { |key| "new_data" } # => "new_data"
cache_store.fetch("data", expires_in: 8, force: true) { |key| "new_#{key}" } # => "new_data"
cache_store.fetch("data") # => "new_data"
# ...sleep 8 seconds...
cache_store.fetch("data") # => nil
```

---

### Fetch Multi

- `AnyCache#fetch_multy(*keys, [force:], [expires_in:], [&fallback])`

```ruby
```

---

### Read

- `AnyCache#read(key)` - get an entry value from the cache storage
Expand All @@ -396,6 +405,16 @@ cache_store.read("data") # => nil

---

### Read Multi

- `AnyCache#read_multi(*keys)`


```ruby
```

---

### Write

- `AnyCache#write(key, value, [expires_in:])` - write a new entry to the cache storage
Expand All @@ -410,6 +429,15 @@ cache_store.write("data", 123, expires_in: 60)

---

### Write Multi

- `AnyCache#write_multi(**entry_pairs)`

```ruby
```

---

### Delete

- `AnyCache#delete(key)` - remove entry from the cache storage
Expand All @@ -420,6 +448,15 @@ cache_store.delete("data")

---

### Delete Matched

- `AnyCache#delete_matched(pattern)`

```ruby
```

---

### Increment

- `AnyCache#increment(key, amount = 1, [expires_in:])` - increment entry's value by the given amount
Expand Down Expand Up @@ -547,6 +584,13 @@ bin/rspec --test-as-mem-cache-store # run specs with ActiveSupport::Cache::MemCa

---

## Roadmap

- instrumentation layer;
- `#delete_matched` for memcached-based cache storages;

---

## Contributing

- Fork it (https://github.com/0exp/any_cache/fork)
Expand Down
2 changes: 1 addition & 1 deletion lib/any_cache/adapters/active_support_naive_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def delete(key, **options)
lock.with_write_lock { super }
end

# @param pattern [Object]
# @param pattern [String, Regexp]
# @param options [Hash]
# @return [void]
#
Expand Down
2 changes: 1 addition & 1 deletion lib/any_cache/adapters/active_support_redis_cache_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def supported_driver?(driver)
READ_MULTI_EMPTY_KEYS_SET = [].freeze

# @since 0.1.0
def_delegators :driver, :delete, :clear, :delete_matched
def_delegators :driver, :delete, :delete_matched, :clear

# @return [NilClass]
#
Expand Down
4 changes: 2 additions & 2 deletions lib/any_cache/adapters/dalli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ def delete(key, **options)
driver.delete(key)
end

# @param pattern [Object]
# @param pattern [String, Regexp]
# @param options [Hash]
# @return [void]
#
# @api private
# @since 0.3.0
def delete_matched(pattern, **options)
# NOTE: unsupported
# TODO: make it real >:]
end

# @param key [String]
Expand Down
12 changes: 6 additions & 6 deletions lib/any_cache/adapters/redis.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def supported_driver?(driver)
#
# @api private
# @since 0.3.0
DELETE_MATCHED_CURSOR = "0"
DELETE_MATCHED_CURSOR_START = "0"

# @return [Integer]
#
Expand Down Expand Up @@ -151,20 +151,20 @@ def delete(key, **options)
# @api private
# @since 0.3.0
def delete_matched(pattern, **options)
cursor = DELETE_MATCHED_CURSOR
cursor = DELETE_MATCHED_CURSOR_START

case pattern
when String
loop do
cursor, keys = adapter.scan(cursor, match: pattern, count: DELETE_MATCHED_BATCH_SIZE)
cursor, keys = scan(cursor, match: pattern, count: DELETE_MATCHED_BATCH_SIZE)
del(keys)
break if cursor == DELETE_MATCHED_CURSOR
break if cursor == DELETE_MATCHED_CURSOR_START
end
when Regexp
loop do
cursor, keys = adapter.scan(cursor, count: DELETE_MATCHED_BATCH_SIZE)
cursor, keys = scan(cursor, count: DELETE_MATCHED_BATCH_SIZE)
del(keys.grep(pattern))
break if cursor == DELETE_MATCHED_CURSOR
break if cursor == DELETE_MATCHED_CURSOR_START
end
end
end
Expand Down
48 changes: 48 additions & 0 deletions spec/features/delete_matched_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,52 @@

describe 'Operation: #delete_matched' do
include_context 'cache store'

let(:entry_1) { { key: 'test_1', value: SecureRandom.hex(4) } }
let(:entry_2) { { key: '2_test', value: SecureRandom.hex(4) } }
let(:entry_3) { { key: 'no_key', value: SecureRandom.hex(4) } }

before do
cache_store.write_multi(
entry_1[:key] => entry_1[:value],
entry_2[:key] => entry_2[:value],
entry_3[:key] => entry_3[:value]
)
end

specify 'removes matched keys only', exclude: %i[dalli as_mem_cache_store as_redis_cache_store] do
expect(cache_store.read(entry_1[:key])).to eq(entry_1[:value])
expect(cache_store.read(entry_2[:key])).to eq(entry_2[:value])
expect(cache_store.read(entry_3[:key])).to eq(entry_3[:value])

cache_store.delete_matched(/\A.*test.*\z/i)

expect(cache_store.read(entry_1[:key])).to eq(nil)
expect(cache_store.read(entry_2[:key])).to eq(nil)
expect(cache_store.read(entry_3[:key])).to eq(entry_3[:value])

cache_store.delete_matched('no_key')

expect(cache_store.read(entry_1[:key])).to eq(nil)
expect(cache_store.read(entry_2[:key])).to eq(nil)
expect(cache_store.read(entry_3[:key])).to eq(nil)
end

specify 'removes matched keys only', only: %i[as_redis_cache_store redis redis_store] do
expect(cache_store.read(entry_1[:key])).to eq(entry_1[:value])
expect(cache_store.read(entry_2[:key])).to eq(entry_2[:value])
expect(cache_store.read(entry_3[:key])).to eq(entry_3[:value])

cache_store.delete_matched('test?1') # NOTE: redis KEYS glob pattners

expect(cache_store.read(entry_1[:key])).to eq(nil)
expect(cache_store.read(entry_2[:key])).to eq(entry_2[:value])
expect(cache_store.read(entry_3[:key])).to eq(entry_3[:value])

cache_store.delete_matched('*_*') # NOTE: redis KEYS glob patterns

expect(cache_store.read(entry_1[:key])).to eq(nil)
expect(cache_store.read(entry_2[:key])).to eq(nil)
expect(cache_store.read(entry_3[:key])).to eq(nil)
end
end
4 changes: 2 additions & 2 deletions spec/features/fetch_multi_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

describe 'Operation: #fetch_multi', :focus do
describe 'Operation: #fetch_multi' do
include_context 'cache store'

let(:entry_1) { { key: SecureRandom.hex, value: SecureRandom.hex(4) } }
Expand All @@ -9,7 +9,7 @@

let(:expires_in) { 8 } # NOTE: in seconds

specify do
specify 'returns a set of key-value pairs by the given key set' do
# NOTE: nonexisntent data
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => nil,
Expand Down
2 changes: 1 addition & 1 deletion spec/features/read_multi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

let(:expires_in) { 8 } # NOTE: in seconds

specify do
specify 'returns a set of key-value pairs by the given key set' do
expect(cache_store.read_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => nil, # NOTE: nonexistent entry
entry_2[:key] => nil, # NOTE: nonexistent entry
Expand Down
2 changes: 1 addition & 1 deletion spec/features/write_multi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
let(:entry_2) { { key: SecureRandom.hex, value: SecureRandom.hex(4) } }
let(:entry_3) { { key: SecureRandom.hex, value: SecureRandom.hex(4) } }

specify do
specify 'writes a set of permanent entries' do
expect(cache_store.read(entry_1[:key])).to eq(nil) # NOTE: nonexistent entry
expect(cache_store.read(entry_2[:key])).to eq(nil) # NOTE: nonexistent entry
expect(cache_store.read(entry_3[:key])).to eq(nil) # NOTE: nonexistent entry
Expand Down
28 changes: 28 additions & 0 deletions spec/support/spec_support/meta_scopes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,32 @@
config.around(:example, :as_memory_store) do |example|
example.call if test_as_memory_store_cache?
end

# rubocop:disable Metrics/LineLength
config.around(:example, :exclude) do |example|
next if test_redis_cache? && example.metadata[:exclude].include?(:redis)
next if test_redis_store_cache? && example.metadata[:exclude].include?(:redis_store)
next if test_dalli_cache? && example.metadata[:exclude].include?(:dalli)
next if test_as_redis_cache_store_cache? && example.metadata[:exclude].include?(:as_redis_cache_store)
next if test_as_mem_cache_store_cache? && example.metadata[:exclude].include?(:as_mem_cache_store)
next if test_as_file_store_cache? && example.metadata[:exclude].include?(:as_file_store)
next if test_as_memory_store_cache? && example.metadata[:exclude].include?(:as_memory_store)

example.call
end
# rubocop:enable Metrics/LineLength

# rubocop:disable Metrics/LineLength
config.around(:example, :only) do |example|
next if test_redis_cache? && !example.metadata[:only].include?(:redis)
next if test_redis_store_cache? && !example.metadata[:only].include?(:redis_store)
next if test_dalli_cache? && !example.metadata[:only].include?(:dalli)
next if test_as_redis_cache_store_cache? && !example.metadata[:only].include?(:as_redis_cache_store)
next if test_as_mem_cache_store_cache? && !example.metadata[:only].include?(:as_mem_cache_store)
next if test_as_file_store_cache? && !example.metadata[:only].include?(:as_file_store)
next if test_as_memory_store_cache? && !example.metadata[:only].include?(:as_memory_store)

example.call
end
# rubocop:enable Metrics/LineLength
end

0 comments on commit f68bd36

Please sign in to comment.