summaryrefslogtreecommitdiff
path: root/lib/gitlab/database/concurrent_reindex.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/database/concurrent_reindex.rb')
-rw-r--r--lib/gitlab/database/concurrent_reindex.rb143
1 files changed, 0 insertions, 143 deletions
diff --git a/lib/gitlab/database/concurrent_reindex.rb b/lib/gitlab/database/concurrent_reindex.rb
deleted file mode 100644
index 485ab35e55d..00000000000
--- a/lib/gitlab/database/concurrent_reindex.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Database
- class ConcurrentReindex
- include Gitlab::Utils::StrongMemoize
- include MigrationHelpers
-
- ReindexError = Class.new(StandardError)
-
- PG_IDENTIFIER_LENGTH = 63
- TEMPORARY_INDEX_PREFIX = 'tmp_reindex_'
- REPLACED_INDEX_PREFIX = 'old_reindex_'
-
- attr_reader :index_name, :logger
-
- def initialize(index_name, logger:)
- @index_name = index_name
- @logger = logger
- end
-
- def execute
- raise ReindexError, "index #{index_name} does not exist" unless index_exists?
-
- raise ReindexError, 'UNIQUE indexes are currently not supported' if index_unique?
-
- logger.debug("dropping dangling index from previous run: #{replacement_index_name}")
- remove_replacement_index
-
- begin
- create_replacement_index
-
- unless replacement_index_valid?
- message = 'replacement index was created as INVALID'
- logger.error("#{message}, cleaning up")
- raise ReindexError, "failed to reindex #{index_name}: #{message}"
- end
-
- swap_replacement_index
- rescue Gitlab::Database::WithLockRetries::AttemptsExhaustedError => e
- logger.error('failed to obtain the required database locks to swap the indexes, cleaning up')
- raise ReindexError, e.message
- rescue ActiveRecord::ActiveRecordError, PG::Error => e
- logger.error("database error while attempting reindex of #{index_name}: #{e.message}")
- raise ReindexError, e.message
- ensure
- logger.info("dropping unneeded replacement index: #{replacement_index_name}")
- remove_replacement_index
- end
- end
-
- private
-
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
-
- def replacement_index_name
- @replacement_index_name ||= constrained_index_name(TEMPORARY_INDEX_PREFIX)
- end
-
- def index
- strong_memoize(:index) do
- find_index(index_name)
- end
- end
-
- def index_exists?
- !index.nil?
- end
-
- def index_unique?
- index.indisunique
- end
-
- def constrained_index_name(prefix)
- "#{prefix}#{index_name}".slice(0, PG_IDENTIFIER_LENGTH)
- end
-
- def create_replacement_index
- create_replacement_index_statement = index.indexdef
- .sub(/CREATE INDEX/, 'CREATE INDEX CONCURRENTLY')
- .sub(/#{index_name}/, replacement_index_name)
-
- logger.info("creating replacement index #{replacement_index_name}")
- logger.debug("replacement index definition: #{create_replacement_index_statement}")
-
- disable_statement_timeout do
- connection.execute(create_replacement_index_statement)
- end
- end
-
- def replacement_index_valid?
- find_index(replacement_index_name).indisvalid
- end
-
- def find_index(index_name)
- record = connection.select_one(<<~SQL)
- SELECT
- pg_index.indisunique,
- pg_index.indisvalid,
- pg_indexes.indexdef
- FROM pg_index
- INNER JOIN pg_class ON pg_class.oid = pg_index.indexrelid
- INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
- INNER JOIN pg_indexes ON pg_class.relname = pg_indexes.indexname
- WHERE pg_namespace.nspname = 'public'
- AND pg_class.relname = #{connection.quote(index_name)}
- SQL
-
- OpenStruct.new(record) if record
- end
-
- def swap_replacement_index
- replaced_index_name = constrained_index_name(REPLACED_INDEX_PREFIX)
-
- logger.info("swapping replacement index #{replacement_index_name} with #{index_name}")
-
- with_lock_retries do
- rename_index(index_name, replaced_index_name)
- rename_index(replacement_index_name, index_name)
- rename_index(replaced_index_name, replacement_index_name)
- end
- end
-
- def rename_index(old_index_name, new_index_name)
- connection.execute("ALTER INDEX #{old_index_name} RENAME TO #{new_index_name}")
- end
-
- def remove_replacement_index
- disable_statement_timeout do
- connection.execute("DROP INDEX CONCURRENTLY IF EXISTS #{replacement_index_name}")
- end
- end
-
- def with_lock_retries(&block)
- arguments = { klass: self.class, logger: logger }
-
- Gitlab::Database::WithLockRetries.new(arguments).run(raise_on_exhaustion: true, &block)
- end
- end
- end
-end