Turn Gitlab::Geo::Fdw into a Class

parent f7bd0d2e
......@@ -2,114 +2,118 @@
module Gitlab
module Geo
module Fdw
class 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 << self
# Return full table name with FDW schema
#
# @param [String] table_name
def table(table_name)
FDW_SCHEMA + ".#{table_name}"
end
# Return if FDW is enabled for this instance
#
# @return [Boolean] whether FDW is enabled
def self.enabled?
return false unless fdw_capable?
# 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
# 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 self.fdw_capable?
has_foreign_schema? && connection_exist? && count_tables.positive?
end
def fdw_up_to_date?
has_foreign_schema? && foreign_schema_tables_match?
end
def self.fdw_up_to_date?
has_foreign_schema? && foreign_schema_tables_match?
end
# Number of existing tables
#
# @return [Integer] number of tables
def 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
::Geo::TrackingBase.connection.execute(sql).first.fetch('count').to_i
end
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
def count_tables_match?
gitlab_tables.count == count_tables
end
::Geo::TrackingBase.connection.execute(sql).count.positive?
def gitlab_tables
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }
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
private
# 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 fdw_capable?
has_foreign_schema? && connection_exist? && count_tables.positive?
end
def 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
::Geo::TrackingBase.connection.execute(sql).first.fetch('count').to_i
::Geo::TrackingBase.connection.execute(sql).count.positive?
end
end
# Check if there is at least one FDW connection configured
#
# @return [Boolean] whether any FDW connection exists
def connection_exist?
::Geo::TrackingBase.connection.execute(
"SELECT 1 FROM pg_foreign_server"
).count.positive?
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_fdw_schema_tables_match) do
schema = gitlab_schema
schema.present? && (schema.to_set == fdw_schema.to_set)
schema.present? && (schema.to_set == fdw_schema.to_set)
end
end
end
def self.count_tables_match?
gitlab_tables.count == count_tables
end
def gitlab_schema
retrieve_schema_tables(ActiveRecord::Base, ActiveRecord::Base.connection_config[:database], DEFAULT_SCHEMA).to_a
end
def self.gitlab_tables
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }
end
def fdw_schema
retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FDW_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_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.fdw_schema
retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FDW_SCHEMA).to_a
adapter.connection.select_all(sql)
end
end
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)
end
private_class_method :retrieve_schema_tables
end
end
end
......@@ -22,7 +22,7 @@ describe Gitlab::Geo::Fdw, :geo do
expect(described_class.enabled?).to be_truthy
end
it 'returns false if configured in `config/database_geo.yml`' do
it 'returns false if disabled in `config/database_geo.yml`' do
allow(Rails.configuration).to receive(:geo_database).and_return('fdw' => false)
expect(described_class.enabled?).to be_falsey
......@@ -38,67 +38,14 @@ describe Gitlab::Geo::Fdw, :geo do
describe '.gitlab_tables' do
it 'excludes pg_ tables' do
tables = described_class.gitlab_tables
ActiveRecord::Base.connection.create_table(:pg_gitlab_test)
expect(described_class.gitlab_tables).to eq(tables)
expect(described_class.gitlab_tables).not_to include('pg_gitlab_test')
ActiveRecord::Base.connection.drop_table(:pg_gitlab_test)
end
end
describe 'fdw_capable?' do
context 'with mocked FDW environment' do
it 'returns true when PostgreSQL FDW is enabled' do
expect(described_class).to receive(:has_foreign_schema?).and_return(true)
expect(described_class).to receive(:count_tables).and_return(1)
expect(described_class.fdw_capable?).to be_truthy
end
it 'returns false when PostgreSQL FDW is not enabled' do
expect(described_class).to receive(:has_foreign_schema?).and_return(false)
expect(described_class.fdw_capable?).to be_falsey
end
it 'returns false when PostgreSQL FDW is enabled but remote tables are empty' do
expect(described_class).to receive(:has_foreign_schema?).and_return(true)
expect(described_class).to receive(:count_tables).and_return(0)
expect(described_class.fdw_capable?).to be_falsey
end
it 'returns false when PostgreSQL FDW is enabled but no remote connection is defined' do
expect(described_class).to receive(:has_foreign_schema?).and_return(true)
expect(described_class).to receive(:connection_exist?).and_return(false)
expect(described_class.fdw_capable?).to be_falsey
end
end
context 'with functional FDW environment' do
it 'returns true' do
expect(described_class.fdw_capable?).to be_truthy
end
context 'with a pg_ table' do
before do
ActiveRecord::Base.connection.create_table(:pg_gitlab_test)
end
after do
ActiveRecord::Base.connection.drop_table(:pg_gitlab_test)
end
it 'returns true' do
expect(described_class.fdw_capable?).to be_truthy
end
end
end
end
describe 'fdw_up_to_date?' do
context 'with mocked FDW environment' do
it 'returns true when FDW is enabled and foreign schema has same tables as secondary database' do
......@@ -129,18 +76,6 @@ describe Gitlab::Geo::Fdw, :geo do
end
end
describe 'has_foreign_schema?' do
context 'with functional FDW environment' do
it 'returns true' do
# When testing it locally, make sure you have FDW set up correctly.
# If you are using GDK, you can run, from GDK root folder:
#
# make postgresql/geo-fdw/test
expect(described_class.has_foreign_schema?).to be_truthy
end
end
end
describe 'count_tables' do
context 'with functional FDW environment' do
it 'returns same amount as defined in schema migration' do
......@@ -151,40 +86,4 @@ describe Gitlab::Geo::Fdw, :geo do
end
end
end
describe 'connection_exist?' do
context 'with functional FDW environment' do
it 'returns true' do
# When testing it locally, make sure you have FDW set up correctly.
# If you are using GDK, you can run, from GDK root folder:
#
# make postgresql/geo-fdw/test
expect(described_class.connection_exist?).to be_truthy
end
end
end
describe 'foreign_schema_tables_match?' do
context 'with functional FDW environment' do
it 'returns true' do
# When testing it locally, you may need to refresh FDW with:
#
# rake geo:db:test:refresh_foreign_tables
expect(described_class.foreign_schema_tables_match?).to be_truthy
end
it 'returns true if order is different' do
one_schema = [
{ "table_name" => "events", "column_name" => "target_type", "data_type" => "character varying" },
{ "table_name" => "ci_job_artifacts", "column_name" => "id", "data_type" => "integer" }
]
second_schema = one_schema.reverse
allow(described_class).to receive(:gitlab_schema).and_return(one_schema)
allow(described_class).to receive(:fdw_schema).and_return(second_schema)
expect(described_class.foreign_schema_tables_match?).to be_truthy
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