Commit 9913a3a1 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch...

Merge branch '5933-geo-inconsistent-database-replication-status-when-wal-e-streaming-is-in-use-2-tidy' into 'master'

Geo: Tidy up Gitlab::Geo::HealthCheck

See merge request gitlab-org/gitlab-ee!9990
parents 0b04be12 044a695c
......@@ -3,6 +3,6 @@ HealthCheck.setup do |config|
config.full_checks = %w(database migrations cache)
config.add_custom_check('geo') do
Gitlab::Geo::HealthCheck.perform_checks
Gitlab::Geo::HealthCheck.new.perform_checks
end
end
......@@ -209,7 +209,7 @@ class GeoNodeStatus < ActiveRecord::Base
def load_secondary_data
if Gitlab::Geo.secondary?
self.db_replication_lag_seconds = Gitlab::Geo::HealthCheck.db_replication_lag_seconds
self.db_replication_lag_seconds = Gitlab::Geo::HealthCheck.new.db_replication_lag_seconds
self.cursor_last_event_id = current_cursor_last_event_id
self.cursor_last_event_date = Geo::EventLog.find_by(id: self.cursor_last_event_id)&.created_at
self.repositories_synced_count = projects_finder.count_synced_repositories
......
......@@ -3,34 +3,21 @@
module Gitlab
module Geo
class HealthCheck
def self.perform_checks
include Gitlab::Utils::StrongMemoize
def perform_checks
raise NotImplementedError.new('Geo is only compatible with PostgreSQL') unless Gitlab::Database.postgresql?
return '' unless Gitlab::Geo.secondary?
return 'The Geo database configuration file is missing.' unless Gitlab::Geo.geo_database_configured?
return 'The Geo node has a database that is not configured for streaming replication with the primary node.' unless self.database_secondary?
return 'The Geo node does not appear to be replicating the database from the primary node.' if Gitlab::Database.pg_stat_wal_receiver_supported? && !self.streaming_active?
database_version = self.get_database_version.to_i
migration_version = self.get_migration_version.to_i
if database_version != migration_version
return "Current Geo database version (#{database_version}) does not match latest migration (#{migration_version}).\n"\
'You may have to run `gitlab-rake geo:db:migrate` as root on the secondary.'
end
return 'The Geo database is not configured to use Foreign Data Wrapper.' unless Gitlab::Geo::Fdw.enabled?
return 'Geo database configuration file is missing.' unless Gitlab::Geo.geo_database_configured?
return 'Geo node has a database that is not configured for streaming replication with the primary node.' unless Gitlab::Database.db_read_only?
return 'Geo node does not appear to be replicating the database from the primary node.' if Gitlab::Database.pg_stat_wal_receiver_supported? && !streaming_active?
return "Current Geo database version (#{database_version}) does not match latest migration (#{migration_version}).\nYou may have to run `gitlab-rake geo:db:migrate` as root on the secondary." unless database_migration_version_match?
return 'Geo database is not configured to use Foreign Data Wrapper.' unless Gitlab::Geo::Fdw.enabled?
unless Gitlab::Geo::Fdw.foreign_tables_up_to_date?
output = "The Geo database has an outdated FDW remote schema."
foreign_schema_tables_count = Gitlab::Geo::Fdw.foreign_schema_tables_count
gitlab_schema_tables_count = Gitlab::Geo::Fdw.gitlab_schema_tables_count
unless gitlab_schema_tables_count == foreign_schema_tables_count
output = "#{output} It contains #{foreign_schema_tables_count} of #{gitlab_schema_tables_count} expected tables."
end
output = "Geo database has an outdated FDW remote schema."
output = "#{output} It contains #{foreign_schema_tables_count} of #{gitlab_schema_tables_count} expected tables." unless schema_tables_match?
return output
end
......@@ -39,74 +26,90 @@ module Gitlab
e.message
end
def self.db_migrate_path
def db_replication_lag_seconds
# Obtain the replication lag in seconds
ActiveRecord::Base.connection
.execute(db_replication_lag_seconds_query)
.first
.fetch('replication_lag').to_i
end
private
def db_replication_lag_seconds_query
<<-SQL.squish
SELECT CASE
WHEN #{Gitlab::Database.pg_last_wal_receive_lsn}() = #{Gitlab::Database.pg_last_wal_replay_lsn}()
THEN 0
ELSE
EXTRACT (EPOCH FROM now() - pg_last_xact_replay_timestamp())::INTEGER
END
AS replication_lag
SQL
end
def db_migrate_path
# Lazy initialisation so Rails.root will be defined
@db_migrate_path ||= File.join(Rails.root, 'ee', 'db', 'geo', 'migrate')
end
def self.db_post_migrate_path
def db_post_migrate_path
# Lazy initialisation so Rails.root will be defined
@db_post_migrate_path ||= File.join(Rails.root, 'ee', 'db', 'geo', 'post_migrate')
end
def self.get_database_version
if defined?(ActiveRecord)
connection = ::Geo::BaseRegistry.connection
schema_migrations_table_name = ActiveRecord::Base.schema_migrations_table_name
if connection.data_source_exists?(schema_migrations_table_name)
connection.execute("SELECT MAX(version) AS version FROM #{schema_migrations_table_name}")
.first
.fetch('version')
def database_version
strong_memoize(:database_version) do
if defined?(ActiveRecord)
connection = ::Geo::BaseRegistry.connection
schema_migrations_table_name = ActiveRecord::Base.schema_migrations_table_name
if connection.data_source_exists?(schema_migrations_table_name)
connection.execute("SELECT MAX(version) AS version FROM #{schema_migrations_table_name}")
.first
.fetch('version')
end
end
end
end
def self.get_migration_version
latest_migration = nil
def migration_version
strong_memoize(:migration_version) do
latest_migration = nil
Dir[File.join(self.db_migrate_path, "[0-9]*_*.rb"), File.join(self.db_post_migrate_path, "[0-9]*_*.rb")].each do |f|
timestamp = f.scan(/0*([0-9]+)_[_.a-zA-Z0-9]*.rb/).first.first rescue -1
Dir[File.join(db_migrate_path, "[0-9]*_*.rb"), File.join(db_post_migrate_path, "[0-9]*_*.rb")].each do |f|
timestamp = f.scan(/0*([0-9]+)_[_.a-zA-Z0-9]*.rb/).first.first rescue -1
if latest_migration.nil? || timestamp.to_i > latest_migration.to_i
latest_migration = timestamp
if latest_migration.nil? || timestamp.to_i > latest_migration.to_i
latest_migration = timestamp
end
end
latest_migration
end
end
latest_migration
def database_migration_version_match?
database_version.to_i == migration_version.to_i
end
def self.database_secondary?
Gitlab::Database.db_read_only?
def gitlab_schema_tables_count
@gitlab_schema_tables_count ||= Gitlab::Geo::Fdw.gitlab_schema_tables_count
end
def self.db_replication_lag_seconds
# Obtain the replication lag in seconds
lag =
ActiveRecord::Base.connection.execute(<<-SQL.squish)
SELECT CASE
WHEN #{Gitlab::Database.pg_last_wal_receive_lsn}() = #{Gitlab::Database.pg_last_wal_receive_lsn}()
THEN 0
ELSE
EXTRACT (EPOCH FROM now() - pg_last_xact_replay_timestamp())::INTEGER
END
AS replication_lag
SQL
.first
.fetch('replication_lag')
def foreign_schema_tables_count
@foreign_schema_tables_count ||= Gitlab::Geo::Fdw.foreign_schema_tables_count
end
lag.present? ? lag.to_i : lag
def schema_tables_match?
gitlab_schema_tables_count == foreign_schema_tables_count
end
def self.streaming_active?
def streaming_active?
# Get a streaming status
# This only works for Postgresql 9.6 and greater
pid =
ActiveRecord::Base.connection.select_values('
SELECT pid FROM pg_stat_wal_receiver;')
.first
pid.to_i > 0
ActiveRecord::Base.connection
.select_values('SELECT pid FROM pg_stat_wal_receiver').first.to_i > 0
end
end
end
......
......@@ -297,7 +297,7 @@ namespace :geo do
puts geo_node.namespaces.any? ? 'Selective' : 'Full'
print 'Database replication lag: '.rjust(COLUMN_WIDTH)
puts "#{Gitlab::Geo::HealthCheck.db_replication_lag_seconds} seconds"
puts "#{Gitlab::Geo::HealthCheck.new.db_replication_lag_seconds} seconds"
print 'Last event ID seen from primary: '.rjust(COLUMN_WIDTH)
last_event = Geo::EventLog.last
......
This diff is collapsed.
......@@ -228,14 +228,14 @@ describe GeoNodeStatus, :geo do
describe '#db_replication_lag_seconds' do
it 'returns the set replication lag if secondary' do
allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
allow(Gitlab::Geo::HealthCheck).to receive(:db_replication_lag_seconds).and_return(1000)
geo_health_check = double('Gitlab::Geo::HealthCheck', perform_checks: '', db_replication_lag_seconds: 1000)
allow(Gitlab::Geo::HealthCheck).to receive(:new).and_return(geo_health_check)
expect(subject.db_replication_lag_seconds).to eq(1000)
end
it "doesn't attempt to set replication lag if primary" do
stub_current_geo_node(primary)
expect(Gitlab::Geo::HealthCheck).not_to receive(:db_replication_lag_seconds)
expect(subject.db_replication_lag_seconds).to eq(nil)
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