summaryrefslogtreecommitdiff
path: root/lib/gitlab/database
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/database')
-rw-r--r--lib/gitlab/database/batch_count.rb25
-rw-r--r--lib/gitlab/database/concurrent_reindex.rb143
-rw-r--r--lib/gitlab/database/count/reltuples_count_strategy.rb3
-rw-r--r--lib/gitlab/database/migration_helpers.rb37
-rw-r--r--lib/gitlab/database/partitioning/partition_creator.rb4
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb2
-rw-r--r--lib/gitlab/database/postgres_index.rb31
-rw-r--r--lib/gitlab/database/reindexing.rb22
-rw-r--r--lib/gitlab/database/reindexing/concurrent_reindex.rb120
-rw-r--r--lib/gitlab/database/reindexing/reindex_action.rb35
-rw-r--r--lib/gitlab/database/schema_helpers.rb17
-rw-r--r--lib/gitlab/database/similarity_score.rb11
-rw-r--r--lib/gitlab/database/with_lock_retries.rb8
13 files changed, 279 insertions, 179 deletions
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 1762b81b7d8..0de67ed8cf0 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -8,15 +8,20 @@
# In order to not use a possible complex time consuming query when calculating min and max for batch_distinct_count
# the start and finish can be sent specifically
#
+# Grouped relations can be used as well. However, the preferred batch count should be around 10K because group by count is more expensive.
+#
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
#
# Examples:
# extend ::Gitlab::Database::BatchCount
# batch_count(User.active)
# batch_count(::Clusters::Cluster.aws_installed.enabled, :cluster_id)
+# batch_count(Namespace.group(:type))
# batch_distinct_count(::Project, :creator_id)
# batch_distinct_count(::Project.with_active_services.service_desk_enabled.where(time_period), start: ::User.minimum(:id), finish: ::User.maximum(:id))
+# batch_distinct_count(Project.group(:visibility_level), :creator_id)
# batch_sum(User, :sign_in_count)
+# batch_sum(Issue.group(:state_id), :weight))
module Gitlab
module Database
module BatchCount
@@ -77,12 +82,12 @@ module Gitlab
raise "Batch counting expects positive values only for #{@column}" if start < 0 || finish < 0
return FALLBACK if unwanted_configuration?(finish, batch_size, start)
- counter = 0
+ results = nil
batch_start = start
while batch_start <= finish
begin
- counter += batch_fetch(batch_start, batch_start + batch_size, mode)
+ results = merge_results(results, batch_fetch(batch_start, batch_start + batch_size, mode))
batch_start += batch_size
rescue ActiveRecord::QueryCanceled
# retry with a safe batch size & warmer cache
@@ -95,7 +100,17 @@ module Gitlab
sleep(SLEEP_TIME_IN_SECONDS)
end
- counter
+ results
+ end
+
+ def merge_results(results, object)
+ return object unless results
+
+ if object.is_a?(Hash)
+ results.merge!(object) { |_, a, b| a + b }
+ else
+ results + object
+ end
end
def batch_fetch(start, finish, mode)
@@ -118,11 +133,11 @@ module Gitlab
end
def actual_start(start)
- start || @relation.minimum(@column) || 0
+ start || @relation.unscope(:group, :having).minimum(@column) || 0
end
def actual_finish(finish)
- finish || @relation.maximum(@column) || 0
+ finish || @relation.unscope(:group, :having).maximum(@column) || 0
end
def check_mode!(mode)
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
diff --git a/lib/gitlab/database/count/reltuples_count_strategy.rb b/lib/gitlab/database/count/reltuples_count_strategy.rb
index e226ed7613a..89190320cf9 100644
--- a/lib/gitlab/database/count/reltuples_count_strategy.rb
+++ b/lib/gitlab/database/count/reltuples_count_strategy.rb
@@ -74,8 +74,9 @@ module Gitlab
def get_statistics(table_names, check_statistics: true)
time = 6.hours.ago
- query = PgClass.joins("LEFT JOIN pg_stat_user_tables USING (relname)")
+ query = PgClass.joins("LEFT JOIN pg_stat_user_tables ON pg_stat_user_tables.relid = pg_class.oid")
.where(relname: table_names)
+ .where('schemaname = current_schema()')
.select('pg_class.relname AS table_name, reltuples::bigint AS estimate')
if check_statistics
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 723f0f6a308..4e2e1eaf21c 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -176,7 +176,7 @@ module Gitlab
name: name.presence || concurrent_foreign_key_name(source, column)
}
- if foreign_key_exists?(source, target, options)
+ if foreign_key_exists?(source, target, **options)
warning_message = "Foreign key not created because it exists already " \
"(this may be due to an aborted migration or similar): " \
"source: #{source}, target: #{target}, column: #{options[:column]}, "\
@@ -330,13 +330,13 @@ module Gitlab
# * +timing_configuration+ - [[ActiveSupport::Duration, ActiveSupport::Duration], ...] lock timeout for the block, sleep time before the next iteration, defaults to `Gitlab::Database::WithLockRetries::DEFAULT_TIMING_CONFIGURATION`
# * +logger+ - [Gitlab::JsonLogger]
# * +env+ - [Hash] custom environment hash, see the example with `DISABLE_LOCK_RETRIES`
- def with_lock_retries(**args, &block)
+ def with_lock_retries(*args, **kwargs, &block)
merged_args = {
klass: self.class,
logger: Gitlab::BackgroundMigration::Logger
- }.merge(args)
+ }.merge(kwargs)
- Gitlab::Database::WithLockRetries.new(merged_args).run(&block)
+ Gitlab::Database::WithLockRetries.new(**merged_args).run(&block)
end
def true_value
@@ -882,7 +882,7 @@ module Gitlab
# column.
opclasses[new] = opclasses.delete(old) if opclasses[old]
- options[:opclasses] = opclasses
+ options[:opclass] = opclasses
end
add_concurrent_index(table, new_columns, options)
@@ -994,10 +994,10 @@ into similar problems in the future (e.g. when new tables are created).
def postgres_exists_by_name?(table, name)
index_sql = <<~SQL
SELECT COUNT(*)
- FROM pg_index
- JOIN pg_class i ON (indexrelid=i.oid)
- JOIN pg_class t ON (indrelid=t.oid)
- WHERE i.relname = '#{name}' AND t.relname = '#{table}'
+ FROM pg_catalog.pg_indexes
+ WHERE schemaname = #{connection.quote(current_schema)}
+ AND tablename = #{connection.quote(table)}
+ AND indexname = #{connection.quote(name)}
SQL
connection.select_value(index_sql).to_i > 0
@@ -1053,11 +1053,15 @@ into similar problems in the future (e.g. when new tables are created).
# the table name in addition to using the constraint_name
check_sql = <<~SQL
SELECT COUNT(*)
- FROM pg_constraint
- JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
- WHERE pg_constraint.contype = 'c'
- AND pg_constraint.conname = '#{constraint_name}'
- AND pg_class.relname = '#{table}'
+ FROM pg_catalog.pg_constraint con
+ INNER JOIN pg_catalog.pg_class rel
+ ON rel.oid = con.conrelid
+ INNER JOIN pg_catalog.pg_namespace nsp
+ ON nsp.oid = con.connamespace
+ WHERE con.contype = 'c'
+ AND con.conname = #{connection.quote(constraint_name)}
+ AND nsp.nspname = #{connection.quote(current_schema)}
+ AND rel.relname = #{connection.quote(table)}
SQL
connection.select_value(check_sql) > 0
@@ -1284,8 +1288,9 @@ into similar problems in the future (e.g. when new tables are created).
check_sql = <<~SQL
SELECT c.is_nullable
FROM information_schema.columns c
- WHERE c.table_name = '#{table}'
- AND c.column_name = '#{column}'
+ WHERE c.table_schema = #{connection.quote(current_schema)}
+ AND c.table_name = #{connection.quote(table)}
+ AND c.column_name = #{connection.quote(column)}
SQL
connection.select_value(check_sql) == 'YES'
diff --git a/lib/gitlab/database/partitioning/partition_creator.rb b/lib/gitlab/database/partitioning/partition_creator.rb
index 4c1b13fe3b5..547e0b9b957 100644
--- a/lib/gitlab/database/partitioning/partition_creator.rb
+++ b/lib/gitlab/database/partitioning/partition_creator.rb
@@ -72,10 +72,10 @@ module Gitlab
end
def with_lock_retries(&block)
- Gitlab::Database::WithLockRetries.new({
+ Gitlab::Database::WithLockRetries.new(
klass: self.class,
logger: Gitlab::AppLogger
- }).run(&block)
+ ).run(&block)
end
def connection
diff --git a/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb b/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb
index f9ad1e60776..17a42d997e6 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb
@@ -11,8 +11,6 @@ module Gitlab
PAUSE_SECONDS = 0.25
def perform(start_id, stop_id, source_table, partitioned_table, source_column)
- return unless Feature.enabled?(:backfill_partitioned_audit_events, default_enabled: true)
-
if transaction_open?
raise "Aborting job to backfill partitioned #{source_table} table! Do not run this job in a transaction block!"
end
diff --git a/lib/gitlab/database/postgres_index.rb b/lib/gitlab/database/postgres_index.rb
new file mode 100644
index 00000000000..2a9f23f0098
--- /dev/null
+++ b/lib/gitlab/database/postgres_index.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ class PostgresIndex < ActiveRecord::Base
+ self.table_name = 'postgres_indexes'
+ self.primary_key = 'identifier'
+
+ scope :by_identifier, ->(identifier) do
+ raise ArgumentError, "Index name is not fully qualified with a schema: #{identifier}" unless identifier =~ /^\w+\.\w+$/
+
+ find(identifier)
+ end
+
+ # A 'regular' index is a non-unique index,
+ # that does not serve an exclusion constraint and
+ # is defined on a table that is not partitioned.
+ scope :regular, -> { where(unique: false, partitioned: false, exclusion: false)}
+
+ scope :random_few, ->(how_many) do
+ limit(how_many).order(Arel.sql('RANDOM()'))
+ end
+
+ scope :not_match, ->(regex) { where("name !~ ?", regex)}
+
+ def to_s
+ name
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/reindexing.rb b/lib/gitlab/database/reindexing.rb
new file mode 100644
index 00000000000..baffe28d9ed
--- /dev/null
+++ b/lib/gitlab/database/reindexing.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Reindexing
+ def self.perform(index_selector)
+ Array.wrap(index_selector).each do |index|
+ ReindexAction.keep_track_of(index) do
+ ConcurrentReindex.new(index).perform
+ end
+ end
+ end
+
+ def self.candidate_indexes
+ Gitlab::Database::PostgresIndex
+ .regular
+ .not_match("^#{ConcurrentReindex::TEMPORARY_INDEX_PREFIX}")
+ .not_match("^#{ConcurrentReindex::REPLACED_INDEX_PREFIX}")
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/reindexing/concurrent_reindex.rb b/lib/gitlab/database/reindexing/concurrent_reindex.rb
new file mode 100644
index 00000000000..89fab4a183c
--- /dev/null
+++ b/lib/gitlab/database/reindexing/concurrent_reindex.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Reindexing
+ 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, :logger
+
+ def initialize(index, logger: Gitlab::AppLogger)
+ @index = index
+ @logger = logger
+ end
+
+ def perform
+ raise ReindexError, 'UNIQUE indexes are currently not supported' if index.unique?
+ raise ReindexError, 'partitioned indexes are currently not supported' if index.partitioned?
+ raise ReindexError, 'indexes serving an exclusion constraint are currently not supported' if index.exclusion?
+ raise ReindexError, 'index is a left-over temporary index from a previous reindexing run' if index.name.start_with?(TEMPORARY_INDEX_PREFIX, REPLACED_INDEX_PREFIX)
+
+ logger.info "Starting reindex of #{index}"
+
+ with_rebuilt_index do |replacement_index|
+ swap_index(replacement_index)
+ end
+ end
+
+ private
+
+ def with_rebuilt_index
+ if Gitlab::Database::PostgresIndex.find_by(schema: index.schema, name: replacement_index_name)
+ logger.debug("dropping dangling index from previous run (if it exists): #{replacement_index_name}")
+ remove_index(index.schema, replacement_index_name)
+ end
+
+ create_replacement_index_statement = index.definition
+ .sub(/CREATE INDEX #{index.name}/, "CREATE INDEX CONCURRENTLY #{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
+
+ replacement_index = Gitlab::Database::PostgresIndex.find_by(schema: index.schema, name: replacement_index_name)
+
+ unless replacement_index.valid_index?
+ message = 'replacement index was created as INVALID'
+ logger.error("#{message}, cleaning up")
+ raise ReindexError, "failed to reindex #{index}: #{message}"
+ end
+
+ yield replacement_index
+ ensure
+ begin
+ remove_index(index.schema, replacement_index_name)
+ rescue => e
+ logger.error(e)
+ end
+ end
+
+ def swap_index(replacement_index)
+ logger.info("swapping replacement index #{replacement_index} with #{index}")
+
+ with_lock_retries do
+ rename_index(index.schema, index.name, replaced_index_name)
+ rename_index(replacement_index.schema, replacement_index.name, index.name)
+ rename_index(index.schema, replaced_index_name, replacement_index.name)
+ end
+ end
+
+ def rename_index(schema, old_index_name, new_index_name)
+ connection.execute(<<~SQL)
+ ALTER INDEX #{quote_table_name(schema)}.#{quote_table_name(old_index_name)}
+ RENAME TO #{quote_table_name(new_index_name)}
+ SQL
+ end
+
+ def remove_index(schema, name)
+ logger.info("Removing index #{schema}.#{name}")
+
+ disable_statement_timeout do
+ connection.execute(<<~SQL)
+ DROP INDEX CONCURRENTLY
+ IF EXISTS #{quote_table_name(schema)}.#{quote_table_name(name)}
+ SQL
+ end
+ end
+
+ def replacement_index_name
+ @replacement_index_name ||= "#{TEMPORARY_INDEX_PREFIX}#{index.indexrelid}"
+ end
+
+ def replaced_index_name
+ @replaced_index_name ||= "#{REPLACED_INDEX_PREFIX}#{index.indexrelid}"
+ end
+
+ def with_lock_retries(&block)
+ arguments = { klass: self.class, logger: logger }
+
+ Gitlab::Database::WithLockRetries.new(**arguments).run(raise_on_exhaustion: true, &block)
+ end
+
+ delegate :execute, :quote_table_name, to: :connection
+ def connection
+ @connection ||= ActiveRecord::Base.connection
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/reindexing/reindex_action.rb b/lib/gitlab/database/reindexing/reindex_action.rb
new file mode 100644
index 00000000000..0928ef90e5d
--- /dev/null
+++ b/lib/gitlab/database/reindexing/reindex_action.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Reindexing
+ class ReindexAction < ActiveRecord::Base
+ self.table_name = 'postgres_reindex_actions'
+
+ enum state: { started: 0, finished: 1, failed: 2 }
+
+ def self.keep_track_of(index, &block)
+ action = create!(
+ index_identifier: index.identifier,
+ action_start: Time.zone.now,
+ ondisk_size_bytes_start: index.ondisk_size_bytes
+ )
+
+ yield
+
+ action.state = :finished
+ rescue
+ action.state = :failed
+ raise
+ ensure
+ index.reload # rubocop:disable Cop/ActiveRecordAssociationReload
+
+ action.action_end = Time.zone.now
+ action.ondisk_size_bytes_end = index.ondisk_size_bytes
+
+ action.save!
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/schema_helpers.rb b/lib/gitlab/database/schema_helpers.rb
index dda4d8eecdb..3d929c62933 100644
--- a/lib/gitlab/database/schema_helpers.rb
+++ b/lib/gitlab/database/schema_helpers.rb
@@ -32,11 +32,14 @@ module Gitlab
def trigger_exists?(table_name, name)
connection.select_value(<<~SQL)
SELECT 1
- FROM pg_trigger
- INNER JOIN pg_class
- ON pg_trigger.tgrelid = pg_class.oid
- WHERE pg_class.relname = '#{table_name}'
- AND pg_trigger.tgname = '#{name}'
+ FROM pg_catalog.pg_trigger trgr
+ INNER JOIN pg_catalog.pg_class rel
+ ON trgr.tgrelid = rel.oid
+ INNER JOIN pg_catalog.pg_namespace nsp
+ ON nsp.oid = rel.relnamespace
+ WHERE nsp.nspname = #{connection.quote(current_schema)}
+ AND rel.relname = #{connection.quote(table_name)}
+ AND trgr.tgname = #{connection.quote(name)}
SQL
end
@@ -68,10 +71,10 @@ module Gitlab
end
def with_lock_retries(&block)
- Gitlab::Database::WithLockRetries.new({
+ Gitlab::Database::WithLockRetries.new(
klass: self.class,
logger: Gitlab::BackgroundMigration::Logger
- }).run(&block)
+ ).run(&block)
end
def assert_not_in_transaction_block(scope:)
diff --git a/lib/gitlab/database/similarity_score.rb b/lib/gitlab/database/similarity_score.rb
index 2633c29438a..ff78fd0218c 100644
--- a/lib/gitlab/database/similarity_score.rb
+++ b/lib/gitlab/database/similarity_score.rb
@@ -6,6 +6,11 @@ module Gitlab
EMPTY_STRING = Arel.sql("''").freeze
EXPRESSION_ON_INVALID_INPUT = Arel::Nodes::NamedFunction.new('CAST', [Arel.sql('0').as('integer')]).freeze
DEFAULT_MULTIPLIER = 1
+ DISPLAY_NAME = self.name.underscore.freeze
+
+ # Adds a "magic" comment in the generated SQL expression in order to be able to tell if we're sorting by similarity.
+ # Example: /* gitlab/database/similarity_score */ SIMILARITY(COALESCE...
+ SIMILARITY_FUNCTION_CALL_WITH_ANNOTATION = "/* #{DISPLAY_NAME} */ SIMILARITY".freeze
# This method returns an Arel expression that can be used in an ActiveRecord query to order the resultset by similarity.
#
@@ -74,6 +79,10 @@ module Gitlab
end
end
+ def self.order_by_similarity?(arel_query)
+ arel_query.to_sql.include?(SIMILARITY_FUNCTION_CALL_WITH_ANNOTATION)
+ end
+
# (SIMILARITY(COALESCE(column, ''), 'search_string') * CAST(multiplier AS numeric))
def self.rule_to_arel(search, rule)
Arel::Nodes::Grouping.new(
@@ -91,7 +100,7 @@ module Gitlab
# SIMILARITY(COALESCE(column, ''), 'search_string')
def self.similarity_function_call(search, column)
- Arel::Nodes::NamedFunction.new('SIMILARITY', [column, Arel.sql(search)])
+ Arel::Nodes::NamedFunction.new(SIMILARITY_FUNCTION_CALL_WITH_ANNOTATION, [column, Arel.sql(search)])
end
# CAST(multiplier AS numeric)
diff --git a/lib/gitlab/database/with_lock_retries.rb b/lib/gitlab/database/with_lock_retries.rb
index a9c86e4e267..3fb52d786ad 100644
--- a/lib/gitlab/database/with_lock_retries.rb
+++ b/lib/gitlab/database/with_lock_retries.rb
@@ -95,7 +95,7 @@ module Gitlab
run_block_with_transaction
rescue ActiveRecord::LockWaitTimeout
if retry_with_lock_timeout?
- disable_idle_in_transaction_timeout
+ disable_idle_in_transaction_timeout if ActiveRecord::Base.connection.transaction_open?
wait_until_next_retry
reset_db_settings
@@ -149,7 +149,7 @@ module Gitlab
log(message: "Couldn't acquire lock to perform the migration", current_iteration: current_iteration)
log(message: "Executing the migration without lock timeout", current_iteration: current_iteration)
- execute("SET LOCAL lock_timeout TO '0'")
+ disable_lock_timeout if ActiveRecord::Base.connection.transaction_open?
run_block
@@ -184,6 +184,10 @@ module Gitlab
execute("SET LOCAL idle_in_transaction_session_timeout TO '0'")
end
+ def disable_lock_timeout
+ execute("SET LOCAL lock_timeout TO '0'")
+ end
+
def reset_db_settings
execute('RESET idle_in_transaction_session_timeout; RESET lock_timeout')
end