summaryrefslogtreecommitdiff
path: root/spec/models/postgresql/replication_slot_spec.rb
blob: c3b67a2e7b870cf3a14381ee393bd521fcae1c34 (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 Postgresql::ReplicationSlot do
  describe '.in_use?' do
    it 'returns true when replication slots are present' do
      expect(described_class).to receive(:exists?).and_return(true)
      expect(described_class.in_use?).to be_truthy
    end

    it 'returns false when replication slots are not present' do
      expect(described_class.in_use?).to be_falsey
    end

    it 'returns false if the existence check is invalid' do
      expect(described_class).to receive(:exists?).and_raise(ActiveRecord::StatementInvalid.new('PG::FeatureNotSupported'))
      expect(described_class.in_use?).to be_falsey
    end
  end

  describe '.lag_too_great?' do
    before do
      expect(described_class).to receive(:in_use?).and_return(true)
    end

    it 'does not raise an exception' do
      expect { described_class.lag_too_great? }.not_to raise_error
    end

    it 'returns true when replication lag is too great' do
      expect(described_class)
        .to receive(:pluck)
        .and_return([125.megabytes])

      expect(described_class.lag_too_great?).to eq(true)
    end

    it 'returns false when more than one replicas is up to date enough' do
      expect(described_class)
        .to receive(:pluck)
        .and_return([125.megabytes, 0.megabytes, 0.megabytes])

      expect(described_class.lag_too_great?).to eq(false)
    end

    it 'returns false when replication lag is not too great' do
      expect(described_class)
        .to receive(:pluck)
        .and_return([0.megabytes])

      expect(described_class.lag_too_great?).to eq(false)
    end

    it 'returns false when there is a nil replication lag' do
      expect(described_class)
        .to receive(:pluck)
        .and_return([0.megabytes, nil])

      expect(described_class.lag_too_great?).to eq(false)
    end
  end

  describe '#max_replication_slots' do
    it 'returns the maximum number of replication slots' do
      expect(described_class.max_replication_slots).to be >= 0
    end
  end

  context 'with enough slots available' do
    skip_examples = described_class.max_replication_slots <= described_class.count

    before(:all) do
      skip('max_replication_slots too small') if skip_examples

      @current_slot_count = ApplicationRecord
        .connection
        .execute("SELECT COUNT(*) FROM pg_replication_slots;")
        .first
        .fetch('count')
        .to_i

      @current_unused_count = ApplicationRecord
        .connection
        .execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 'f';")
        .first
        .fetch('count')
        .to_i

      ApplicationRecord
        .connection
        .execute("SELECT * FROM pg_create_physical_replication_slot('test_slot');")
    end

    after(:all) do
      unless skip_examples
        ApplicationRecord
          .connection
          .execute("SELECT pg_drop_replication_slot('test_slot');")
      end
    end

    describe '#slots_count' do
      it 'returns the number of replication slots' do
        expect(described_class.count).to eq(@current_slot_count + 1)
      end
    end

    describe '#unused_slots_count' do
      it 'returns the number of unused replication slots' do
        expect(described_class.unused_slots_count).to eq(@current_unused_count + 1)
      end
    end

    describe '#max_retained_wal' do
      it 'returns the retained WAL size' do
        expect(described_class.max_retained_wal).not_to be_nil
      end
    end

    describe '#slots_retained_bytes' do
      it 'returns the number of retained bytes' do
        slot = described_class.slots_retained_bytes.find {|x| x['slot_name'] == 'test_slot' }

        expect(slot).not_to be_nil
        expect(slot['retained_bytes']).to be_nil
      end
    end
  end
end