summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/background_migration/delete_diff_files.rb83
-rw-r--r--lib/gitlab/background_migration/schedule_diff_files_deletion.rb61
2 files changed, 58 insertions, 86 deletions
diff --git a/lib/gitlab/background_migration/delete_diff_files.rb b/lib/gitlab/background_migration/delete_diff_files.rb
index e2c90fce6b1..36161285aac 100644
--- a/lib/gitlab/background_migration/delete_diff_files.rb
+++ b/lib/gitlab/background_migration/delete_diff_files.rb
@@ -8,6 +8,7 @@ module Gitlab
self.table_name = 'merge_request_diffs'
belongs_to :merge_request
+ has_many :merge_request_diff_files
include EachBatch
end
@@ -18,41 +19,73 @@ module Gitlab
include EachBatch
end
- def perform(merge_request_diff_id)
- merge_request_diff = MergeRequestDiff.find_by(id: merge_request_diff_id)
+ BATCH = 5_000
+ DEAD_TUPLES_THRESHOLD = 50_000
+ VACUUM_WAIT_TIME = 5.minutes
- return unless merge_request_diff
- return unless should_delete_diff_files?(merge_request_diff)
+ def perform
+ diffs_with_files = MergeRequestDiff
+ .joins(:merge_request)
+ .where("merge_requests.state = 'merged'")
+ .where('merge_requests.latest_merge_request_diff_id IS NOT NULL')
+ .where('merge_requests.latest_merge_request_diff_id != merge_request_diffs.id')
+ .where("merge_request_diffs.state NOT IN ('without_files', 'empty')")
- MergeRequestDiff.transaction do
- merge_request_diff.update_column(:state, 'without_files')
-
- # explain (analyze, buffers) when deleting 453 diff files:
- #
- # Delete on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actual time=43.265..43.265 rows=0 loops=1)
- # Buffers: shared hit=2043 read=259 dirtied=254
- # -> Index Scan using index_merge_request_diff_files_on_mr_diff_id_and_order on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actu
- # al time=0.466..26.317 rows=453 loops=1)
- # Index Cond: (merge_request_diff_id = 463448)
- # Buffers: shared hit=17 read=84
- # Planning time: 0.107 ms
- # Execution time: 43.287 ms
- #
- MergeRequestDiffFile.where(merge_request_diff_id: merge_request_diff.id).delete_all
+ diffs_with_files.each_batch(of: BATCH) do |batch, index|
+ wait_deadtuple_vacuum(index)
+ prune_diff_files(batch, index)
+ end
+ end
+
+ def wait_deadtuple_vacuum(index)
+ db_klass = Gitlab::Database
+
+ if defined?(db_klass) && db_klass.respond_to?(:postgresql?) && db_klass.postgresql?
+ while diff_files_dead_tuples_count >= DEAD_TUPLES_THRESHOLD
+ log_info("Dead tuple threshold hit on merge_request_diff_files (#{index}th batch): " \
+ "#{diff_files_dead_tuples_count}, waiting 5 minutes")
+ sleep VACUUM_WAIT_TIME
+ end
end
end
private
- def should_delete_diff_files?(merge_request_diff)
- return false if merge_request_diff.state == 'without_files'
+ def diff_files_dead_tuples_count
+ dead_tuple =
+ execute_statement("SELECT n_dead_tup FROM pg_stat_all_tables "\
+ "WHERE relname = 'merge_request_diff_files'")[0]
+
+ if dead_tuple.present?
+ dead_tuple['n_dead_tup'].to_i
+ else
+ 0
+ end
+ end
- merge_request = merge_request_diff.merge_request
+ def prune_diff_files(batch, index)
+ diff_ids = batch.pluck(:id)
- return false unless merge_request.state == 'merged'
- return false if merge_request_diff.id == merge_request.latest_merge_request_diff_id
+ removed = 0
+ updated = 0
+
+ MergeRequestDiff.transaction do
+ updated = MergeRequestDiff.where(id: diff_ids)
+ .update_all(state: 'without_files')
+ removed = MergeRequestDiffFile.where(merge_request_diff_id: diff_ids)
+ .delete_all
+ end
+
+ log_info("#{index}th batch - Removed #{removed} merge_request_diff_files rows, "\
+ "updated #{updated} merge_request_diffs rows")
+ end
+
+ def execute_statement(sql)
+ ActiveRecord::Base.connection.execute(sql)
+ end
- true
+ def log_info(message)
+ Rails.logger.info("BackgroundMigration::DeleteDiffFiles - #{message}")
end
end
end
diff --git a/lib/gitlab/background_migration/schedule_diff_files_deletion.rb b/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
deleted file mode 100644
index 6442468836b..00000000000
--- a/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class ScheduleDiffFilesDeletion
- class MergeRequestDiff < ActiveRecord::Base
- self.table_name = 'merge_request_diffs'
-
- has_many :merge_request_diff_files
-
- include EachBatch
- end
-
- ITERATION_BATCH = 1000
- DELETION_BATCH = 1000 # per minute
- MIGRATION = 'DeleteDiffFiles'
-
- # Considering query times and Redis writings, this should take around 2
- # hours to complete.
- def perform
- diffs_with_files = MergeRequestDiff.where.not(state: %w(without_files empty))
-
- # This will be increased for each scheduled job
- process_job_in = 1.second
-
- # explain (analyze, buffers) example for the iteration:
- #
- # Index Only Scan using tmp_index_20013 on merge_request_diffs (cost=0.43..1630.19 rows=60567 width=4) (actual time=0.047..9.572 rows=56976 loops=1)
- # Index Cond: ((id >= 764586) AND (id < 835298))
- # Heap Fetches: 8
- # Buffers: shared hit=18188
- # Planning time: 0.752 ms
- # Execution time: 12.430 ms
- #
- diffs_with_files.reorder(nil).each_batch(of: ITERATION_BATCH) do |relation, scheduler_index|
- relation.each do |diff|
- BackgroundMigrationWorker.perform_in(process_job_in, MIGRATION, [diff.id])
-
- diff_files_count = diff.merge_request_diff_files.reorder(nil).count
-
- # We should limit on 1000 diff files deletion per minute to avoid
- # replication lag issues.
- #
- interval = (diff_files_count.to_f / DELETION_BATCH).minutes
- process_job_in += interval
- end
- end
-
- log_days_to_process_all_jobs(process_job_in)
- end
-
- def log_days_to_process_all_jobs(seconds_to_process)
- days_to_process_all_jobs = (seconds_to_process / 60 / 60 / 24).to_i
- Rails.logger.info("Gitlab::BackgroundMigration::DeleteDiffFiles will take " \
- "#{days_to_process_all_jobs} days to be processed")
- end
- end
- end
-end