Class: Postgresql::ReplicationSlot

Inherits:
Gitlab::Database::SharedModel show all
Defined in:
app/models/postgresql/replication_slot.rb

Class Method Summary collapse

Methods inherited from Gitlab::Database::SharedModel

connection, #connection_db_config, connection_pool, using_connection

Class Method Details

.countObject



43
44
45
46
47
48
49
# File 'app/models/postgresql/replication_slot.rb', line 43

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

.in_use?Boolean

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.

Returns:

  • (Boolean)


10
11
12
13
14
# File 'app/models/postgresql/replication_slot.rb', line 10

def self.in_use?
  transaction { exists? }
rescue ActiveRecord::StatementInvalid
  false
end

.lag_too_great?(max = 100.megabytes) ⇒ Boolean

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.

Returns:

  • (Boolean)


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'app/models/postgresql/replication_slot.rb', line 22

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

.max_replication_slotsObject



87
88
89
90
91
# File 'app/models/postgresql/replication_slot.rb', line 87

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

.max_retained_walObject

returns the max number WAL space (in bytes) being used across the replication slots



80
81
82
83
84
85
# File 'app/models/postgresql/replication_slot.rb', line 80

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

.slots_retained_bytesObject



70
71
72
73
74
75
76
77
# File 'app/models/postgresql/replication_slot.rb', line 70

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

.unused_slots_countObject



51
52
53
54
55
56
57
# File 'app/models/postgresql/replication_slot.rb', line 51

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

.used_slots_countObject



59
60
61
62
63
64
65
# File 'app/models/postgresql/replication_slot.rb', line 59

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