Merge branch '58375-reactive-caching-changes' into 'master'

Allow reactive caching to be used in services

See merge request gitlab-org/gitlab-ce!26839
This commit is contained in:
Michael Kozono 2019-04-04 17:27:30 +00:00
commit 465f82e32c
4 changed files with 92 additions and 3 deletions

View file

@ -29,6 +29,40 @@
# However, it will enqueue a background worker to call `#calculate_reactive_cache`
# and set an initial cache lifetime of ten minutes.
#
# The background worker needs to find or generate the object on which
# `with_reactive_cache` was called.
# The default behaviour can be overridden by defining a custom
# `reactive_cache_worker_finder`.
# Otherwise the background worker will use the class name and primary key to get
# the object using the ActiveRecord find_by method.
#
# class Bar
# include ReactiveCaching
#
# self.reactive_cache_key = ->() { ["bar", "thing"] }
# self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
#
# def self.from_cache(var1, var2)
# # This method will be called by the background worker with "bar1" and
# # "bar2" as arguments.
# new(var1, var2)
# end
#
# def initialize(var1, var2)
# # ...
# end
#
# def calculate_reactive_cache
# # Expensive operation here. The return value of this method is cached
# end
#
# def result
# with_reactive_cache("bar1", "bar2") do |data|
# # ...
# end
# end
# end
#
# Each time the background job completes, it stores the return value of
# `#calculate_reactive_cache`. It is also re-enqueued to run again after
# `reactive_cache_refresh_interval`, so keeping the stored value up to date.
@ -52,6 +86,7 @@ module ReactiveCaching
class_attribute :reactive_cache_key
class_attribute :reactive_cache_lifetime
class_attribute :reactive_cache_refresh_interval
class_attribute :reactive_cache_worker_finder
# defaults
self.reactive_cache_lease_timeout = 2.minutes
@ -59,6 +94,10 @@ module ReactiveCaching
self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime = 10.minutes
self.reactive_cache_worker_finder = ->(id, *_args) do
find_by(primary_key => id)
end
def calculate_reactive_cache(*args)
raise NotImplementedError
end

View file

@ -3,7 +3,6 @@
class ReactiveCachingWorker
include ApplicationWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(class_name, id, *args)
klass = begin
class_name.constantize
@ -12,7 +11,9 @@ class ReactiveCachingWorker
end
return unless klass
klass.find_by(klass.primary_key => id).try(:exclusively_update_reactive_cache!, *args)
klass
.reactive_cache_worker_finder
.call(id, *args)
.try(:exclusively_update_reactive_cache!, *args)
end
# rubocop: enable CodeReuse/ActiveRecord
end

View file

@ -0,0 +1,5 @@
---
title: Allow reactive caching to be used in services
merge_request: 26839
author:
type: added

View file

@ -16,6 +16,10 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
attr_reader :id
def self.primary_key
:id
end
def initialize(id, &blk)
@id = id
@calculator = blk
@ -106,6 +110,46 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
end
describe '.reactive_cache_worker_finder' do
context 'with default reactive_cache_worker_finder' do
let(:args) { %w(other args) }
before do
allow(instance.class).to receive(:find_by).with(id: instance.id)
.and_return(instance)
end
it 'calls the activerecord find_by method' do
result = instance.class.reactive_cache_worker_finder.call(instance.id, *args)
expect(result).to eq(instance)
expect(instance.class).to have_received(:find_by).with(id: instance.id)
end
end
context 'with custom reactive_cache_worker_finder' do
let(:args) { %w(arg1 arg2) }
let(:instance) { CustomFinderCacheTest.new(666, &calculation) }
class CustomFinderCacheTest < CacheTest
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
def self.from_cache(*args); end
end
before do
allow(instance.class).to receive(:from_cache).with(*args).and_return(instance)
end
it 'overrides the default reactive_cache_worker_finder' do
result = instance.class.reactive_cache_worker_finder.call(instance.id, *args)
expect(result).to eq(instance)
expect(instance.class).to have_received(:from_cache).with(*args)
end
end
end
describe '#clear_reactive_cache!' do
before do
stub_reactive_cache(instance, 4)