Use RequestStoreWrap for Commit#author
We also try to use instance variable to cache the result if RequestStore is not available, so we could keep the same logic, using the same cache key. Also introduce a way to specify method specific cache key
This commit is contained in:
parent
143fc48aba
commit
aada5273fa
|
@ -1,5 +1,6 @@
|
|||
class Commit
|
||||
extend ActiveModel::Naming
|
||||
extend Gitlab::Cache::RequestStoreWrap
|
||||
|
||||
include ActiveModel::Conversion
|
||||
include Noteable
|
||||
|
@ -169,19 +170,9 @@ class Commit
|
|||
end
|
||||
|
||||
def author
|
||||
if RequestStore.active?
|
||||
key = "commit_author:#{author_email.downcase}"
|
||||
# nil is a valid value since no author may exist in the system
|
||||
if RequestStore.store.key?(key)
|
||||
@author = RequestStore.store[key]
|
||||
else
|
||||
@author = find_author_by_any_email
|
||||
RequestStore.store[key] = @author
|
||||
end
|
||||
else
|
||||
@author ||= find_author_by_any_email
|
||||
end
|
||||
User.find_by_any_email(author_email.downcase)
|
||||
end
|
||||
request_store_wrap(:author) { author_email.downcase }
|
||||
|
||||
def committer
|
||||
@committer ||= User.find_by_any_email(committer_email.downcase)
|
||||
|
@ -368,10 +359,6 @@ class Commit
|
|||
end
|
||||
end
|
||||
|
||||
def find_author_by_any_email
|
||||
User.find_by_any_email(author_email.downcase)
|
||||
end
|
||||
|
||||
def repo_changes
|
||||
changes = { added: [], modified: [], removed: [] }
|
||||
|
||||
|
|
|
@ -37,23 +37,45 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def request_store_wrap(method_name)
|
||||
const_get(:RequestStoreWrapExtension)
|
||||
.send(:define_method, method_name) do |*args|
|
||||
return super(*args) unless RequestStore.active?
|
||||
def request_store_wrap(method_name, &method_key_block)
|
||||
const_get(:RequestStoreWrapExtension).module_eval do
|
||||
define_method(method_name) do |*args|
|
||||
store =
|
||||
if RequestStore.active?
|
||||
RequestStore.store
|
||||
else
|
||||
ivar_name = # ! and ? cannot be used as ivar name
|
||||
"@#{method_name.to_s.tr('!', "\u2605").tr('?', "\u2606")}"
|
||||
|
||||
klass = self.class
|
||||
key = [klass.name,
|
||||
method_name,
|
||||
*instance_exec(&klass.request_store_wrap_key),
|
||||
*args].join(':')
|
||||
instance_variable_get(ivar_name) ||
|
||||
instance_variable_set(ivar_name, {})
|
||||
end
|
||||
|
||||
if RequestStore.store.key?(key)
|
||||
RequestStore.store[key]
|
||||
key = send("#{method_name}_cache_key", args)
|
||||
|
||||
if store.key?(key)
|
||||
store[key]
|
||||
else
|
||||
RequestStore.store[key] = super(*args)
|
||||
store[key] = super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
cache_key_method_name = "#{method_name}_cache_key"
|
||||
|
||||
define_method(cache_key_method_name) do |args|
|
||||
klass = self.class
|
||||
|
||||
instance_key = instance_exec(&klass.request_store_wrap_key) if
|
||||
klass.request_store_wrap_key
|
||||
|
||||
method_key = instance_exec(&method_key_block) if method_key_block
|
||||
|
||||
[klass.name, method_name, *instance_key, *method_key, *args]
|
||||
.join(':')
|
||||
end
|
||||
|
||||
private cache_key_method_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,20 +5,17 @@ describe Gitlab::Cache::RequestStoreWrap, :request_store do
|
|||
Class.new do
|
||||
extend Gitlab::Cache::RequestStoreWrap
|
||||
|
||||
attr_accessor :id, :name, :result
|
||||
attr_accessor :id, :name, :result, :extra
|
||||
|
||||
def self.name
|
||||
'ExpensiveAlgorithm'
|
||||
end
|
||||
|
||||
def initialize(id, name, result)
|
||||
def initialize(id, name, result, extra = nil)
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.result = result
|
||||
end
|
||||
|
||||
request_store_wrap_key do
|
||||
[id, name]
|
||||
self.extra = nil
|
||||
end
|
||||
|
||||
request_store_wrap def compute(arg)
|
||||
|
@ -28,6 +25,11 @@ describe Gitlab::Cache::RequestStoreWrap, :request_store do
|
|||
request_store_wrap def repute(arg)
|
||||
result << arg
|
||||
end
|
||||
|
||||
def dispute(arg)
|
||||
result << arg
|
||||
end
|
||||
request_store_wrap(:dispute) { extra }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -50,24 +52,6 @@ describe Gitlab::Cache::RequestStoreWrap, :request_store do
|
|||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'computes twice for the different keys, id' do
|
||||
algorithm.compute(true)
|
||||
algorithm.id = 'ad'
|
||||
result = algorithm.compute(true)
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'computes twice for the different keys, name' do
|
||||
algorithm.compute(true)
|
||||
algorithm.name = 'same'
|
||||
result = algorithm.compute(true)
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'computes twice for the different class name' do
|
||||
algorithm.compute(true)
|
||||
allow(klass).to receive(:name).and_return('CheapAlgo')
|
||||
|
@ -95,6 +79,42 @@ describe Gitlab::Cache::RequestStoreWrap, :request_store do
|
|||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
context 'when request_store_wrap_key is provided' do
|
||||
before do
|
||||
klass.request_store_wrap_key do
|
||||
[id, name]
|
||||
end
|
||||
end
|
||||
|
||||
it 'computes twice for the different keys, id' do
|
||||
algorithm.compute(true)
|
||||
algorithm.id = 'ad'
|
||||
result = algorithm.compute(true)
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'computes twice for the different keys, name' do
|
||||
algorithm.compute(true)
|
||||
algorithm.name = 'same'
|
||||
result = algorithm.compute(true)
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'uses extra method cache key if provided' do
|
||||
algorithm.dispute(true) # miss
|
||||
algorithm.extra = true
|
||||
algorithm.dispute(true) # miss
|
||||
result = algorithm.dispute(true) # hit
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when RequestStore is inactive' do
|
||||
|
@ -102,10 +122,18 @@ describe Gitlab::Cache::RequestStoreWrap, :request_store do
|
|||
RequestStore.end!
|
||||
end
|
||||
|
||||
it 'computes twice even if everything is the same' do
|
||||
it 'computes only once if it is the same instance for the same key' do
|
||||
algorithm.compute(true)
|
||||
result = algorithm.compute(true)
|
||||
|
||||
expect(result).to eq([true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
||||
it 'computes twice for different instances even if keys are the same' do
|
||||
algorithm.compute(true)
|
||||
result = klass.new('id', 'name', algorithm.result).compute(true)
|
||||
|
||||
expect(result).to eq([true, true])
|
||||
expect(algorithm.result).to eq(result)
|
||||
end
|
||||
|
|
|
@ -19,17 +19,15 @@ describe Commit, models: true do
|
|||
expect(commit.author).to eq(user)
|
||||
end
|
||||
|
||||
it 'caches the author' do
|
||||
allow(RequestStore).to receive(:active?).and_return(true)
|
||||
it 'caches the author', :request_store do
|
||||
user = create(:user, email: commit.author_email)
|
||||
expect_any_instance_of(Commit).to receive(:find_author_by_any_email).and_call_original
|
||||
expect(User).to receive(:find_by_any_email).and_call_original
|
||||
|
||||
expect(commit.author).to eq(user)
|
||||
key = "commit_author:#{commit.author_email}"
|
||||
key = "Commit:author:#{commit.author_email.downcase}"
|
||||
expect(RequestStore.store[key]).to eq(user)
|
||||
|
||||
expect(commit.author).to eq(user)
|
||||
RequestStore.store.clear
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue