summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/cache/request_cache_spec.rb
blob: 62f914cf121db99234ffc40037ab1aaefc83a0db (plain)
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
134
135
136
137
138
139
140
141
require 'spec_helper'

describe Gitlab::Cache::RequestCache, :request_store 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_store_wrap def compute(arg)
        result << arg
      end

      request_store_wrap def repute(arg)
        result << arg
      end

      def dispute(arg)
        result << arg
      end
      request_store_wrap(:dispute) { extra }
    end
  end

  let(:algorithm) { klass.new('id', 'name', []) }

  context 'when RequestStore is active' do
    it 'does not compute twice for the same argument' do
      result = algorithm.compute(true)

      expect(result).to eq([true])
      expect(algorithm.compute(true)).to eq(result)
      expect(algorithm.result).to eq(result)
    end

    it 'computes twice for the different argument' do
      algorithm.compute(true)
      result = algorithm.compute(false)

      expect(result).to eq([true, false])
      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')
      result = algorithm.compute(true)

      expect(result).to eq([true, true])
      expect(algorithm.result).to eq(result)
    end

    it 'computes twice for the different method' do
      algorithm.compute(true)
      result = algorithm.repute(true)

      expect(result).to eq([true, true])
      expect(algorithm.result).to eq(result)
    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])
      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
    before do
      RequestStore.end!
    end

    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
  end
end