BigW Consortium Gitlab

Commit eb98a6f6 by Winnie Hellmann

Merge branch 'bvl-fork-networks-for-deleted-projects-10-2' into '10-2-stable-patch-5'

Create fork networks for deleted source projects (10.2 port) See merge request gitlab-org/gitlab-ce!15927
parents b452d57e 5708bd6c
---
title: Create a fork network for forks with a deleted source
merge_request: 15595
author:
type: fixed
class RescheduleForkNetworkCreation < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'PopulateForkNetworksRange'.freeze
BATCH_SIZE = 100
DELAY_INTERVAL = 15.seconds
disable_ddl_transaction!
class ForkedProjectLink < ActiveRecord::Base
include EachBatch
self.table_name = 'forked_project_links'
end
def up
say 'Populating the `fork_networks` based on existing `forked_project_links`'
queue_background_migration_jobs_by_range_at_intervals(ForkedProjectLink, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
def down
# nothing
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20171124132536) do ActiveRecord::Schema.define(version: 20171124150326) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
......
# frozen_string_literal: true
module Gitlab module Gitlab
module BackgroundMigration 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 class PopulateForkNetworksRange
def perform(start_id, end_id) def perform(start_id, end_id)
log("Creating fork networks for forked project links: #{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 # rubocop:disable Metrics/LineLength
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 ActiveRecord::Base.connection.execute <<~INSERT_NETWORKS
INSERT INTO fork_networks (root_project_id) INSERT INTO fork_networks (root_project_id)
SELECT DISTINCT forked_project_links.forked_from_project_id SELECT DISTINCT forked_project_links.forked_from_project_id
FROM forked_project_links FROM forked_project_links
-- Exclude the forks that are not the first level fork of a project
WHERE NOT EXISTS ( WHERE NOT EXISTS (
SELECT true SELECT true
FROM forked_project_links inner_links FROM forked_project_links inner_links
WHERE inner_links.forked_to_project_id = forked_project_links.forked_from_project_id 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 ( AND NOT EXISTS (
SELECT true SELECT true
FROM fork_networks FROM fork_networks
WHERE forked_project_links.forked_from_project_id = fork_networks.root_project_id 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 ( AND EXISTS (
SELECT true SELECT true
FROM projects FROM projects
...@@ -27,7 +57,45 @@ module Gitlab ...@@ -27,7 +57,45 @@ module Gitlab
) )
AND forked_project_links.id BETWEEN #{start_id} AND #{end_id} AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
INSERT_NETWORKS 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}") log("Creating memberships for root projects: #{start_id} - #{end_id}")
ActiveRecord::Base.connection.execute <<~INSERT_ROOT ActiveRecord::Base.connection.execute <<~INSERT_ROOT
...@@ -36,8 +104,12 @@ module Gitlab ...@@ -36,8 +104,12 @@ module Gitlab
FROM fork_networks 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 INNER JOIN forked_project_links
ON forked_project_links.forked_from_project_id = fork_networks.root_project_id 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 ( WHERE NOT EXISTS (
SELECT true SELECT true
...@@ -46,9 +118,6 @@ module Gitlab ...@@ -46,9 +118,6 @@ module Gitlab
) )
AND forked_project_links.id BETWEEN #{start_id} AND #{end_id} AND forked_project_links.id BETWEEN #{start_id} AND #{end_id}
INSERT_ROOT INSERT_ROOT
delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY
BackgroundMigrationWorker.perform_in(delay, "CreateForkNetworkMembershipsRange", [start_id, end_id])
end end
def log(message) def log(message)
......
...@@ -62,12 +62,15 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch ...@@ -62,12 +62,15 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
expect(base2_membership).not_to be_nil expect(base2_membership).not_to be_nil
end end
it 'skips links that had their source project deleted' do it 'creates a fork network for the fork of which the source was deleted' do
forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: create(:project).id) fork = create(:project)
forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id)
migration.perform(5, 8) migration.perform(5, 8)
expect(fork_networks.find_by(root_project_id: 99999)).to be_nil expect(fork_networks.find_by(root_project_id: 99999)).to be_nil
expect(fork_networks.find_by(root_project_id: fork.id)).not_to be_nil
expect(fork_network_members.find_by(project_id: fork.id)).not_to be_nil
end end
it 'schedules a job for inserting memberships for forks-of-forks' do it 'schedules a job for inserting memberships for forks-of-forks' do
......
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