Commit 465f82e3 authored by Michael Kozono's avatar Michael Kozono

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

Allow reactive caching to be used in services

See merge request gitlab-org/gitlab-ce!26839
parents a2d044bf 5caced36
...@@ -29,6 +29,40 @@ ...@@ -29,6 +29,40 @@
# However, it will enqueue a background worker to call `#calculate_reactive_cache` # However, it will enqueue a background worker to call `#calculate_reactive_cache`
# and set an initial cache lifetime of ten minutes. # 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 # Each time the background job completes, it stores the return value of
# `#calculate_reactive_cache`. It is also re-enqueued to run again after # `#calculate_reactive_cache`. It is also re-enqueued to run again after
# `reactive_cache_refresh_interval`, so keeping the stored value up to date. # `reactive_cache_refresh_interval`, so keeping the stored value up to date.
...@@ -52,6 +86,7 @@ module ReactiveCaching ...@@ -52,6 +86,7 @@ module ReactiveCaching
class_attribute :reactive_cache_key class_attribute :reactive_cache_key
class_attribute :reactive_cache_lifetime class_attribute :reactive_cache_lifetime
class_attribute :reactive_cache_refresh_interval class_attribute :reactive_cache_refresh_interval
class_attribute :reactive_cache_worker_finder
# defaults # defaults
self.reactive_cache_lease_timeout = 2.minutes self.reactive_cache_lease_timeout = 2.minutes
...@@ -59,6 +94,10 @@ module ReactiveCaching ...@@ -59,6 +94,10 @@ module ReactiveCaching
self.reactive_cache_refresh_interval = 1.minute self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime = 10.minutes 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) def calculate_reactive_cache(*args)
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
class ReactiveCachingWorker class ReactiveCachingWorker
include ApplicationWorker include ApplicationWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(class_name, id, *args) def perform(class_name, id, *args)
klass = begin klass = begin
class_name.constantize class_name.constantize
...@@ -12,7 +11,9 @@ class ReactiveCachingWorker ...@@ -12,7 +11,9 @@ class ReactiveCachingWorker
end end
return unless klass 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 end
# rubocop: enable CodeReuse/ActiveRecord
end end
---
title: Allow reactive caching to be used in services
merge_request: 26839
author:
type: added
...@@ -16,6 +16,10 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do ...@@ -16,6 +16,10 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
attr_reader :id attr_reader :id
def self.primary_key
:id
end
def initialize(id, &blk) def initialize(id, &blk)
@id = id @id = id
@calculator = blk @calculator = blk
...@@ -106,6 +110,46 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do ...@@ -106,6 +110,46 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end end
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 describe '#clear_reactive_cache!' do
before do before do
stub_reactive_cache(instance, 4) stub_reactive_cache(instance, 4)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment