summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/database/count_spec.rb
blob: 1e1749b9b14035d8e733614b6af934295511f87e (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
require 'spec_helper'

describe Gitlab::Database::Count do
  before do
    create_list(:project, 3)
    create(:identity)
  end

  let(:models) { [Project, Identity] }
  let(:reltuples_strategy) { double('reltuples_strategy', count: {}) }
  let(:exact_strategy) { double('exact_strategy', count: {}) }

  before do
    allow(Gitlab::Database::Count::ReltuplesCountStrategy).to receive(:new).with(models).and_return(reltuples_strategy)
  end

  context '.approximate_counts' do
    context 'selecting strategies' do
      let(:strategies) { [double('s1', :enabled? => true), double('s2', :enabled? => false)] }

      it 'uses only enabled strategies' do
        expect(strategies[0]).to receive(:new).and_return(double('strategy1', count: {}))
        expect(strategies[1]).not_to receive(:new)

        described_class.approximate_counts(models, strategies: strategies)
      end
    end

    context 'fallbacks' do
    end

    context 'with PostgreSQL', :postgresql do
      describe 'when reltuples have not been updated' do
        it 'counts all models the normal way' do
          expect(Project).to receive(:count).and_call_original
          expect(Identity).to receive(:count).and_call_original
          expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
        end
      end

      describe 'no permission' do
        it 'falls back to standard query' do
          allow(ActiveRecord::Base).to receive(:transaction).and_raise(PG::InsufficientPrivilege)

          expect(Project).to receive(:count).and_call_original
          expect(Identity).to receive(:count).and_call_original
          expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
        end
      end

      describe 'when some reltuples have been updated' do
        it 'counts projects in the fast way' do
          expect(reltuples_strategy).to receive(:count).and_return({ Project => 3 })

          expect(Project).not_to receive(:count).and_call_original
          expect(Identity).to receive(:count).and_call_original
          expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
        end
      end

      # TODO: This covers two parts: reltuple strategy itself and the fallback
      # TODO: Add spec that covers strategy details for reltuple strategy
      describe 'when all reltuples have been updated' do
        #before do
          #ActiveRecord::Base.connection.execute('ANALYZE projects')
          #ActiveRecord::Base.connection.execute('ANALYZE identities')
        #end

        it 'counts models with the standard way' do
          allow(reltuples_strategy).to receive(:count).and_return({ Project => 3, Identity => 1 })
          expect(Project).not_to receive(:count)
          expect(Identity).not_to receive(:count)

          expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
        end
      end

      describe Gitlab::Database::Count::ExactCountStrategy do
        subject { described_class.new(models).count }

        describe '#count' do
          it 'counts all models' do
            models.each { |model| expect(model).to receive(:count).and_call_original }

            expect(subject).to eq({ Project => 3, Identity => 1 })
          end
        end

        describe '.enabled?' do
          it 'is enabled for PostgreSQL' do
            allow(Gitlab::Database).to receive(:postgresql?).and_return(true)

            expect(described_class.enabled?).to be_truthy
          end

          it 'is enabled for MySQL' do
            allow(Gitlab::Database).to receive(:postgresql?).and_return(false)

            expect(described_class.enabled?).to be_truthy
          end
        end
      end

      describe Gitlab::Database::Count::ReltuplesCountStrategy do
        subject { described_class.new(models).count }

        describe '#count' do
          context 'when reltuples is not up to date' do
            it 'returns an empty hash' do
              models.each { |model| expect(model).not_to receive(:count) }

              expect(subject).to eq({})
            end
          end
        end

        describe '.enabled?' do
          it 'is enabled for PostgreSQL' do
            allow(Gitlab::Database).to receive(:postgresql?).and_return(true)

            expect(described_class.enabled?).to be_truthy
          end

          it 'is disabled for MySQL' do
            allow(Gitlab::Database).to receive(:postgresql?).and_return(false)

            expect(described_class.enabled?).to be_falsey
          end
        end
      end
    end
  end
end