summaryrefslogtreecommitdiff
path: root/app/models/postgresql/replication_slot.rb
blob: 7a123deb7193a5bf417cc80a5d9a80ec65eb0861 (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
# 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 = "#{Gitlab::Database.pg_wal_lsn_diff}" \
        "(#{Gitlab::Database.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 EE DB load balancer.
      sizes = transaction { pluck(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.positive?
        (sizes.length - too_great) <= 1
      else
        false
      end
    end
  end
end