summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb')
-rw-r--r--lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb78
1 files changed, 78 insertions, 0 deletions
diff --git a/lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb b/lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb
new file mode 100644
index 00000000000..cb9b0e88ef4
--- /dev/null
+++ b/lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # The migration is used to cleanup orphaned lfs_objects_projects in order to
+ # introduce valid foreign keys to this table
+ class CleanupOrphanedLfsObjectsProjects
+ # A model to access lfs_objects_projects table in migrations
+ class LfsObjectsProject < ActiveRecord::Base
+ self.table_name = 'lfs_objects_projects'
+
+ include ::EachBatch
+
+ belongs_to :lfs_object
+ belongs_to :project
+ end
+
+ # A model to access lfs_objects table in migrations
+ class LfsObject < ActiveRecord::Base
+ self.table_name = 'lfs_objects'
+ end
+
+ # A model to access projects table in migrations
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ end
+
+ SUB_BATCH_SIZE = 5000
+ CLEAR_CACHE_DELAY = 1.minute
+
+ def perform(start_id, end_id)
+ cleanup_lfs_objects_projects_without_lfs_object(start_id, end_id)
+ cleanup_lfs_objects_projects_without_project(start_id, end_id)
+ end
+
+ private
+
+ def cleanup_lfs_objects_projects_without_lfs_object(start_id, end_id)
+ each_record_without_association(start_id, end_id, :lfs_object, :lfs_objects) do |lfs_objects_projects_without_lfs_objects|
+ projects = Project.where(id: lfs_objects_projects_without_lfs_objects.select(:project_id))
+
+ if projects.present?
+ ProjectCacheWorker.bulk_perform_in_with_contexts(
+ CLEAR_CACHE_DELAY,
+ projects,
+ arguments_proc: ->(project) { [project.id, [], [:lfs_objects_size]] },
+ context_proc: ->(project) { { project: project } }
+ )
+ end
+
+ lfs_objects_projects_without_lfs_objects.delete_all
+ end
+ end
+
+ def cleanup_lfs_objects_projects_without_project(start_id, end_id)
+ each_record_without_association(start_id, end_id, :project, :projects) do |lfs_objects_projects_without_projects|
+ lfs_objects_projects_without_projects.delete_all
+ end
+ end
+
+ def each_record_without_association(start_id, end_id, association, table_name)
+ batch = LfsObjectsProject.where(id: start_id..end_id)
+
+ batch.each_batch(of: SUB_BATCH_SIZE) do |sub_batch|
+ first, last = sub_batch.pluck(Arel.sql('min(lfs_objects_projects.id), max(lfs_objects_projects.id)')).first
+
+ lfs_objects_without_association =
+ LfsObjectsProject
+ .unscoped
+ .left_outer_joins(association)
+ .where(id: (first..last), table_name => { id: nil })
+
+ yield lfs_objects_without_association
+ end
+ end
+ end
+ end
+end