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

replication_slot.rb « postgresql « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 1a4d3bd57945a9897fde399fb9382ce8232c3b1c (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
# frozen_string_literal: true

module Postgresql
  class ReplicationSlot < ApplicationRecord
    self.table_name = 'pg_replication_slots'

    # Returns true if there are any replication slots in use.
    # PostgreSQL-compatible databases such as Aurora don't support
    # replication slots, so this will return false as well.
    def self.in_use?
      transaction { exists? }
    rescue ActiveRecord::StatementInvalid
      false
    end

    # Returns true if the lag observed across all replication slots exceeds a
    # given threshold.
    #
    # max - The maximum replication lag size, in bytes. Based on GitLab.com
    #       statistics it takes between 1 and 5 seconds to replicate around
    #       100 MB of data.
    def self.lag_too_great?(max = 100.megabytes)
      return false unless in_use?

      lag_function = "pg_wal_lsn_diff" \
        "(pg_current_wal_insert_lsn(), restart_lsn)::bigint"

      # We force the use of a transaction here so the query always goes to the
      # primary, even when using the DB load balancer.
      sizes = transaction { pluck(Arel.sql(lag_function)) }
      too_great = sizes.compact.count { |size| size >= max }

      # If too many replicas are falling behind too much, the availability of a
      # GitLab instance might suffer. To prevent this from happening we require
      # at least 1 replica to have data recent enough.
      if sizes.any? && too_great > 0
        (sizes.length - too_great) <= 1
      else
        false
      end
    end

    def self.count
      connection
        .execute("SELECT COUNT(*) FROM pg_replication_slots;")
        .first
        .fetch('count')
        .to_i
    end

    def self.unused_slots_count
      connection
        .execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 'f';")
        .first
        .fetch('count')
        .to_i
    end

    def self.used_slots_count
      connection
        .execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 't';")
        .first
        .fetch('count')
        .to_i
    end

    # array of slots and the retained_bytes
    # https://www.skillslogic.com/blog/databases/checking-postgres-replication-lag
    # http://bdr-project.org/docs/stable/monitoring-peers.html
    def self.slots_retained_bytes
      connection.execute(<<-SQL.squish).to_a
        SELECT slot_name, database,
              active, pg_wal_lsn_diff(pg_current_wal_insert_lsn(), restart_lsn)
          AS retained_bytes
          FROM pg_replication_slots;
      SQL
    end

    # returns the max number WAL space (in bytes) being used across the replication slots
    def self.max_retained_wal
      connection.execute(<<-SQL.squish).first.fetch('coalesce').to_i
        SELECT COALESCE(MAX(pg_wal_lsn_diff(pg_current_wal_insert_lsn(), restart_lsn)), 0)
          FROM pg_replication_slots;
      SQL
    end

    def self.max_replication_slots
      connection.execute(<<-SQL.squish).first&.fetch('setting').to_i
        SELECT setting FROM pg_settings WHERE name = 'max_replication_slots';
      SQL
    end
  end
end