1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
require 'spec_helper'
describe Gitlab::Cache::RequestCache do
let(:klass) do
Class.new do
extend Gitlab::Cache::RequestCache
attr_accessor :id, :name, :result, :extra
def self.name
'ExpensiveAlgorithm'
end
def initialize(id, name, result, extra = nil)
self.id = id
self.name = name
self.result = result
self.extra = nil
end
request_cache def compute(arg)
result << arg
end
request_cache def repute(arg)
result << arg
end
def dispute(arg)
result << arg
end
request_cache(:dispute) { extra }
end
end
let(:algorithm) { klass.new('id', 'name', []) }
shared_examples 'cache for the same instance' do
it 'does not compute twice for the same argument' do
algorithm.compute(true)
result = algorithm.compute(true)
expect(result).to eq([true])
end
it 'computes twice for the different argument' do
algorithm.compute(true)
result = algorithm.compute(false)
expect(result).to eq([true, false])
end
it 'computes twice for the different class name' do
algorithm.compute(true)
allow(klass).to receive(:name).and_return('CheapAlgo')
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
it 'computes twice for the different method' do
algorithm.compute(true)
result = algorithm.repute(true)
expect(result).to eq([true, true])
end
context 'when request_cache_key is provided' do
before do
klass.request_cache_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])
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])
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])
end
end
end
context 'when RequestStore is active', :request_store do
it_behaves_like 'cache for the same instance'
it 'computes once for different instances when keys are the same' do
algorithm.compute(true)
result = klass.new('id', 'name', algorithm.result).compute(true)
expect(result).to eq([true])
end
it 'computes twice if RequestStore starts over' do
algorithm.compute(true)
RequestStore.end!
RequestStore.clear!
RequestStore.begin!
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
end
context 'when RequestStore is inactive' do
it_behaves_like 'cache for the same instance'
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])
end
end
end