Commit a93907ad authored by Patrick Bajao's avatar Patrick Bajao

Create/update cleanup schedule on schedule and completion

Instead of scheduling sidekiq jobs and keeping them in redis
for 14 days before they get performed, we set the schedule in the
DB.

This is to prevent huge number of scheduled jobs in cases when
there are a lot of closed/merged MRs.
parent f229df90
...@@ -2,4 +2,6 @@ ...@@ -2,4 +2,6 @@
class MergeRequest::CleanupSchedule < ApplicationRecord class MergeRequest::CleanupSchedule < ApplicationRecord
belongs_to :merge_request, inverse_of: :cleanup_schedule belongs_to :merge_request, inverse_of: :cleanup_schedule
validates :scheduled_at, presence: true
end end
...@@ -9,7 +9,7 @@ module MergeRequests ...@@ -9,7 +9,7 @@ module MergeRequests
attr_reader :merge_request attr_reader :merge_request
def self.schedule(merge_request) def self.schedule(merge_request)
MergeRequestCleanupRefsWorker.perform_in(TIME_THRESHOLD, merge_request.id) merge_request.create_cleanup_schedule(scheduled_at: TIME_THRESHOLD.from_now)
end end
def initialize(merge_request) def initialize(merge_request)
...@@ -22,6 +22,7 @@ module MergeRequests ...@@ -22,6 +22,7 @@ module MergeRequests
end end
def execute def execute
return error("Merge request is not scheduled to be cleaned up yet.") unless scheduled?
return error("Merge request has not been closed nor merged for #{TIME_THRESHOLD.inspect}.") unless eligible? return error("Merge request has not been closed nor merged for #{TIME_THRESHOLD.inspect}.") unless eligible?
# Ensure that commit shas of refs are kept around so we won't lose them when GC runs. # Ensure that commit shas of refs are kept around so we won't lose them when GC runs.
...@@ -31,13 +32,17 @@ module MergeRequests ...@@ -31,13 +32,17 @@ module MergeRequests
return error('Failed to cache merge ref sha.') unless cache_merge_ref_sha return error('Failed to cache merge ref sha.') unless cache_merge_ref_sha
delete_refs delete_refs
success success if update_schedule
end end
private private
attr_reader :repository, :ref_path, :merge_ref_path, :ref_head_sha, :merge_ref_sha attr_reader :repository, :ref_path, :merge_ref_path, :ref_head_sha, :merge_ref_sha
def scheduled?
merge_request.cleanup_schedule.present? && merge_request.cleanup_schedule.scheduled_at <= Time.current
end
def eligible? def eligible?
return met_time_threshold?(merge_request.metrics&.latest_closed_at) if merge_request.closed? return met_time_threshold?(merge_request.metrics&.latest_closed_at) if merge_request.closed?
...@@ -71,5 +76,9 @@ module MergeRequests ...@@ -71,5 +76,9 @@ module MergeRequests
def delete_refs def delete_refs
repository.delete_refs(ref_path, merge_ref_path) repository.delete_refs(ref_path, merge_ref_path)
end end
def update_schedule
merge_request.cleanup_schedule.update(completed_at: Time.current)
end
end end
end end
...@@ -15,6 +15,7 @@ module MergeRequests ...@@ -15,6 +15,7 @@ module MergeRequests
invalidate_cache_counts(merge_request, users: merge_request.assignees) invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches merge_request.update_project_counter_caches
merge_request.cache_merge_request_closes_issues!(current_user) merge_request.cache_merge_request_closes_issues!(current_user)
merge_request.cleanup_schedule&.destroy
end end
merge_request merge_request
......
...@@ -6,4 +6,8 @@ RSpec.describe MergeRequest::CleanupSchedule do ...@@ -6,4 +6,8 @@ RSpec.describe MergeRequest::CleanupSchedule do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:merge_request) } it { is_expected.to belong_to(:merge_request) }
end end
describe 'validations' do
it { is_expected.to validate_presence_of(:scheduled_at) }
end
end end
...@@ -4,14 +4,15 @@ require 'spec_helper' ...@@ -4,14 +4,15 @@ require 'spec_helper'
RSpec.describe MergeRequests::CleanupRefsService do RSpec.describe MergeRequests::CleanupRefsService do
describe '.schedule' do describe '.schedule' do
let(:merge_request) { build(:merge_request) } let(:merge_request) { create(:merge_request) }
it 'schedules MergeRequestCleanupRefsWorker' do it 'creates a merge request cleanup schedule' do
expect(MergeRequestCleanupRefsWorker) freeze_time do
.to receive(:perform_in) described_class.schedule(merge_request)
.with(described_class::TIME_THRESHOLD, merge_request.id)
described_class.schedule(merge_request) expect(merge_request.reload.cleanup_schedule.scheduled_at)
.to eq(described_class::TIME_THRESHOLD.from_now)
end
end end
end end
...@@ -20,6 +21,8 @@ RSpec.describe MergeRequests::CleanupRefsService do ...@@ -20,6 +21,8 @@ RSpec.describe MergeRequests::CleanupRefsService do
# Need to re-enable this as it's being stubbed in spec_helper for # Need to re-enable this as it's being stubbed in spec_helper for
# performance reasons but is needed to run for this test. # performance reasons but is needed to run for this test.
allow(Gitlab::Git::KeepAround).to receive(:execute).and_call_original allow(Gitlab::Git::KeepAround).to receive(:execute).and_call_original
merge_request.create_cleanup_schedule(scheduled_at: described_class::TIME_THRESHOLD.ago)
end end
subject(:result) { described_class.new(merge_request).execute } subject(:result) { described_class.new(merge_request).execute }
...@@ -32,6 +35,7 @@ RSpec.describe MergeRequests::CleanupRefsService do ...@@ -32,6 +35,7 @@ RSpec.describe MergeRequests::CleanupRefsService do
expect(result[:status]).to eq(:success) expect(result[:status]).to eq(:success)
expect(kept_around?(old_ref_head)).to be_truthy expect(kept_around?(old_ref_head)).to be_truthy
expect(ref_head).to be_nil expect(ref_head).to be_nil
expect(merge_request.cleanup_schedule.completed_at).to be_present
end end
end end
...@@ -43,6 +47,7 @@ RSpec.describe MergeRequests::CleanupRefsService do ...@@ -43,6 +47,7 @@ RSpec.describe MergeRequests::CleanupRefsService do
it 'does not fail' do it 'does not fail' do
expect(result[:status]).to eq(:success) expect(result[:status]).to eq(:success)
expect(merge_request.cleanup_schedule.completed_at).to be_present
end end
end end
...@@ -85,6 +90,14 @@ RSpec.describe MergeRequests::CleanupRefsService do ...@@ -85,6 +90,14 @@ RSpec.describe MergeRequests::CleanupRefsService do
it_behaves_like 'service that does not clean up merge request refs' it_behaves_like 'service that does not clean up merge request refs'
end end
context 'when merge request is not scheduled to be cleaned up yet' do
before do
merge_request.cleanup_schedule.update!(scheduled_at: 1.day.from_now)
end
it_behaves_like 'service that does not clean up merge request refs'
end
end end
shared_examples_for 'service that does not clean up merge request refs' do shared_examples_for 'service that does not clean up merge request refs' do
...@@ -92,6 +105,7 @@ RSpec.describe MergeRequests::CleanupRefsService do ...@@ -92,6 +105,7 @@ RSpec.describe MergeRequests::CleanupRefsService do
aggregate_failures do aggregate_failures do
expect(result[:status]).to eq(:error) expect(result[:status]).to eq(:error)
expect(ref_head).to be_present expect(ref_head).to be_present
expect(merge_request.cleanup_schedule.completed_at).not_to be_present
end end
end end
end end
......
...@@ -23,6 +23,7 @@ RSpec.describe MergeRequests::ReopenService do ...@@ -23,6 +23,7 @@ RSpec.describe MergeRequests::ReopenService do
before do before do
allow(service).to receive(:execute_hooks) allow(service).to receive(:execute_hooks)
merge_request.create_cleanup_schedule(scheduled_at: Time.current)
perform_enqueued_jobs do perform_enqueued_jobs do
service.execute(merge_request) service.execute(merge_request)
...@@ -43,6 +44,10 @@ RSpec.describe MergeRequests::ReopenService do ...@@ -43,6 +44,10 @@ RSpec.describe MergeRequests::ReopenService do
expect(email.subject).to include(merge_request.title) expect(email.subject).to include(merge_request.title)
end end
it 'destroys cleanup schedule record' do
expect(merge_request.reload.cleanup_schedule).to be_nil
end
context 'note creation' do context 'note creation' do
it 'creates resource state event about merge_request reopen' do it 'creates resource state event about merge_request reopen' do
event = merge_request.resource_state_events.last event = merge_request.resource_state_events.last
......
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