summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/database/reindexing/coordinator_spec.rb
blob: bb91617714a4fc7b48468aca58f289a9f786ef7c (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Database::Reindexing::Coordinator do
  include Database::DatabaseHelpers
  include ExclusiveLeaseHelpers

  let(:notifier) { instance_double(Gitlab::Database::Reindexing::GrafanaNotifier, notify_start: nil, notify_end: nil) }
  let(:index) { create(:postgres_index) }
  let(:connection) { index.connection }

  let!(:lease) { stub_exclusive_lease(lease_key, uuid, timeout: lease_timeout) }
  let(:lease_key) { "gitlab/database/reindexing/coordinator/#{Gitlab::Database::PRIMARY_DATABASE_NAME}" }
  let(:lease_timeout) { 1.day }
  let(:uuid) { 'uuid' }

  around do |example|
    model = Gitlab::Database.database_base_models[Gitlab::Database::PRIMARY_DATABASE_NAME]

    Gitlab::Database::SharedModel.using_connection(model.connection) do
      example.run
    end
  end

  before do
    swapout_view_for_table(:postgres_indexes)
  end

  describe '#perform' do
    subject { described_class.new(index, notifier).perform }

    let(:reindexer) { instance_double(Gitlab::Database::Reindexing::ReindexConcurrently, perform: nil) }
    let(:action) { create(:reindex_action, index: index) }

    before do
      allow(Gitlab::Database::Reindexing::ReindexConcurrently).to receive(:new).with(index).and_return(reindexer)
      allow(Gitlab::Database::Reindexing::ReindexAction).to receive(:create_for).with(index).and_return(action)
    end

    context 'locking' do
      it 'acquires a lock while reindexing' do
        expect(lease).to receive(:try_obtain).ordered.and_return(uuid)

        expect(reindexer).to receive(:perform).ordered

        expect(Gitlab::ExclusiveLease).to receive(:cancel).ordered.with(lease_key, uuid)

        subject
      end

      it 'does not perform reindexing actions if lease is not granted' do
        expect(lease).to receive(:try_obtain).ordered.and_return(false)
        expect(Gitlab::Database::Reindexing::ReindexConcurrently).not_to receive(:new)

        subject
      end
    end

    context 'notifications' do
      it 'sends #notify_start before reindexing' do
        expect(notifier).to receive(:notify_start).with(action).ordered
        expect(reindexer).to receive(:perform).ordered

        subject
      end

      it 'sends #notify_end after reindexing and updating the action is done' do
        expect(action).to receive(:finish).ordered
        expect(notifier).to receive(:notify_end).with(action).ordered

        subject
      end
    end

    context 'action tracking' do
      it 'calls #finish on the action' do
        expect(reindexer).to receive(:perform).ordered
        expect(action).to receive(:finish).ordered

        subject
      end

      it 'upon error, it still calls finish and raises the error' do
        expect(reindexer).to receive(:perform).ordered.and_raise('something went wrong')
        expect(action).to receive(:finish).ordered

        expect { subject }.to raise_error(/something went wrong/)

        expect(action).to be_failed
      end
    end
  end

  describe '#drop' do
    let(:connection) { index.connection }

    subject(:drop) { described_class.new(index, notifier).drop }

    context 'when exclusive lease is granted' do
      it 'drops the index with lock retries' do
        expect(lease).to receive(:try_obtain).ordered.and_return(uuid)

        expect_query("SET lock_timeout TO '60000ms'")
        expect_query("DROP INDEX CONCURRENTLY IF EXISTS \"public\".\"#{index.name}\"")
        expect_query("RESET idle_in_transaction_session_timeout; RESET lock_timeout")

        expect(Gitlab::ExclusiveLease).to receive(:cancel).ordered.with(lease_key, uuid)

        drop
      end

      def expect_query(sql)
        expect(connection).to receive(:execute).ordered.with(sql).and_wrap_original do |method, sql|
          method.call(sql.sub(/CONCURRENTLY/, ''))
        end
      end
    end

    context 'when exclusive lease is not granted' do
      it 'does not drop the index' do
        expect(lease).to receive(:try_obtain).ordered.and_return(false)
        expect(Gitlab::Database::WithLockRetriesOutsideTransaction).not_to receive(:new)
        expect(connection).not_to receive(:execute)

        drop
      end
    end
  end
end