summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2019-07-01 14:37:18 +0100
committerNick Thomas <nick@gitlab.com>2019-07-02 09:35:03 +0100
commit351392f4090ee7e8fe5c4f47286afe6da14b1895 (patch)
tree64fa15613b0e862847eb71395f000e1467d371a0 /lib
parent876d4151167d237fda853585442d9a8e9681c23e (diff)
downloadgitlab-ce-351392f4090ee7e8fe5c4f47286afe6da14b1895.tar.gz
Remove background migrations for old schemas
On the assumption that a background migration whose specs need a schema older than 2018 is obsoleted by this migration squash, we can remove both specs and code for those that fail to run in CI as a result of the schema at that date no longer existing. This is true for all but the MigrateStageStatus background migration, which is also used from the MigrateBuildStage background migration.
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/background_migration/create_fork_network_memberships_range.rb85
-rw-r--r--lib/gitlab/background_migration/delete_conflicting_redirect_routes_range.rb13
-rw-r--r--lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb179
-rw-r--r--lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb319
-rw-r--r--lib/gitlab/background_migration/populate_fork_networks_range.rb128
-rw-r--r--lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id.rb33
6 files changed, 0 insertions, 757 deletions
diff --git a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
deleted file mode 100644
index ccd1f9b4dba..00000000000
--- a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class CreateForkNetworkMembershipsRange
- RESCHEDULE_DELAY = 15
-
- class ForkedProjectLink < ActiveRecord::Base
- self.table_name = 'forked_project_links'
- end
-
- def perform(start_id, end_id)
- log("Creating memberships for forks: #{start_id} - #{end_id}")
-
- insert_members(start_id, end_id)
-
- if missing_members?(start_id, end_id)
- BackgroundMigrationWorker.perform_in(RESCHEDULE_DELAY, "CreateForkNetworkMembershipsRange", [start_id, end_id])
- end
- end
-
- def insert_members(start_id, end_id)
- ActiveRecord::Base.connection.execute <<~INSERT_MEMBERS
- INSERT INTO fork_network_members (fork_network_id, project_id, forked_from_project_id)
-
- SELECT fork_network_members.fork_network_id,
- forked_project_links.forked_to_project_id,
- forked_project_links.forked_from_project_id
-
- FROM forked_project_links
-
- INNER JOIN fork_network_members
- ON forked_project_links.forked_from_project_id = fork_network_members.project_id
-
- WHERE forked_project_links.id BETWEEN #{start_id} AND #{end_id}
- AND NOT EXISTS (
- SELECT true
- FROM fork_network_members existing_members
- WHERE existing_members.project_id = forked_project_links.forked_to_project_id
- )
- INSERT_MEMBERS
- rescue ActiveRecord::RecordNotUnique => e
- # `fork_network_member` was created concurrently in another migration
- log(e.message)
- end
-
- def missing_members?(start_id, end_id)
- count_sql = <<~MISSING_MEMBERS
- SELECT COUNT(*)
-
- FROM forked_project_links
-
- WHERE NOT EXISTS (
- SELECT true
- FROM fork_network_members
- WHERE fork_network_members.project_id = forked_project_links.forked_to_project_id
- )
- AND EXISTS (
- SELECT true
- FROM projects
- WHERE forked_project_links.forked_from_project_id = projects.id
- )
- AND NOT EXISTS (
- SELECT true
- FROM forked_project_links AS parent_links
- WHERE parent_links.forked_to_project_id = forked_project_links.forked_from_project_id
- AND NOT EXISTS (
- SELECT true
- FROM projects
- WHERE parent_links.forked_from_project_id = projects.id
- )
- )
- AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
- MISSING_MEMBERS
-
- ForkedProjectLink.count_by_sql(count_sql) > 0
- end
-
- def log(message)
- Rails.logger.info("#{self.class.name} - #{message}")
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range.rb b/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range.rb
deleted file mode 100644
index 21b626dde56..00000000000
--- a/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class DeleteConflictingRedirectRoutesRange
- def perform(start_id, end_id)
- # No-op.
- # See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb b/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
deleted file mode 100644
index 42fcaa87e66..00000000000
--- a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
+++ /dev/null
@@ -1,179 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- # Class that migrates events for the new push event payloads setup. All
- # events are copied to a shadow table, and push events will also have a row
- # created in the push_event_payloads table.
- class MigrateEventsToPushEventPayloads
- class Event < ActiveRecord::Base
- self.table_name = 'events'
-
- serialize :data
-
- BLANK_REF = ('0' * 40).freeze
- TAG_REF_PREFIX = 'refs/tags/'.freeze
- MAX_INDEX = 69
- PUSHED = 5
-
- def push_event?
- action == PUSHED && data.present?
- end
-
- def commit_title
- commit = commits.last
-
- return unless commit && commit[:message]
-
- index = commit[:message].index("\n")
- message = index ? commit[:message][0..index] : commit[:message]
-
- message.strip.truncate(70)
- end
-
- def commit_from_sha
- if create?
- nil
- else
- data[:before]
- end
- end
-
- def commit_to_sha
- if remove?
- nil
- else
- data[:after]
- end
- end
-
- def data
- super || {}
- end
-
- def commits
- data[:commits] || []
- end
-
- def commit_count
- data[:total_commits_count] || 0
- end
-
- def ref
- data[:ref]
- end
-
- def trimmed_ref_name
- if ref_type == :tag
- ref[10..-1]
- else
- ref[11..-1]
- end
- end
-
- def create?
- data[:before] == BLANK_REF
- end
-
- def remove?
- data[:after] == BLANK_REF
- end
-
- def push_action
- if create?
- :created
- elsif remove?
- :removed
- else
- :pushed
- end
- end
-
- def ref_type
- if ref.start_with?(TAG_REF_PREFIX)
- :tag
- else
- :branch
- end
- end
- end
-
- class EventForMigration < ActiveRecord::Base
- self.table_name = 'events_for_migration'
- end
-
- class PushEventPayload < ActiveRecord::Base
- self.table_name = 'push_event_payloads'
-
- enum action: {
- created: 0,
- removed: 1,
- pushed: 2
- }
-
- enum ref_type: {
- branch: 0,
- tag: 1
- }
- end
-
- # start_id - The start ID of the range of events to process
- # end_id - The end ID of the range to process.
- def perform(start_id, end_id)
- return unless migrate?
-
- find_events(start_id, end_id).each { |event| process_event(event) }
- end
-
- def process_event(event)
- ActiveRecord::Base.transaction do
- replicate_event(event)
- create_push_event_payload(event) if event.push_event?
- end
- rescue ActiveRecord::InvalidForeignKey => e
- # A foreign key error means the associated event was removed. In this
- # case we'll just skip migrating the event.
- Rails.logger.error("Unable to migrate event #{event.id}: #{e}")
- end
-
- def replicate_event(event)
- new_attributes = event.attributes
- .with_indifferent_access.except(:title, :data)
-
- EventForMigration.create!(new_attributes)
- end
-
- def create_push_event_payload(event)
- commit_from = pack(event.commit_from_sha)
- commit_to = pack(event.commit_to_sha)
-
- PushEventPayload.create!(
- event_id: event.id,
- commit_count: event.commit_count,
- ref_type: event.ref_type,
- action: event.push_action,
- commit_from: commit_from,
- commit_to: commit_to,
- ref: event.trimmed_ref_name,
- commit_title: event.commit_title
- )
- end
-
- def find_events(start_id, end_id)
- Event
- .where('NOT EXISTS (SELECT true FROM events_for_migration WHERE events_for_migration.id = events.id)')
- .where(id: start_id..end_id)
- end
-
- def migrate?
- Event.table_exists? && PushEventPayload.table_exists? &&
- EventForMigration.table_exists?
- end
-
- def pack(value)
- value ? [value].pack('H*') : nil
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
deleted file mode 100644
index 48aa369705f..00000000000
--- a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
+++ /dev/null
@@ -1,319 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/ClassLength
-# rubocop:disable Metrics/BlockLength
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class NormalizeLdapExternUidsRange
- class Identity < ActiveRecord::Base
- self.table_name = 'identities'
- end
-
- # Copied this class to make this migration resilient to future code changes.
- # And if the normalize behavior is changed in the future, it must be
- # accompanied by another migration.
- module Gitlab
- module Auth
- module LDAP
- class DN
- FormatError = Class.new(StandardError)
- MalformedError = Class.new(FormatError)
- UnsupportedError = Class.new(FormatError)
-
- def self.normalize_value(given_value)
- dummy_dn = "placeholder=#{given_value}"
- normalized_dn = new(*dummy_dn).to_normalized_s
- normalized_dn.sub(/\Aplaceholder=/, '')
- end
-
- ##
- # Initialize a DN, escaping as required. Pass in attributes in name/value
- # pairs. If there is a left over argument, it will be appended to the dn
- # without escaping (useful for a base string).
- #
- # Most uses of this class will be to escape a DN, rather than to parse it,
- # so storing the dn as an escaped String and parsing parts as required
- # with a state machine seems sensible.
- def initialize(*args)
- if args.length > 1
- initialize_array(args)
- else
- initialize_string(args[0])
- end
- end
-
- ##
- # Parse a DN into key value pairs using ASN from
- # http://tools.ietf.org/html/rfc2253 section 3.
- # rubocop:disable Metrics/AbcSize
- # rubocop:disable Metrics/CyclomaticComplexity
- # rubocop:disable Metrics/PerceivedComplexity
- def each_pair
- state = :key
- key = StringIO.new
- value = StringIO.new
- hex_buffer = ""
-
- @dn.each_char.with_index do |char, dn_index|
- case state
- when :key then
- case char
- when 'a'..'z', 'A'..'Z' then
- state = :key_normal
- key << char
- when '0'..'9' then
- state = :key_oid
- key << char
- when ' ' then state = :key
- else raise(MalformedError, "Unrecognized first character of an RDN attribute type name \"#{char}\"")
- end
- when :key_normal then
- case char
- when '=' then state = :value
- when 'a'..'z', 'A'..'Z', '0'..'9', '-', ' ' then key << char
- else raise(MalformedError, "Unrecognized RDN attribute type name character \"#{char}\"")
- end
- when :key_oid then
- case char
- when '=' then state = :value
- when '0'..'9', '.', ' ' then key << char
- else raise(MalformedError, "Unrecognized RDN OID attribute type name character \"#{char}\"")
- end
- when :value then
- case char
- when '\\' then state = :value_normal_escape
- when '"' then state = :value_quoted
- when ' ' then state = :value
- when '#' then
- state = :value_hexstring
- value << char
- when ',' then
- state = :key
- yield key.string.strip, rstrip_except_escaped(value.string, dn_index)
- key = StringIO.new
- value = StringIO.new
- else
- state = :value_normal
- value << char
- end
- when :value_normal then
- case char
- when '\\' then state = :value_normal_escape
- when ',' then
- state = :key
- yield key.string.strip, rstrip_except_escaped(value.string, dn_index)
- key = StringIO.new
- value = StringIO.new
- when '+' then raise(UnsupportedError, "Multivalued RDNs are not supported")
- else value << char
- end
- when :value_normal_escape then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_normal_escape_hex
- hex_buffer = char
- else
- state = :value_normal
- value << char
- end
- when :value_normal_escape_hex then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_normal
- value << "#{hex_buffer}#{char}".to_i(16).chr
- else raise(MalformedError, "Invalid escaped hex code \"\\#{hex_buffer}#{char}\"")
- end
- when :value_quoted then
- case char
- when '\\' then state = :value_quoted_escape
- when '"' then state = :value_end
- else value << char
- end
- when :value_quoted_escape then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_quoted_escape_hex
- hex_buffer = char
- else
- state = :value_quoted
- value << char
- end
- when :value_quoted_escape_hex then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_quoted
- value << "#{hex_buffer}#{char}".to_i(16).chr
- else raise(MalformedError, "Expected the second character of a hex pair inside a double quoted value, but got \"#{char}\"")
- end
- when :value_hexstring then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_hexstring_hex
- value << char
- when ' ' then state = :value_end
- when ',' then
- state = :key
- yield key.string.strip, rstrip_except_escaped(value.string, dn_index)
- key = StringIO.new
- value = StringIO.new
- else raise(MalformedError, "Expected the first character of a hex pair, but got \"#{char}\"")
- end
- when :value_hexstring_hex then
- case char
- when '0'..'9', 'a'..'f', 'A'..'F' then
- state = :value_hexstring
- value << char
- else raise(MalformedError, "Expected the second character of a hex pair, but got \"#{char}\"")
- end
- when :value_end then
- case char
- when ' ' then state = :value_end
- when ',' then
- state = :key
- yield key.string.strip, rstrip_except_escaped(value.string, dn_index)
- key = StringIO.new
- value = StringIO.new
- else raise(MalformedError, "Expected the end of an attribute value, but got \"#{char}\"")
- end
- else raise "Fell out of state machine"
- end
- end
-
- # Last pair
- raise(MalformedError, 'DN string ended unexpectedly') unless
- [:value, :value_normal, :value_hexstring, :value_end].include? state
-
- yield key.string.strip, rstrip_except_escaped(value.string, @dn.length)
- end
-
- def rstrip_except_escaped(str, dn_index)
- str_ends_with_whitespace = str.match(/\s\z/)
-
- if str_ends_with_whitespace
- dn_part_ends_with_escaped_whitespace = @dn[0, dn_index].match(/\\(\s+)\z/)
-
- if dn_part_ends_with_escaped_whitespace
- dn_part_rwhitespace = dn_part_ends_with_escaped_whitespace[1]
- num_chars_to_remove = dn_part_rwhitespace.length - 1
- str = str[0, str.length - num_chars_to_remove]
- else
- str.rstrip!
- end
- end
-
- str
- end
-
- ##
- # Returns the DN as an array in the form expected by the constructor.
- def to_a
- a = []
- self.each_pair { |key, value| a << key << value } unless @dn.empty?
- a
- end
-
- ##
- # Return the DN as an escaped string.
- def to_s
- @dn
- end
-
- ##
- # Return the DN as an escaped and normalized string.
- def to_normalized_s
- self.class.new(*to_a).to_s.downcase
- end
-
- # https://tools.ietf.org/html/rfc4514 section 2.4 lists these exceptions
- # for DN values. All of the following must be escaped in any normal string
- # using a single backslash ('\') as escape. The space character is left
- # out here because in a "normalized" string, spaces should only be escaped
- # if necessary (i.e. leading or trailing space).
- NORMAL_ESCAPES = [',', '+', '"', '\\', '<', '>', ';', '='].freeze
-
- # The following must be represented as escaped hex
- HEX_ESCAPES = {
- "\n" => '\0a',
- "\r" => '\0d'
- }.freeze
-
- # Compiled character class regexp using the keys from the above hash, and
- # checking for a space or # at the start, or space at the end, of the
- # string.
- ESCAPE_RE = Regexp.new("(^ |^#| $|[" +
- NORMAL_ESCAPES.map { |e| Regexp.escape(e) }.join +
- "])")
-
- HEX_ESCAPE_RE = Regexp.new("([" +
- HEX_ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
- "])")
-
- ##
- # Escape a string for use in a DN value
- def self.escape(string)
- escaped = string.gsub(ESCAPE_RE) { |char| "\\" + char }
- escaped.gsub(HEX_ESCAPE_RE) { |char| HEX_ESCAPES[char] }
- end
-
- private
-
- def initialize_array(args)
- buffer = StringIO.new
-
- args.each_with_index do |arg, index|
- if index.even? # key
- buffer << "," if index > 0
- buffer << arg
- else # value
- buffer << "="
- buffer << self.class.escape(arg)
- end
- end
-
- @dn = buffer.string
- end
-
- def initialize_string(arg)
- @dn = arg.to_s
- end
-
- ##
- # Proxy all other requests to the string object, because a DN is mainly
- # used within the library as a string
- # rubocop:disable GitlabSecurity/PublicSend
- def method_missing(method, *args, &block)
- @dn.send(method, *args, &block)
- end
-
- ##
- # Redefined to be consistent with redefined `method_missing` behavior
- def respond_to?(sym, include_private = false)
- @dn.respond_to?(sym, include_private)
- end
- end
- end
- end
- end
-
- def perform(start_id, end_id)
- return unless migrate?
-
- ldap_identities = Identity.where("provider like 'ldap%'").where(id: start_id..end_id)
- ldap_identities.each do |identity|
- identity.extern_uid = Gitlab::Auth::LDAP::DN.new(identity.extern_uid).to_normalized_s
- unless identity.save
- Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\". Skipping."
- end
- rescue Gitlab::Auth::LDAP::DN::FormatError => e
- Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\" due to \"#{e.message}\". Skipping."
- end
- end
-
- def migrate?
- Identity.table_exists?
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/populate_fork_networks_range.rb b/lib/gitlab/background_migration/populate_fork_networks_range.rb
deleted file mode 100644
index aa4f130538c..00000000000
--- a/lib/gitlab/background_migration/populate_fork_networks_range.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module BackgroundMigration
- # This background migration is going to create all `fork_networks` and
- # the `fork_network_members` for the roots of fork networks based on the
- # existing `forked_project_links`.
- #
- # When the source of a fork is deleted, we will create the fork with the
- # target project as the root. This way, when there are forks of the target
- # project, they will be joined into the same fork network.
- #
- # When the `fork_networks` and memberships for the root projects are created
- # the `CreateForkNetworkMembershipsRange` migration is scheduled. This
- # migration will create the memberships for all remaining forks-of-forks
- class PopulateForkNetworksRange
- def perform(start_id, end_id)
- create_fork_networks_for_existing_projects(start_id, end_id)
- create_fork_networks_for_missing_projects(start_id, end_id)
- create_fork_networks_memberships_for_root_projects(start_id, end_id)
-
- delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY
- BackgroundMigrationWorker.perform_in(
- delay, "CreateForkNetworkMembershipsRange", [start_id, end_id]
- )
- end
-
- def create_fork_networks_for_existing_projects(start_id, end_id)
- log("Creating fork networks: #{start_id} - #{end_id}")
- ActiveRecord::Base.connection.execute <<~INSERT_NETWORKS
- INSERT INTO fork_networks (root_project_id)
- SELECT DISTINCT forked_project_links.forked_from_project_id
-
- FROM forked_project_links
-
- -- Exclude the forks that are not the first level fork of a project
- WHERE NOT EXISTS (
- SELECT true
- FROM forked_project_links inner_links
- WHERE inner_links.forked_to_project_id = forked_project_links.forked_from_project_id
- )
-
- /* Exclude the ones that are already created, in case the fork network
- was already created for another fork of the project.
- */
- AND NOT EXISTS (
- SELECT true
- FROM fork_networks
- WHERE forked_project_links.forked_from_project_id = fork_networks.root_project_id
- )
-
- -- Only create a fork network for a root project that still exists
- AND EXISTS (
- SELECT true
- FROM projects
- WHERE projects.id = forked_project_links.forked_from_project_id
- )
- AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
- INSERT_NETWORKS
- end
-
- def create_fork_networks_for_missing_projects(start_id, end_id)
- log("Creating fork networks with missing root: #{start_id} - #{end_id}")
- ActiveRecord::Base.connection.execute <<~INSERT_NETWORKS
- INSERT INTO fork_networks (root_project_id)
- SELECT DISTINCT forked_project_links.forked_to_project_id
-
- FROM forked_project_links
-
- -- Exclude forks that are not the root forks
- WHERE NOT EXISTS (
- SELECT true
- FROM forked_project_links inner_links
- WHERE inner_links.forked_to_project_id = forked_project_links.forked_from_project_id
- )
-
- /* Exclude the ones that are already created, in case this migration is
- re-run
- */
- AND NOT EXISTS (
- SELECT true
- FROM fork_networks
- WHERE forked_project_links.forked_to_project_id = fork_networks.root_project_id
- )
-
- /* Exclude projects for which the project still exists, those are
- Processed in the previous step of this migration
- */
- AND NOT EXISTS (
- SELECT true
- FROM projects
- WHERE projects.id = forked_project_links.forked_from_project_id
- )
- AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
- INSERT_NETWORKS
- end
-
- def create_fork_networks_memberships_for_root_projects(start_id, end_id)
- log("Creating memberships for root projects: #{start_id} - #{end_id}")
-
- ActiveRecord::Base.connection.execute <<~INSERT_ROOT
- INSERT INTO fork_network_members (fork_network_id, project_id)
- SELECT DISTINCT fork_networks.id, fork_networks.root_project_id
-
- FROM fork_networks
-
- /* Joining both on forked_from- and forked_to- so we could create the
- memberships for forks for which the source was deleted
- */
- INNER JOIN forked_project_links
- ON forked_project_links.forked_from_project_id = fork_networks.root_project_id
- OR forked_project_links.forked_to_project_id = fork_networks.root_project_id
-
- WHERE NOT EXISTS (
- SELECT true
- FROM fork_network_members
- WHERE fork_network_members.project_id = fork_networks.root_project_id
- )
- AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
- INSERT_ROOT
- end
-
- def log(message)
- Rails.logger.info("#{self.class.name} - #{message}")
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id.rb b/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id.rb
deleted file mode 100644
index dcac355e1b0..00000000000
--- a/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class PopulateMergeRequestsLatestMergeRequestDiffId
- BATCH_SIZE = 1_000
-
- class MergeRequest < ActiveRecord::Base
- self.table_name = 'merge_requests'
-
- include ::EachBatch
- end
-
- def perform(start_id, stop_id)
- update = '
- latest_merge_request_diff_id = (
- SELECT MAX(id)
- FROM merge_request_diffs
- WHERE merge_requests.id = merge_request_diffs.merge_request_id
- )'.squish
-
- MergeRequest
- .where(id: start_id..stop_id)
- .where(latest_merge_request_diff_id: nil)
- .each_batch(of: BATCH_SIZE) do |relation|
-
- relation.update_all(update)
- end
- end
- end
- end
-end