Commit 317f2917 authored by Andreas Brandl's avatar Andreas Brandl

Rebuild statistics for expression indexes

Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/272997
parent 801ce791
......@@ -59,6 +59,13 @@ module Gitlab
raise ReindexError, "failed to reindex #{index}: #{message}"
end
# Some expression indexes (aka functional indexes)
# require additional statistics. The existing statistics
# are tightly bound to the original index. We have to
# rebuild statistics for the new index before dropping
# the original one.
rebuild_statistics if index.expression?
yield replacement_index
ensure
begin
......@@ -96,6 +103,14 @@ module Gitlab
end
end
def rebuild_statistics
logger.info("rebuilding table statistics for #{index.schema}.#{index.tablename}")
connection.execute(<<~SQL)
ANALYZE #{quote_table_name(index.schema)}.#{quote_table_name(index.tablename)}
SQL
end
def replacement_index_name
@replacement_index_name ||= "#{TEMPORARY_INDEX_PREFIX}#{index.indexrelid}"
end
......
......@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Database::Reindexing::ConcurrentReindex, '#perform' do
let(:table_name) { '_test_reindex_table' }
let(:column_name) { '_test_column' }
let(:index_name) { '_test_reindex_index' }
let(:index) { instance_double(Gitlab::Database::PostgresIndex, indexrelid: 42, name: index_name, schema: 'public', partitioned?: false, unique?: false, exclusion?: false, definition: 'CREATE INDEX _test_reindex_index ON public._test_reindex_table USING btree (_test_column)') }
let(:index) { instance_double(Gitlab::Database::PostgresIndex, indexrelid: 42, name: index_name, schema: 'public', tablename: table_name, partitioned?: false, unique?: false, exclusion?: false, expression?: false, definition: 'CREATE INDEX _test_reindex_index ON public._test_reindex_table USING btree (_test_column)') }
let(:logger) { double('logger', debug: nil, info: nil, error: nil ) }
let(:connection) { ActiveRecord::Base.connection }
......@@ -130,6 +130,36 @@ RSpec.describe Gitlab::Database::Reindexing::ConcurrentReindex, '#perform' do
check_index_exists
end
context 'for expression indexes' do
before do
allow(index).to receive(:expression?).and_return(true)
end
it 'rebuilds table statistics before dropping the original index' do
expect(connection).to receive(:execute).with('SET statement_timeout TO \'21600s\'').twice
expect_to_execute_concurrently_in_order(create_index)
expect_to_execute_concurrently_in_order(<<~SQL)
ANALYZE "#{index.schema}"."#{index.tablename}"
SQL
expect_next_instance_of(::Gitlab::Database::WithLockRetries) do |instance|
expect(instance).to receive(:run).with(raise_on_exhaustion: true).and_yield
end
expect_index_rename(index.name, replaced_name)
expect_index_rename(replacement_name, index.name)
expect_index_rename(replaced_name, replacement_name)
expect_to_execute_concurrently_in_order(drop_index)
subject.perform
check_index_exists
end
end
context 'when a dangling index is left from a previous run' do
before do
connection.execute("CREATE INDEX #{replacement_name} ON #{table_name} (#{column_name})")
......
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