Commit 2c7a9c35 authored by Stan Hu's avatar Stan Hu

Merge branch '6097-turn-gitlab-geo-fdw-into-a-class' into 'master'

Geo - Turn Gitlab::Geo::Fdw into a class

Closes #6097

See merge request gitlab-org/gitlab-ee!9263
parents 7e2145e0 b9f8569a
......@@ -8,7 +8,7 @@ module Geo
STORE_COLUMN = :file_store
self.table_name = Gitlab::Geo::Fdw.table('ci_job_artifacts')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('ci_job_artifacts')
scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) }
scope :geo_syncable, -> { with_files_stored_locally.not_expired }
......
......@@ -7,7 +7,7 @@ module Geo
STORE_COLUMN = :file_store
self.table_name = Gitlab::Geo::Fdw.table('lfs_objects')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('lfs_objects')
scope :geo_syncable, -> { with_files_stored_locally }
end
......
......@@ -5,7 +5,7 @@ module Geo
class Project < ::Geo::BaseFdw
include Gitlab::SQL::Pattern
self.table_name = Gitlab::Geo::Fdw.table('projects')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('projects')
class << self
# Searches for a list of projects based on the query given in `query`.
......
......@@ -3,7 +3,7 @@
module Geo
module Fdw
class ProjectFeature < ::Geo::BaseFdw
self.table_name = Gitlab::Geo::Fdw.table('project_features')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('project_features')
end
end
end
......@@ -3,7 +3,7 @@
module Geo
module Fdw
class ProjectRepositoryState < ::Geo::BaseFdw
self.table_name = Gitlab::Geo::Fdw.table('project_repository_states')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('project_repository_states')
end
end
end
......@@ -7,7 +7,7 @@ module Geo
STORE_COLUMN = :store
self.table_name = Gitlab::Geo::Fdw.table('uploads')
self.table_name = Gitlab::Geo::Fdw.foreign_table_name('uploads')
scope :geo_syncable, -> { with_files_stored_locally }
end
......
......@@ -2,114 +2,115 @@
module Gitlab
module Geo
module Fdw
DEFAULT_SCHEMA = 'public'.freeze
FDW_SCHEMA = 'gitlab_secondary'.freeze
# Return full table name with FDW schema
#
# @param [String] table_name
def self.table(table_name)
FDW_SCHEMA + ".#{table_name}"
end
class Fdw
DEFAULT_SCHEMA = 'public'
FOREIGN_SERVER = 'gitlab_secondary'
FOREIGN_SCHEMA = 'gitlab_secondary'
class << self
# Return if FDW is enabled for this instance
#
# @return [Boolean] whether FDW is enabled
def enabled?
return false unless fdw_capable?
# FDW is enabled by default, disable it by setting `fdw: false` in config/database_geo.yml
value = Rails.configuration.geo_database['fdw']
value.nil? ? true : value
end
# Return if FDW is enabled for this instance
#
# @return [Boolean] whether FDW is enabled
def self.enabled?
return false unless fdw_capable?
# Return full table name with foreign schema
#
# @param [String] table_name
def foreign_table_name(table_name)
FOREIGN_SCHEMA + ".#{table_name}"
end
# FDW is enabled by default, disable it by setting `fdw: false` in config/database_geo.yml
value = Rails.configuration.geo_database['fdw']
value.nil? ? true : value
end
def foreign_tables_up_to_date?
has_foreign_schema? && foreign_schema_tables_match?
end
def self.fdw_capable?
has_foreign_schema? && connection_exist? && count_tables.positive?
end
# Number of existing tables
#
# @return [Integer] number of tables
def foreign_schema_tables_count
Gitlab::Geo.cache_value(:geo_fdw_count_tables) do
sql = <<~SQL
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = '#{FOREIGN_SCHEMA}'
AND table_type = 'FOREIGN TABLE'
AND table_name NOT LIKE 'pg_%'
SQL
::Geo::TrackingBase.connection.execute(sql).first.fetch('count').to_i
end
end
def self.fdw_up_to_date?
has_foreign_schema? && foreign_schema_tables_match?
end
def gitlab_schema_tables_count
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }.count
end
def self.has_foreign_schema?
Gitlab::Geo.cache_value(:geo_fdw_schema_exist) do
sql = <<~SQL
SELECT 1
FROM information_schema.schemata
WHERE schema_name='#{FDW_SCHEMA}'
SQL
private
::Geo::TrackingBase.connection.execute(sql).count.positive?
def fdw_capable?
has_foreign_server? && has_foreign_schema? && foreign_schema_tables_count.positive?
end
end
# Check if there is at least one FDW connection configured
#
# @return [Boolean] whether any FDW connection exists
def self.connection_exist?
::Geo::TrackingBase.connection.execute(
"SELECT 1 FROM pg_foreign_server"
).count.positive?
end
# Check if there is at least one foreign server configured
#
# @return [Boolean] whether any foreign server exists
def has_foreign_server?
::Geo::TrackingBase.connection.execute(
"SELECT 1 FROM pg_foreign_server"
).count.positive?
end
# Number of existing tables
#
# @return [Integer] number of tables
def self.count_tables
Gitlab::Geo.cache_value(:geo_fdw_count_tables) do
sql = <<~SQL
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = '#{FDW_SCHEMA}'
AND table_type = 'FOREIGN TABLE'
AND table_name NOT LIKE 'pg_%'
SQL
def has_foreign_schema?
Gitlab::Geo.cache_value(:geo_FOREIGN_SCHEMA_exist) do
sql = <<~SQL
SELECT 1
FROM information_schema.schemata
WHERE schema_name='#{FOREIGN_SCHEMA}'
SQL
::Geo::TrackingBase.connection.execute(sql).first.fetch('count').to_i
::Geo::TrackingBase.connection.execute(sql).count.positive?
end
end
end
# Check if foreign schema has exact the same tables and fields defined on secondary database
#
# @return [Boolean] whether schemas match and are not empty
def self.foreign_schema_tables_match?
Gitlab::Geo.cache_value(:geo_fdw_schema_tables_match) do
schema = gitlab_schema
# Check if foreign schema has exact the same tables and fields defined on secondary database
#
# @return [Boolean] whether schemas match and are not empty
def foreign_schema_tables_match?
Gitlab::Geo.cache_value(:geo_foreign_schema_tables_match) do
gitlab_schema_tables = retrieve_gitlab_schema_tables.to_set
foreign_schema_tables = retrieve_foreign_schema_tables.to_set
schema.present? && (schema.to_set == fdw_schema.to_set)
gitlab_schema_tables.present? && (gitlab_schema_tables == foreign_schema_tables)
end
end
end
def self.count_tables_match?
gitlab_tables.count == count_tables
end
def self.gitlab_tables
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }
end
def retrieve_foreign_schema_tables
retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FOREIGN_SCHEMA).to_a
end
def self.gitlab_schema
retrieve_schema_tables(ActiveRecord::Base, ActiveRecord::Base.connection_config[:database], DEFAULT_SCHEMA).to_a
end
def retrieve_gitlab_schema_tables
retrieve_schema_tables(ActiveRecord::Base, ActiveRecord::Base.connection_config[:database], DEFAULT_SCHEMA).to_a
end
def self.fdw_schema
retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FDW_SCHEMA).to_a
end
def retrieve_schema_tables(adapter, database, schema)
sql = <<~SQL
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_catalog = '#{database}'
AND table_schema = '#{schema}'
AND table_name NOT LIKE 'pg_%'
ORDER BY table_name, column_name, data_type
SQL
def self.retrieve_schema_tables(adapter, database, schema)
sql = <<~SQL
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_catalog = '#{database}'
AND table_schema = '#{schema}'
AND table_name NOT LIKE 'pg_%'
ORDER BY table_name, column_name, data_type
SQL
adapter.connection.select_all(sql)
adapter.connection.select_all(sql)
end
end
private_class_method :retrieve_schema_tables
end
end
end
......@@ -55,7 +55,7 @@ module Gitlab
sql = <<~SQL
SELECT count(1)
FROM pg_foreign_server
WHERE srvname = '#{Gitlab::Geo::Fdw::FDW_SCHEMA}';
WHERE srvname = '#{Gitlab::Geo::Fdw::FOREIGN_SERVER}';
SQL
Gitlab::Geo::DatabaseTasks.with_geo_db do
......
......@@ -21,11 +21,14 @@ module Gitlab
return 'The Geo database is not configured to use Foreign Data Wrapper.' unless Gitlab::Geo::Fdw.enabled?
unless Gitlab::Geo::Fdw.fdw_up_to_date?
unless Gitlab::Geo::Fdw.foreign_tables_up_to_date?
output = "The Geo database has an outdated FDW remote schema."
unless Gitlab::Geo::Fdw.count_tables_match?
output = "#{output} It contains #{Gitlab::Geo::Fdw.count_tables} of #{Gitlab::Geo::Fdw.gitlab_tables.count} expected tables."
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
return output
......
......@@ -25,7 +25,7 @@ module SystemCheck
end
def check?
Gitlab::Geo::Fdw.fdw_up_to_date?
Gitlab::Geo::Fdw.foreign_tables_up_to_date?
end
def show_error
......
......@@ -58,7 +58,7 @@ namespace :geo do
desc 'Refresh Foreign Tables definition in Geo Secondary node'
task refresh_foreign_tables: [:environment] do
if Gitlab::Geo::GeoTasks.foreign_server_configured?
print "\nRefreshing foreign tables for FDW: #{Gitlab::Geo::Fdw::FDW_SCHEMA} ... "
print "\nRefreshing foreign tables for FDW: #{Gitlab::Geo::Fdw::FOREIGN_SCHEMA} ... "
Gitlab::Geo::GeoTasks.refresh_foreign_tables!
puts 'Done!'
else
......
This diff is collapsed.
......@@ -96,8 +96,9 @@ describe Gitlab::Geo::HealthCheck, :geo do
allow(described_class).to receive(:database_secondary?) { true }
allow(described_class).to receive(:streaming_active?) { true }
allow(Gitlab::Geo::Fdw).to receive(:fdw_up_to_date?) { false }
allow(Gitlab::Geo::Fdw).to receive(:count_tables_match?) { true }
allow(Gitlab::Geo::Fdw).to receive(:foreign_tables_up_to_date?) { false }
allow(Gitlab::Geo::Fdw).to receive(:foreign_schema_tables_count) { 1 }
allow(Gitlab::Geo::Fdw).to receive(:gitlab_schema_tables_count) { 1 }
expect(subject.perform_checks).to match(/The Geo database has an outdated FDW remote schema\./)
end
......@@ -106,8 +107,9 @@ describe Gitlab::Geo::HealthCheck, :geo do
allow(described_class).to receive(:database_secondary?) { true }
allow(described_class).to receive(:streaming_active?) { true }
allow(Gitlab::Geo::Fdw).to receive(:fdw_up_to_date?) { false }
allow(Gitlab::Geo::Fdw).to receive(:count_tables_match?) { false }
allow(Gitlab::Geo::Fdw).to receive(:foreign_tables_up_to_date?) { false }
allow(Gitlab::Geo::Fdw).to receive(:foreign_schema_tables_count) { 1 }
allow(Gitlab::Geo::Fdw).to receive(:gitlab_schema_tables_count) { 2 }
expect(subject.perform_checks).to match(/The Geo database has an outdated FDW remote schema\. It contains [0-9]+ of [0-9]+ expected tables/)
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