diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 158cef642e1488d261c7d300943c5750a8a4a98b..9de5636d9003c46649d656316c8422d7aab6d340 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -173,9 +173,9 @@ def merge_request_diff after_update :clear_memoized_shas after_update :reload_diff_if_branch_changed after_save :keep_around_commit, unless: :importing? + after_save :save_merge_data_changes after_commit :ensure_metrics!, on: [:create, :update], unless: :importing? after_commit :expire_etag_cache, unless: :importing? - after_commit :save_merge_data_changes after_find :preload_branches @@ -2757,7 +2757,7 @@ def clear_merge_params(param_keys) end def update_merge_ref_sha(sha) - with_merge_data_sync( + with_single_transaction_merge_data_sync( -> { update_column(:merge_ref_sha, sha) }, -> do # update_column only works if the record is already saved. @@ -2771,7 +2771,7 @@ def update_merge_ref_sha(sha) end def update_merge_jid(jid) - with_merge_data_sync( + with_single_transaction_merge_data_sync( -> { update_column(:merge_jid, jid) }, -> do # update_column only works if the record is already saved. @@ -2792,6 +2792,7 @@ def ensure_merge_data # id may not have been set if the merge_request got saved after the initial # ensure_merge_data so we need to set merge_request_id here if it is not already set. record.merge_request_id ||= id + record.project_id ||= target_project_id record end @@ -2838,6 +2839,22 @@ def with_merge_data_sync(merge_request_block, merge_data_block) end end + def with_single_transaction_merge_data_sync(merge_request_block, merge_data_block) + if dual_write_to_merge_data_ff_enabled? + result = nil + ensure_merge_data + + ApplicationRecord.transaction do + result = merge_request_block.call + merge_data_block.call + end + + result + else + merge_request_block.call + end + end + def save_merge_data_changes return unless dual_write_to_merge_data_ff_enabled? return unless merge_data&.changed? @@ -2845,13 +2862,8 @@ def save_merge_data_changes # id is set when merge_request gets saved so we need to manually # set merge_request_id here if it is not already set. merge_data.merge_request_id ||= id + merge_data.project_id ||= target_project_id merge_data.save! - - rescue StandardError => e - Gitlab::ErrorTracking.track_exception(e, - message: "Failed to save MergeData", - merge_request_id: id - ) end def dual_write_to_merge_data_ff_enabled? diff --git a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json index 5489330fc1df0bbdcb6fc1559d8bd1ceff0409a5..a1ea901cc19f44a7785c5f9c23577f174ac5428d 100644 --- a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json +++ b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json @@ -1,5 +1,5 @@ { - "merge_request_123456789": { + "merge_request_12345678": { "files": { "file_a.rb": [ { diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 97be76a51abd9725dbf483643433daa7e6c63cc4..a2a8ca51612532a8e0995da9f22b7314e02e15dc 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -3785,7 +3785,7 @@ def set_compare(merge_request) describe '#find_codequality_mr_diff_reports' do let(:project) { create(:project, :repository) } - let(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project, id: 123456789) } + let(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project, id: 12345678) } let(:pipeline) { merge_request.head_pipeline } subject(:mr_diff_report) { merge_request.find_codequality_mr_diff_reports } @@ -8049,17 +8049,13 @@ def transition! allow(merge_data).to receive(:save!).and_raise(StandardError) end - it 'saves merge_status on merge_requests and logs the error' do - expect(Gitlab::ErrorTracking) - .to receive(:track_exception) - .with( - instance_of(StandardError), - message: "Failed to save MergeData", - merge_request_id: merge_request.id - ) - - merge_request.mark_as_preparing! - expect(merge_request.reload.merge_status).to eq('preparing') + it 'raises exception and does not save merge_status on both merge_requests and merge_data' do + expect do + merge_request.mark_as_preparing! + end.to raise_error(StandardError) + + expect(merge_request.reload.merge_status).to eq(merge_status) + expect(merge_data.merge_status).to eq(merge_status) end end end @@ -8178,27 +8174,14 @@ def transition! end shared_examples 'handles merge_data save failure' do - it 'saves merge_request and logs errors if merge_data save fails' do + it 'raises exception and does not save merge_request if merge_data save fails' do allow_any_instance_of(MergeRequests::MergeData).to receive(:save!).and_raise(StandardError) - expect(Gitlab::ErrorTracking) - .to receive(:track_exception) - .with( - instance_of(StandardError), - hash_including( - message: "Failed to save MergeData", - merge_request_id: be_present - ) - ) - expect do perform_action - end.to change { MergeRequest.count }.by(1) + end.to raise_error(StandardError) + .and not_change { MergeRequest.count } .and not_change { MergeRequests::MergeData.count } - - merge_request.reload - expect(merge_request.persisted?).to be_truthy - expect(merge_request.merge_data).to be_nil end end @@ -8256,7 +8239,7 @@ def perform_action end context 'via update' do - let(:merge_request) do + let!(:merge_request) do described_class.create!( title: 'Test MR', source_project: project, @@ -8422,8 +8405,8 @@ def perform_action merge_request.public_send(method_name, value) expect(merge_request.read_attribute(attribute)).to eq(value) - expect(merge_data.read_attribute(attribute)).to eq(value) expect(merge_data.persisted?).to be_truthy + expect(merge_data.read_attribute(attribute)).to eq(value) end end @@ -8441,22 +8424,12 @@ def perform_action end context 'when an exception occurs during merge_data update' do - it 'logs the error and still saves merge_request' do + it 'raises the error and does not save merge_request' do allow(merge_data).to receive(:update_column).and_raise(StandardError) - expect(Gitlab::ErrorTracking) - .to receive(:track_exception) - .with( - instance_of(StandardError), - hash_including( - message: "Failed to sync MergeData", - merge_request_id: be_present - ) - ) - - merge_request.public_send(method_name, value) + expect { merge_request.public_send(method_name, value) }.to raise_error(StandardError) - expect(merge_request.reload.read_attribute(attribute)).to eq(value) + expect(merge_request.reload.read_attribute(attribute)).not_to eq(value) expect(merge_data.reload.read_attribute(attribute)).to be_nil end end diff --git a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb index f4f0990240d06403eff603004f6d118e4b35a742..18b4223c99e5293424cc41cb550aca0e6ab9decf 100644 --- a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb +++ b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter, feature_category: :code_quality do let(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_codequality_mr_diff_report) } - let(:merge_request) { double(id: 123456789, new_paths: filenames) } + let(:merge_request) { double(id: 12345678, new_paths: filenames) } subject(:presenter) { described_class.new(pipeline_artifact) } diff --git a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb index c33b182e9a9a8ab96411ce112fe63a94c028707e..c981596b9672059b90ed808046b5448e5f53f422 100644 --- a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb +++ b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb @@ -10,7 +10,7 @@ subject { service.execute(base_pipeline, head_pipeline) } context 'when head pipeline has codequality mr diff report' do - let!(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project, id: 123456789) } + let!(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project, id: 12345678) } let!(:service) { described_class.new(project, nil, id: merge_request.id) } let!(:head_pipeline) { merge_request.head_pipeline } let!(:base_pipeline) { nil }