Skip to content

Commit

Permalink
Allow clear with delete or truncate
Browse files Browse the repository at this point in the history
Defaults to `truncate`, but can be set with the `clear_with` option.

If `Rails.test.env?` is true though it will default to `delete` as
truncation is not always transactional (e.g. in MySQL) so clearing the
cache would break transactional tests.
  • Loading branch information
djmb committed Sep 26, 2023
1 parent 66955fa commit b47ef93
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ Solid cache supports these options in addition to the universal `ActiveSupport::
- `trim_batch_size` - the batch size to use when deleting old records (default: `100`)
- `max_age` - the maximum age of entries in the cache (default: `2.weeks.to_i`)
- `max_entries` - the maximum number of entries allowed in the cache (default: `2.weeks.to_i`)
- `cluster` - a Hash of options for the cache database cluster, e.g `{ shards: [:database1, :database2, :database3] }``
- `cluster` - a Hash of options for the cache database cluster, e.g `{ shards: [:database1, :database2, :database3] }`
- `clusters` - and Array of Hashes for multiple cache clusters (ignored if `:cluster` is set)
- `active_record_instrumentation` - whether to instrument the cache's queries (default: `true`)
- `clear_with` - clear the cache with `:truncate` or `:delete` (default `truncate`, except for when Rails.env.test? then `delete`)

For more information on cache clusters see [Sharding the cache](#sharding-the-cache)
### Cache trimming
Expand Down
6 changes: 5 additions & 1 deletion app/models/solid_cache/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ def delete_matched(matcher, batch_size:)
end
end

def clear
def clear_truncate
connection.truncate(table_name)
end

def clear_delete
in_batches.delete_all
end

def increment(key, amount)
transaction do
uncached do
Expand Down
19 changes: 18 additions & 1 deletion lib/solid_cache/store/entries.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
module SolidCache
class Store
module Entries
attr_reader :clear_with

def initialize(options = {})
super(options)

# Truncating in test mode breaks transactional tests in MySQL (not in Postgres though)
@clear_with = options.fetch(:clear_with) { Rails.env.test? ? :delete : :truncate }&.to_sym

unless [:truncate, :delete].include?(clear_with)
raise ArgumentError, "`clear_with` must be either ``:truncate`` or ``:delete`"
end
end

private
def entry_delete_matched(matcher, batch_size)
writing_all(failsafe: :delete_matched) do
Expand All @@ -10,7 +23,11 @@ def entry_delete_matched(matcher, batch_size)

def entry_clear
writing_all(failsafe: :clear) do
Entry.clear
if clear_with == :truncate
Entry.clear_truncate
else
Entry.clear_delete
end
end
end

Expand Down
19 changes: 19 additions & 0 deletions test/models/solid_cache/entry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,24 @@ class EntryTest < ActiveSupport::TestCase

assert_equal 2, Entry.uncached { Entry.id_range }
end

test "clear with truncate" do
write_entries
assert_equal 20, uncached_entry_count
Entry.clear_truncate
assert_equal 0, uncached_entry_count
end

test "clear with delete" do
write_entries
assert_equal 20, uncached_entry_count
Entry.clear_delete
assert_equal 0, uncached_entry_count
end

private
def write_entries(count = 20)
Entry.write_multi(count.times.map { |i| { key: "key#{i}", value: "value#{i}" } })
end
end
end
4 changes: 4 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ def send_entries_back_in_time(distance)
end
end
end

def uncached_entry_count
SolidCache.each_shard.sum { SolidCache::Entry.uncached { SolidCache::Entry.count } }
end
34 changes: 34 additions & 0 deletions test/unit/clear_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require "test_helper"

class DeleteMatchedTest < ActiveSupport::TestCase
setup do
@namespace = "test-#{SecureRandom.hex}"

@delete_cache = lookup_store(expires_in: 60, clear_with: :delete)
end

test "clear by truncation" do
cache = lookup_store(expires_in: 60, clear_with: :truncate)
write_values(cache)

cache.clear

assert_equal cache.clear_with, :truncate
assert_equal 0, uncached_entry_count
end

test "clear by deletion" do
cache = lookup_store(expires_in: 60, clear_with: :delete)
write_values(cache)

cache.clear

assert_equal cache.clear_with, :delete
assert_equal 0, uncached_entry_count
end

private
def write_values(cache)
20.times.map { |i| cache.write("key#{i}", "value#{i}") }
end
end

0 comments on commit b47ef93

Please sign in to comment.