Commit 89aff662 authored by Yorick Peterse's avatar Yorick Peterse

Merge PgReplicationSlot into ReplicationSlot

This merges the EE-only PgReplicationSlot class into the class
Postgresql::ReplicationSlot class, and updates any references to
PgReplicationSlot to use the new class instead.
parent 5b3a1fba
......@@ -39,5 +39,55 @@ module Postgresql
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
......@@ -299,19 +299,19 @@ class GeoNode < ApplicationRecord
def replication_slots_count
return unless primary?
PgReplicationSlot.count
Postgresql::ReplicationSlot.count
end
def replication_slots_used_count
return unless primary?
PgReplicationSlot.used_slots_count
Postgresql::ReplicationSlot.used_slots_count
end
def replication_slots_max_retained_wal_bytes
return unless primary?
PgReplicationSlot.max_retained_wal
Postgresql::ReplicationSlot.max_retained_wal
end
def find_or_build_status
......
# frozen_string_literal: true
# `pg_replication_slots` is a PostgreSQL view
class PgReplicationSlot
def self.count
ApplicationRecord.connection.execute("SELECT COUNT(*) FROM pg_replication_slots;")
.first.fetch('count').to_i
end
def self.unused_slots_count
ApplicationRecord.connection.execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 'f';")
.first.fetch('count').to_i
end
def self.used_slots_count
ApplicationRecord.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
ApplicationRecord.connection.execute(<<-SQL.squish)
SELECT slot_name, database,
active, pg_wal_lsn_diff(pg_current_wal_insert_lsn(), restart_lsn)
AS retained_bytes
FROM pg_replication_slots;
SQL
.to_a
end
# returns the max number WAL space (in bytes) being used across the replication slots
def self.max_retained_wal
ApplicationRecord.connection.execute(<<-SQL.squish)
SELECT COALESCE(MAX(pg_wal_lsn_diff(pg_current_wal_insert_lsn(), restart_lsn)), 0)
FROM pg_replication_slots;
SQL
.first.fetch('coalesce').to_i
end
def self.max_replication_slots
ApplicationRecord.connection.execute(<<-SQL.squish)
SELECT setting FROM pg_settings WHERE name = 'max_replication_slots';
SQL
.first&.fetch('setting').to_i
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PgReplicationSlot do
it '#max_replication_slots' do
expect(described_class.max_replication_slots).to be >= 0
end
skip_examples = described_class.max_replication_slots <= described_class.count
context 'with enough slots available' do
before(:all) do
skip('max_replication_slots too small') if skip_examples
@current_slot_count =
ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM pg_replication_slots;")
.first.fetch('count').to_i
@current_unused_count =
ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 'f';")
.first.fetch('count').to_i
ActiveRecord::Base.connection.execute("SELECT * FROM pg_create_physical_replication_slot('test_slot');")
end
after(:all) do
unless skip_examples
ActiveRecord::Base.connection.execute("SELECT pg_drop_replication_slot('test_slot');")
end
end
it '#slots_count' do
expect(described_class.count).to eq(@current_slot_count + 1)
end
it '#unused_slots_count' do
expect(described_class.unused_slots_count).to eq(@current_unused_count + 1)
end
it '#max_retained_wal' do
expect(described_class.max_retained_wal).not_to be_nil
end
it '#slots_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
......@@ -60,4 +60,71 @@ RSpec.describe Postgresql::ReplicationSlot do
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment