Welcome to mirror list, hosted at ThFree Co, Russian Federation.

optimistic_locking_spec.rb « gitlab « lib « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 34f197b5ddb1f06730143046e1e6df809a50d7d0 (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::OptimisticLocking do
  let!(:pipeline) { create(:ci_pipeline) }
  let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) }
  let(:histogram) { spy('prometheus metric') }

  before do
    allow(described_class)
      .to receive(:retry_lock_histogram)
      .and_return(histogram)
  end

  describe '#retry_lock' do
    let(:name) { 'optimistic_locking_spec' }

    it 'does not change current_scope', :aggregate_failures do
      instance = Class.new { include Gitlab::OptimisticLocking }.new
      relation = pipeline.cancelable_statuses

      expected_scope = Ci::Build.current_scope&.to_sql

      instance.send(:retry_lock, relation, name: :test) do
        expect(Ci::Build.current_scope&.to_sql).to eq(expected_scope)
      end

      expect(Ci::Build.current_scope&.to_sql).to eq(expected_scope)
    end

    context 'when state changed successfully without retries' do
      subject do
        described_class.retry_lock(pipeline, name: name) do |lock_subject|
          lock_subject.succeed
        end
      end

      it 'does not reload object' do
        expect(pipeline).not_to receive(:reset)
        expect(pipeline).to receive(:succeed).and_call_original

        subject
      end

      it 'does not create log record' do
        expect(described_class.retry_lock_logger).not_to receive(:info)

        subject
      end

      it 'adds number of retries to histogram' do
        subject

        expect(histogram).to have_received(:observe).with({}, 0)
      end
    end

    context 'when at least one retry happened, the change succeeded' do
      subject do
        described_class.retry_lock(pipeline2, name: 'optimistic_locking_spec') do |lock_subject|
          lock_subject.drop
        end
      end

      before do
        pipeline.succeed
      end

      it 'completes the action' do
        expect(pipeline2).to receive(:reset).and_call_original
        expect(pipeline2).to receive(:drop).twice.and_call_original

        subject
      end

      it 'creates a single log record' do
        expect(described_class.retry_lock_logger)
          .to receive(:info)
          .once
          .with(hash_including(:time_s, name: name, retries: 1))

        subject
      end

      it 'adds number of retries to histogram' do
        subject

        expect(histogram).to have_received(:observe).with({}, 1)
      end
    end

    context 'when MAX_RETRIES attempts exceeded' do
      subject do
        described_class.retry_lock(pipeline, max_retries, name: name) do |lock_subject|
          lock_subject.lock_version = 100
          lock_subject.drop
        end
      end

      let(:max_retries) { 2 }

      it 'raises an exception' do
        expect(pipeline).to receive(:drop).exactly(max_retries + 1).times.and_call_original

        expect { subject }.to raise_error(ActiveRecord::StaleObjectError)
      end

      it 'creates a single log record' do
        expect(described_class.retry_lock_logger)
          .to receive(:info)
          .once
          .with(hash_including(:time_s, name: name, retries: max_retries))

        expect { subject }.to raise_error(ActiveRecord::StaleObjectError)
      end

      it 'adds number of retries to histogram' do
        expect { subject }.to raise_error(ActiveRecord::StaleObjectError)

        expect(histogram).to have_received(:observe).with({}, max_retries)
      end
    end
  end

  describe '#retry_optimistic_lock' do
    context 'when locking module is mixed in' do
      let(:unlockable) do
        Class.new.include(described_class).new
      end

      it 'is an alias for retry_lock' do
        expect(unlockable.method(:retry_optimistic_lock))
          .to eq unlockable.method(:retry_lock)
      end
    end
  end
end