Skip to content

Commit

Permalink
Merge pull request #127 from rails/expiry-queue
Browse files Browse the repository at this point in the history
Expire records on a custom queue
  • Loading branch information
djmb authored Jan 9, 2024
2 parents bca012a + 7f344fc commit ae9d224
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Solid Cache supports these options in addition to the standard `ActiveSupport::C
- `error_handler` - a Proc to call to handle any `ActiveRecord::ActiveRecordError`s that are raises (default: log errors as warnings)
- `expiry_batch_size` - the batch size to use when deleting old records (default: `100`)
- `expiry_method` - what expiry method to use `thread` or `job` (default: `thread`)
- `expiry_queue` - which queue to add expiry jobs to (default: `default`)
- `max_age` - the maximum age of entries in the cache (default: `2.weeks.to_i`). Can be set to `nil`, but this is not recommended unless using `max_entries` to limit the size of the cache.
- `max_entries` - the maximum number of entries allowed in the cache (default: `nil`, meaning no limit)
- `cluster` - a Hash of options for the cache database cluster, e.g `{ shards: [:database1, :database2, :database3] }`
Expand Down
7 changes: 5 additions & 2 deletions lib/solid_cache/cluster/expiry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ module Expiry
# This ensures there is downward pressure on the cache size while there is valid data to delete
EXPIRY_MULTIPLIER = 1.25

attr_reader :expiry_batch_size, :expiry_method, :expire_every, :max_age, :max_entries
attr_reader :expiry_batch_size, :expiry_method, :expiry_queue, :expire_every, :max_age, :max_entries

def initialize(options = {})
super(options)
@expiry_batch_size = options.fetch(:expiry_batch_size, 100)
@expiry_method = options.fetch(:expiry_method, :thread)
@expiry_queue = options.fetch(:expiry_queue, :default)
@expire_every = [ (expiry_batch_size / EXPIRY_MULTIPLIER).floor, 1 ].max
@max_age = options.fetch(:max_age, 2.weeks.to_i)
@max_entries = options.fetch(:max_entries, nil)
Expand All @@ -29,7 +30,9 @@ def track_writes(count)
private
def expire_later
if expiry_method == :job
ExpiryJob.perform_later(expiry_batch_size, shard: Entry.current_shard, max_age: max_age, max_entries: max_entries)
ExpiryJob
.set(queue: expiry_queue)
.perform_later(expiry_batch_size, shard: Entry.current_shard, max_age: max_age, max_entries: max_entries)
else
async { Entry.expire(expiry_batch_size, max_age: max_age, max_entries: max_entries) }
end
Expand Down
28 changes: 22 additions & 6 deletions test/unit/expiry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,26 @@ class SolidCache::ExpiryTest < ActiveSupport::TestCase
end
end

private
def shard_keys(cache, shard)
namespaced_keys = 100.times.map { |i| @cache.send(:normalize_key, "key#{i}", {}) }
shard_keys = cache.primary_cluster.send(:connections).assign(namespaced_keys)[shard]
shard_keys.map { |key| key.delete_prefix("#{@namespace}:") }
end
test "expires old records with a custom queue" do
@cache = lookup_store(expiry_batch_size: 3, max_entries: 2, expiry_method: :job, expiry_queue: :cache_expiry)

default_shard_keys = shard_keys(@cache, :default)

assert_enqueued_jobs(2, only: SolidCache::ExpiryJob, queue: :cache_expiry) do
@cache.write(default_shard_keys[0], 1)
@cache.write(default_shard_keys[1], 2)
@cache.write(default_shard_keys[2], 3)
@cache.write(default_shard_keys[2], 4)
end

perform_enqueued_jobs
assert_equal 0, SolidCache.each_shard.sum { SolidCache::Entry.count }
end

private
def shard_keys(cache, shard)
namespaced_keys = 100.times.map { |i| @cache.send(:normalize_key, "key#{i}", {}) }
shard_keys = cache.primary_cluster.send(:connections).assign(namespaced_keys)[shard]
shard_keys.map { |key| key.delete_prefix("#{@namespace}:") }
end
end

0 comments on commit ae9d224

Please sign in to comment.