Turn Gitlab::Geo::Fdw into a Class

parent f7bd0d2e
...@@ -2,21 +2,22 @@ ...@@ -2,21 +2,22 @@
module Gitlab module Gitlab
module Geo module Geo
module Fdw class Fdw
DEFAULT_SCHEMA = 'public'.freeze DEFAULT_SCHEMA = 'public'.freeze
FDW_SCHEMA = 'gitlab_secondary'.freeze FDW_SCHEMA = 'gitlab_secondary'.freeze
class << self
# Return full table name with FDW schema # Return full table name with FDW schema
# #
# @param [String] table_name # @param [String] table_name
def self.table(table_name) def table(table_name)
FDW_SCHEMA + ".#{table_name}" FDW_SCHEMA + ".#{table_name}"
end end
# Return if FDW is enabled for this instance # Return if FDW is enabled for this instance
# #
# @return [Boolean] whether FDW is enabled # @return [Boolean] whether FDW is enabled
def self.enabled? def enabled?
return false unless fdw_capable? return false unless fdw_capable?
# FDW is enabled by default, disable it by setting `fdw: false` in config/database_geo.yml # FDW is enabled by default, disable it by setting `fdw: false` in config/database_geo.yml
...@@ -24,15 +25,42 @@ module Gitlab ...@@ -24,15 +25,42 @@ module Gitlab
value.nil? ? true : value value.nil? ? true : value
end end
def self.fdw_capable? def fdw_up_to_date?
has_foreign_schema? && connection_exist? && count_tables.positive? has_foreign_schema? && foreign_schema_tables_match?
end end
def self.fdw_up_to_date? # Number of existing tables
has_foreign_schema? && foreign_schema_tables_match? #
# @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 count_tables_match?
gitlab_tables.count == count_tables
end end
def self.has_foreign_schema? def gitlab_tables
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }
end
private
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 Gitlab::Geo.cache_value(:geo_fdw_schema_exist) do
sql = <<~SQL sql = <<~SQL
SELECT 1 SELECT 1
...@@ -47,33 +75,16 @@ module Gitlab ...@@ -47,33 +75,16 @@ module Gitlab
# Check if there is at least one FDW connection configured # Check if there is at least one FDW connection configured
# #
# @return [Boolean] whether any FDW connection exists # @return [Boolean] whether any FDW connection exists
def self.connection_exist? def connection_exist?
::Geo::TrackingBase.connection.execute( ::Geo::TrackingBase.connection.execute(
"SELECT 1 FROM pg_foreign_server" "SELECT 1 FROM pg_foreign_server"
).count.positive? ).count.positive?
end 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
::Geo::TrackingBase.connection.execute(sql).first.fetch('count').to_i
end
end
# Check if foreign schema has exact the same tables and fields defined on secondary database # Check if foreign schema has exact the same tables and fields defined on secondary database
# #
# @return [Boolean] whether schemas match and are not empty # @return [Boolean] whether schemas match and are not empty
def self.foreign_schema_tables_match? def foreign_schema_tables_match?
Gitlab::Geo.cache_value(:geo_fdw_schema_tables_match) do Gitlab::Geo.cache_value(:geo_fdw_schema_tables_match) do
schema = gitlab_schema schema = gitlab_schema
...@@ -81,23 +92,15 @@ module Gitlab ...@@ -81,23 +92,15 @@ module Gitlab
end end
end end
def self.count_tables_match? def gitlab_schema
gitlab_tables.count == count_tables
end
def self.gitlab_tables
ActiveRecord::Schema.tables.reject { |table| table.start_with?('pg_') }
end
def self.gitlab_schema
retrieve_schema_tables(ActiveRecord::Base, ActiveRecord::Base.connection_config[:database], DEFAULT_SCHEMA).to_a retrieve_schema_tables(ActiveRecord::Base, ActiveRecord::Base.connection_config[:database], DEFAULT_SCHEMA).to_a
end end
def self.fdw_schema def fdw_schema
retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FDW_SCHEMA).to_a retrieve_schema_tables(::Geo::TrackingBase, Rails.configuration.geo_database['database'], FDW_SCHEMA).to_a
end end
def self.retrieve_schema_tables(adapter, database, schema) def retrieve_schema_tables(adapter, database, schema)
sql = <<~SQL sql = <<~SQL
SELECT table_name, column_name, data_type SELECT table_name, column_name, data_type
FROM information_schema.columns FROM information_schema.columns
...@@ -109,7 +112,8 @@ module Gitlab ...@@ -109,7 +112,8 @@ module Gitlab
adapter.connection.select_all(sql) adapter.connection.select_all(sql)
end end
private_class_method :retrieve_schema_tables end
end end
end end
end end
...@@ -22,7 +22,7 @@ describe Gitlab::Geo::Fdw, :geo do ...@@ -22,7 +22,7 @@ describe Gitlab::Geo::Fdw, :geo do
expect(described_class.enabled?).to be_truthy expect(described_class.enabled?).to be_truthy
end 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) allow(Rails.configuration).to receive(:geo_database).and_return('fdw' => false)
expect(described_class.enabled?).to be_falsey expect(described_class.enabled?).to be_falsey
...@@ -38,67 +38,14 @@ describe Gitlab::Geo::Fdw, :geo do ...@@ -38,67 +38,14 @@ describe Gitlab::Geo::Fdw, :geo do
describe '.gitlab_tables' do describe '.gitlab_tables' do
it 'excludes pg_ tables' do it 'excludes pg_ tables' do
tables = described_class.gitlab_tables
ActiveRecord::Base.connection.create_table(:pg_gitlab_test) 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) ActiveRecord::Base.connection.drop_table(:pg_gitlab_test)
end end
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 describe 'fdw_up_to_date?' do
context 'with mocked FDW environment' do context 'with mocked FDW environment' do
it 'returns true when FDW is enabled and foreign schema has same tables as secondary database' 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 ...@@ -129,18 +76,6 @@ describe Gitlab::Geo::Fdw, :geo do
end end
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 describe 'count_tables' do
context 'with functional FDW environment' do context 'with functional FDW environment' do
it 'returns same amount as defined in schema migration' do it 'returns same amount as defined in schema migration' do
...@@ -151,40 +86,4 @@ describe Gitlab::Geo::Fdw, :geo do ...@@ -151,40 +86,4 @@ describe Gitlab::Geo::Fdw, :geo do
end end
end 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 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