Commit 2d134c23 authored by pbair's avatar pbair

Update specs to better test index behavior in db

Update the specs to more thoroughly verify that the creation and removal
of indexes on partitioned works as expected across Postgres versions.
parent 9a58817b
...@@ -3,42 +3,45 @@ ...@@ -3,42 +3,45 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
include TableSchemaHelpers
let(:migration) do let(:migration) do
ActiveRecord::Migration.new.extend(described_class) ActiveRecord::Migration.new.extend(described_class)
end end
let(:table_name) { '_test_partitioned_table' } let(:table_name) { '_test_partitioned_table' }
let(:column_name) { 'created_at' }
let(:index_name) { '_test_partitioning_index_name' } let(:index_name) { '_test_partitioning_index_name' }
let(:partitioned_table) { double(name: 'partitioned_table') } let(:partition_schema) { 'gitlab_partitions_dynamic' }
let(:partition1_identifier) { "#{partition_schema}.#{table_name}_202001" }
let(:partition2_identifier) { "#{partition_schema}.#{table_name}_202002" }
let(:partition1_index) { "index_#{table_name}_202001_#{column_name}" }
let(:partition2_index) { "index_#{table_name}_202002_#{column_name}" }
before do before do
allow(migration).to receive(:puts) allow(migration).to receive(:puts)
allow(Gitlab::Database::PostgresPartitionedTable).to receive(:find_by_name_in_current_schema).with(table_name) connection.execute(<<~SQL)
.and_return(partitioned_table) CREATE TABLE #{table_name} (
end id serial NOT NULL,
created_at timestamptz NOT NULL,
describe '#add_concurrent_partitioned_index' do PRIMARY KEY (id, created_at)
let(:column_name) { '_test_column_name' } ) PARTITION BY RANGE (created_at);
let(:partition_name_1) { "#{table_name}_202001" }
let(:partition_name_2) { "#{table_name}_202002" }
let(:partition_name_3) { "#{table_name}_202003" }
let(:index_name_1) { '_test_index_name_1' } CREATE TABLE #{partition1_identifier} PARTITION OF #{table_name}
let(:index_name_2) { '_test_index_name_2' } FOR VALUES FROM ('2020-01-01') TO ('2020-02-01');
let(:index_name_3) { '_test_index_name_3' }
let(:partition_1) { double(identifier: partition_name_1) } CREATE TABLE #{partition2_identifier} PARTITION OF #{table_name}
let(:partition_2) { double(identifier: partition_name_2) } FOR VALUES FROM ('2020-02-01') TO ('2020-03-01');
let(:partition_3) { double(identifier: partition_name_3) } SQL
let(:current_partitions) { [partition_1, partition_2, partition_3] } end
describe '#add_concurrent_partitioned_index' do
before do before do
allow(partitioned_table).to receive(:postgres_partitions).and_return(current_partitions)
allow(migration).to receive(:generated_index_name).and_return(index_name_1, index_name_2, index_name_3)
allow(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(false) allow(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(false)
allow(migration).to receive(:generated_index_name).and_return(partition1_index, partition2_index)
allow(migration).to receive(:with_lock_retries).and_yield allow(migration).to receive(:with_lock_retries).and_yield
end end
...@@ -46,17 +49,22 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -46,17 +49,22 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
it 'creates the index on each partition, and the parent table', :aggregate_failures do it 'creates the index on each partition, and the parent table', :aggregate_failures do
expect(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(false) expect(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(false)
expect(migration).to receive(:add_concurrent_index) expect_add_concurrent_index_and_call_original(partition1_identifier, column_name, partition1_index)
.with(partition_name_1, column_name, name: index_name_1).ordered expect_add_concurrent_index_and_call_original(partition2_identifier, column_name, partition2_index)
expect(migration).to receive(:add_concurrent_index)
.with(partition_name_2, column_name, name: index_name_2).ordered
expect(migration).to receive(:add_concurrent_index)
.with(partition_name_3, column_name, name: index_name_3).ordered
expect(migration).to receive(:with_lock_retries).and_yield.ordered expect(migration).to receive(:with_lock_retries).ordered.and_yield
expect(migration).to receive(:add_index).with(table_name, column_name, name: index_name).ordered expect(migration).to receive(:add_index).with(table_name, column_name, name: index_name).ordered.and_call_original
migration.add_concurrent_partitioned_index(table_name, column_name, name: index_name) migration.add_concurrent_partitioned_index(table_name, column_name, name: index_name)
expect_index_to_exist(partition1_index, schema: partition_schema)
expect_index_to_exist(partition2_index, schema: partition_schema)
expect_index_to_exist(index_name)
end
def expect_add_concurrent_index_and_call_original(table, column, index)
expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, name: index)
.and_wrap_original { |_, table, column, options| connection.add_index(table, column, options) }
end end
end end
...@@ -73,11 +81,15 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -73,11 +81,15 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end end
context 'when additional index options are given' do context 'when additional index options are given' do
let(:current_partitions) { [partition_1] } before do
connection.execute(<<~SQL)
DROP TABLE #{partition2_identifier}
SQL
end
it 'forwards them to the index helper methods', :aggregate_failures do it 'forwards them to the index helper methods', :aggregate_failures do
expect(migration).to receive(:add_concurrent_index) expect(migration).to receive(:add_concurrent_index)
.with(partition_name_1, column_name, name: index_name_1, where: 'x > 0', unique: true) .with(partition1_identifier, column_name, name: partition1_index, where: 'x > 0', unique: true)
expect(migration).to receive(:add_index) expect(migration).to receive(:add_index)
.with(table_name, column_name, name: index_name, where: 'x > 0', unique: true) .with(table_name, column_name, name: index_name, where: 'x > 0', unique: true)
...@@ -90,6 +102,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -90,6 +102,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
context 'when a name argument for the index is not given' do context 'when a name argument for the index is not given' do
it 'raises an error', :aggregate_failures do it 'raises an error', :aggregate_failures do
expect(migration).not_to receive(:add_concurrent_index) expect(migration).not_to receive(:add_concurrent_index)
expect(migration).not_to receive(:with_lock_retries)
expect(migration).not_to receive(:add_index) expect(migration).not_to receive(:add_index)
expect do expect do
...@@ -106,6 +119,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -106,6 +119,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
it 'raises an error', :aggregate_failures do it 'raises an error', :aggregate_failures do
expect(migration).not_to receive(:add_concurrent_index) expect(migration).not_to receive(:add_concurrent_index)
expect(migration).not_to receive(:with_lock_retries)
expect(migration).not_to receive(:add_index) expect(migration).not_to receive(:add_index)
expect do expect do
...@@ -116,19 +130,29 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -116,19 +130,29 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end end
describe '#remove_concurrent_partitioned_index_by_name' do describe '#remove_concurrent_partitioned_index_by_name' do
before do
allow(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(true)
allow(migration).to receive(:with_lock_retries).and_yield
end
context 'when the index exists' do context 'when the index exists' do
it 'drops the index on the parent table', :aggregate_failures do before do
expect(migration).to receive(:index_name_exists?).with(table_name, index_name).and_return(true) connection.execute(<<~SQL)
CREATE INDEX #{partition1_index} ON #{partition1_identifier} (#{column_name});
CREATE INDEX #{partition2_index} ON #{partition2_identifier} (#{column_name});
CREATE INDEX #{index_name} ON #{table_name} (#{column_name});
SQL
end
expect(migration).to receive(:with_lock_retries).and_yield.ordered it 'drops the index on the parent table, cascading to all partitions', :aggregate_failures do
expect(migration).to receive(:remove_index).with(table_name, name: index_name).ordered expect_index_to_exist(partition1_index, schema: partition_schema)
expect_index_to_exist(partition2_index, schema: partition_schema)
expect_index_to_exist(index_name)
expect(migration).to receive(:with_lock_retries).ordered.and_yield
expect(migration).to receive(:remove_index).with(table_name, name: index_name).ordered.and_call_original
migration.remove_concurrent_partitioned_index_by_name(table_name, index_name) migration.remove_concurrent_partitioned_index_by_name(table_name, index_name)
expect_index_not_to_exist(partition1_index, schema: partition_schema)
expect_index_not_to_exist(partition2_index, schema: partition_schema)
expect_index_not_to_exist(index_name)
end end
end end
...@@ -150,6 +174,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do ...@@ -150,6 +174,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end end
it 'raises an error', :aggregate_failures do it 'raises an error', :aggregate_failures do
expect(migration).not_to receive(:with_lock_retries)
expect(migration).not_to receive(:remove_index) expect(migration).not_to receive(:remove_index)
expect do expect do
......
...@@ -16,6 +16,14 @@ module TableSchemaHelpers ...@@ -16,6 +16,14 @@ module TableSchemaHelpers
expect(table_oid(replacement_table)).to be_nil expect(table_oid(replacement_table)).to be_nil
end end
def expect_index_to_exist(name, schema: nil)
expect(index_exists_by_name(name, schema: schema)).to eq(true)
end
def expect_index_not_to_exist(name, schema: nil)
expect(index_exists_by_name(name, schema: schema)).to be_nil
end
def table_oid(name) def table_oid(name)
connection.select_value(<<~SQL) connection.select_value(<<~SQL)
SELECT oid SELECT oid
...@@ -76,4 +84,19 @@ module TableSchemaHelpers ...@@ -76,4 +84,19 @@ module TableSchemaHelpers
AND contype = 'p' AND contype = 'p'
SQL SQL
end end
def index_exists_by_name(index, schema: nil)
schema = schema ? "'#{schema}'" : 'current_schema'
connection.select_value(<<~SQL)
SELECT true
FROM pg_catalog.pg_index i
INNER JOIN pg_catalog.pg_class c
ON c.oid = i.indexrelid
INNER JOIN pg_catalog.pg_namespace n
ON c.relnamespace = n.oid
WHERE c.relname = '#{index}'
AND n.nspname = #{schema}
SQL
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