Commit 7ba5b9ba authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b56027c9
...@@ -111,14 +111,6 @@ module ApplicationSettingsHelper ...@@ -111,14 +111,6 @@ module ApplicationSettingsHelper
] ]
end end
def repository_storages_options_for_select(selected)
options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{storage['gitaly_address']}", name]
end
options_for_select(options, selected)
end
def repository_storages_options_json def repository_storages_options_json
options = Gitlab.config.repositories.storages.map do |name, storage| options = Gitlab.config.repositories.storages.map do |name, storage|
{ {
......
...@@ -27,7 +27,6 @@ module MergeRequests ...@@ -27,7 +27,6 @@ module MergeRequests
success success
end end
end end
log_info("Merge process finished on JID #{merge_jid} with state #{state}") log_info("Merge process finished on JID #{merge_jid} with state #{state}")
rescue MergeError => e rescue MergeError => e
handle_merge_error(log_message: e.message, save_message_on_model: true) handle_merge_error(log_message: e.message, save_message_on_model: true)
...@@ -55,7 +54,7 @@ module MergeRequests ...@@ -55,7 +54,7 @@ module MergeRequests
error = error =
if @merge_request.should_be_rebased? if @merge_request.should_be_rebased?
'Only fast-forward merge is allowed for your project. Please update your source branch' 'Only fast-forward merge is allowed for your project. Please update your source branch'
elsif !@merge_request.merged? && !@merge_request.mergeable? elsif !@merge_request.mergeable?
'Merge request is not mergeable' 'Merge request is not mergeable'
end end
......
...@@ -8,28 +8,17 @@ module MergeRequests ...@@ -8,28 +8,17 @@ module MergeRequests
# #
class PostMergeService < MergeRequests::BaseService class PostMergeService < MergeRequests::BaseService
def execute(merge_request) def execute(merge_request)
# These operations need to happen transactionally merge_request.mark_as_merged
ActiveRecord::Base.transaction(requires_new: true) do
merge_request.mark_as_merged
# These options do not call external services and should be
# relatively quick enough to put in a Transaction
create_event(merge_request)
todo_service.merge_merge_request(merge_request, current_user)
end
# These operations are idempotent so can be safely run multiple times
notification_service.merge_mr(merge_request, current_user)
create_note(merge_request)
close_issues(merge_request) close_issues(merge_request)
todo_service.merge_merge_request(merge_request, current_user)
create_event(merge_request)
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
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
delete_non_latest_diffs(merge_request) delete_non_latest_diffs(merge_request)
cleanup_environments(merge_request) cleanup_environments(merge_request)
# Anything after this point will be executed at-most-once. Less important activity only
# TODO: make all the work in here a separate sidekiq job so it can go in the transaction
execute_hooks(merge_request, 'merge')
end end
private private
......
...@@ -4,14 +4,12 @@ module MergeRequests ...@@ -4,14 +4,12 @@ module MergeRequests
class SquashService < MergeRequests::BaseService class SquashService < MergeRequests::BaseService
include Git::Logger include Git::Logger
def idempotent?
true
end
def execute def execute
# If performing a squash would result in no change, then # If performing a squash would result in no change, then
# immediately return a success message without performing a squash # immediately return a success message without performing a squash
return success(squash_sha: merge_request.diff_head_sha) if squash_redundant? if merge_request.commits_count < 2 && message.nil?
return success(squash_sha: merge_request.diff_head_sha)
end
if merge_request.squash_in_progress? if merge_request.squash_in_progress?
return error(s_('MergeRequests|Squash task canceled: another squash is already in progress.')) return error(s_('MergeRequests|Squash task canceled: another squash is already in progress.'))
...@@ -22,12 +20,6 @@ module MergeRequests ...@@ -22,12 +20,6 @@ module MergeRequests
private private
def squash_redundant?
return true if merge_request.merged?
merge_request.commits_count < 2 && message.nil?
end
def squash! def squash!
squash_sha = repository.squash(current_user, merge_request, message || merge_request.default_squash_commit_message) squash_sha = repository.squash(current_user, merge_request, message || merge_request.default_squash_commit_message)
......
---
title: Disallow distinct count for regular batch count
merge_request: 28518
author:
type: performance
---
title: Make MergeService idempotent
merge_request: 24708
author:
type: changed
...@@ -486,9 +486,9 @@ to start again from scratch, there are a few steps that can help you: ...@@ -486,9 +486,9 @@ to start again from scratch, there are a few steps that can help you:
1. Reset the Tracking Database 1. Reset the Tracking Database
```shell ```shell
gitlab-rake geo:db:drop gitlab-rake geo:db:drop # on a secondary app node
gitlab-ctl reconfigure gitlab-ctl reconfigure # on the tracking database node
gitlab-rake geo:db:setup gitlab-rake geo:db:setup # on a secondary app node
``` ```
1. Restart previously stopped services 1. Restart previously stopped services
......
...@@ -52,7 +52,7 @@ Some applications are installable only for a project-level cluster. ...@@ -52,7 +52,7 @@ Some applications are installable only for a project-level cluster.
Support for installing these applications in a group-level cluster is Support for installing these applications in a group-level cluster is
planned for future releases. planned for future releases.
For updates, see [the issue tracking For updates, see [the issue tracking
progress](https://gitlab.com/gitlab-org/gitlab-foss/issues/51989). progress](https://gitlab.com/gitlab-org/gitlab/-/issues/24411).
CAUTION: **Caution:** CAUTION: **Caution:**
If you have an existing Kubernetes cluster with Helm already installed, If you have an existing Kubernetes cluster with Helm already installed,
......
...@@ -143,7 +143,7 @@ below. ...@@ -143,7 +143,7 @@ below.
CAUTION: **Warning:** CAUTION: **Warning:**
Interactive Web Terminals for the Web IDE is currently in **Beta**. Interactive Web Terminals for the Web IDE is currently in **Beta**.
Shared Runners [do not yet support Interactive Web Terminals](https://gitlab.com/gitlab-org/gitlab-foss/issues/52611), Shared Runners [do not yet support Interactive Web Terminals](https://gitlab.com/gitlab-org/gitlab/-/issues/24674),
so you would need to use your own private Runner(s) to make use of this feature. so you would need to use your own private Runner(s) to make use of this feature.
[Interactive Web Terminals](../../../ci/interactive_web_terminal/index.md) [Interactive Web Terminals](../../../ci/interactive_web_terminal/index.md)
......
...@@ -56,6 +56,7 @@ module Gitlab ...@@ -56,6 +56,7 @@ module Gitlab
def count(batch_size: nil, mode: :itself, start: nil, finish: nil) def count(batch_size: nil, mode: :itself, start: nil, finish: nil)
raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open? raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open?
raise "The mode #{mode.inspect} is not supported" unless [:itself, :distinct].include?(mode) raise "The mode #{mode.inspect} is not supported" unless [:itself, :distinct].include?(mode)
raise 'Use distinct count for optimized distinct counting' if @relation.limit(1).distinct_value.present? && mode != :distinct
# non-distinct have better performance # non-distinct have better performance
batch_size ||= mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE batch_size ||= mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE
......
...@@ -3794,9 +3794,6 @@ msgstr "" ...@@ -3794,9 +3794,6 @@ msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node" msgid "Choose which shards you wish to synchronize to this secondary node"
msgstr "" msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "Choose which status most accurately reflects the current state of this issue:" msgid "Choose which status most accurately reflects the current state of this issue:"
msgstr "" msgstr ""
...@@ -9416,30 +9413,9 @@ msgstr "" ...@@ -9416,30 +9413,9 @@ msgstr ""
msgid "Geo|All projects are being scheduled for re-verify" msgid "Geo|All projects are being scheduled for re-verify"
msgstr "" msgstr ""
msgid "Geo|Allow this secondary node to replicate content on Object Storage"
msgstr ""
msgid "Geo|Batch operations" msgid "Geo|Batch operations"
msgstr "" msgstr ""
msgid "Geo|Choose which groups you wish to synchronize to this secondary node."
msgstr ""
msgid "Geo|Container repositories sync capacity"
msgstr ""
msgid "Geo|Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Geo|Control the maximum concurrency of container repository operations for this Geo node"
msgstr ""
msgid "Geo|Control the maximum concurrency of verification operations for this Geo node"
msgstr ""
msgid "Geo|Control the minimum interval in days that a repository should be reverified for this primary node"
msgstr ""
msgid "Geo|Could not remove tracking entry for an existing project." msgid "Geo|Could not remove tracking entry for an existing project."
msgstr "" msgstr ""
...@@ -9449,24 +9425,12 @@ msgstr "" ...@@ -9449,24 +9425,12 @@ msgstr ""
msgid "Geo|Failed" msgid "Geo|Failed"
msgstr "" msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Geo Status" msgid "Geo|Geo Status"
msgstr "" msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
msgid "Geo|If enabled, and if object storage is enabled, GitLab will handle Object Storage replication using Geo"
msgstr ""
msgid "Geo|In sync" msgid "Geo|In sync"
msgstr "" msgstr ""
msgid "Geo|Internal URL (optional)"
msgstr ""
msgid "Geo|Last repository check run" msgid "Geo|Last repository check run"
msgstr "" msgstr ""
...@@ -9512,9 +9476,6 @@ msgstr "" ...@@ -9512,9 +9476,6 @@ msgstr ""
msgid "Geo|Projects in certain storage shards" msgid "Geo|Projects in certain storage shards"
msgstr "" msgstr ""
msgid "Geo|Re-verification interval"
msgstr ""
msgid "Geo|Redownload" msgid "Geo|Redownload"
msgstr "" msgstr ""
...@@ -9527,9 +9488,6 @@ msgstr "" ...@@ -9527,9 +9488,6 @@ msgstr ""
msgid "Geo|Remove tracking database entry" msgid "Geo|Remove tracking database entry"
msgstr "" msgstr ""
msgid "Geo|Repository sync capacity"
msgstr ""
msgid "Geo|Resync" msgid "Geo|Resync"
msgstr "" msgstr ""
...@@ -9545,15 +9503,6 @@ msgstr "" ...@@ -9545,15 +9503,6 @@ msgstr ""
msgid "Geo|Reverify all projects" msgid "Geo|Reverify all projects"
msgstr "" msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Selective synchronization"
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
msgid "Geo|Status" msgid "Geo|Status"
msgstr "" msgstr ""
...@@ -9566,18 +9515,12 @@ msgstr "" ...@@ -9566,18 +9515,12 @@ msgstr ""
msgid "Geo|Synchronization failed - %{error}" msgid "Geo|Synchronization failed - %{error}"
msgstr "" msgstr ""
msgid "Geo|The URL defined on the primary node that secondary nodes should use to contact it. Defaults to URL"
msgstr ""
msgid "Geo|The database is currently %{db_lag} behind the primary node." msgid "Geo|The database is currently %{db_lag} behind the primary node."
msgstr "" msgstr ""
msgid "Geo|The node is currently %{minutes_behind} behind the primary node." msgid "Geo|The node is currently %{minutes_behind} behind the primary node."
msgstr "" msgstr ""
msgid "Geo|This is a primary node"
msgstr ""
msgid "Geo|Tracking database entry will be removed. Are you sure?" msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr "" msgstr ""
...@@ -9587,15 +9530,9 @@ msgstr "" ...@@ -9587,15 +9530,9 @@ msgstr ""
msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed." msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed."
msgstr "" msgstr ""
msgid "Geo|URL"
msgstr ""
msgid "Geo|Unknown state" msgid "Geo|Unknown state"
msgstr "" msgstr ""
msgid "Geo|Verification capacity"
msgstr ""
msgid "Geo|Verification failed - %{error}" msgid "Geo|Verification failed - %{error}"
msgstr "" msgstr ""
...@@ -20171,9 +20108,6 @@ msgstr "" ...@@ -20171,9 +20108,6 @@ msgstr ""
msgid "The unique identifier for the Geo node. Must match %{geoNodeName} if it is set in gitlab.rb, otherwise it must match %{externalUrl} with a trailing slash" msgid "The unique identifier for the Geo node. Must match %{geoNodeName} if it is set in gitlab.rb, otherwise it must match %{externalUrl} with a trailing slash"
msgstr "" msgstr ""
msgid "The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url` with a trailing slash"
msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination." msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "" msgstr ""
......
...@@ -725,7 +725,7 @@ describe Projects::IssuesController do ...@@ -725,7 +725,7 @@ describe Projects::IssuesController do
stub_feature_flags(allow_possible_spam: false) stub_feature_flags(allow_possible_spam: false)
end end
it 'rejects an issue recognized as a spam' do it 'rejects an issue recognized as spam' do
expect { update_issue }.not_to change { issue.reload.title } expect { update_issue }.not_to change { issue.reload.title }
end end
...@@ -981,7 +981,7 @@ describe Projects::IssuesController do ...@@ -981,7 +981,7 @@ describe Projects::IssuesController do
stub_feature_flags(allow_possible_spam: false) stub_feature_flags(allow_possible_spam: false)
end end
it 'rejects an issue recognized as a spam' do it 'rejects an issue recognized as spam' do
expect { post_spam_issue }.not_to change(Issue, :count) expect { post_spam_issue }.not_to change(Issue, :count)
end end
......
...@@ -23,7 +23,7 @@ describe 'New issue', :js do ...@@ -23,7 +23,7 @@ describe 'New issue', :js do
sign_in(user) sign_in(user)
end end
context 'when identified as a spam' do context 'when identified as spam' do
before do before do
WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "true", status: 200) WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "true", status: 200)
...@@ -74,7 +74,7 @@ describe 'New issue', :js do ...@@ -74,7 +74,7 @@ describe 'New issue', :js do
end end
end end
context 'when not identified as a spam' do context 'when not identified as spam' do
before do before do
WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: 'false', status: 200) WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: 'false', status: 200)
......
...@@ -52,7 +52,7 @@ shared_examples_for 'snippet editor' do ...@@ -52,7 +52,7 @@ shared_examples_for 'snippet editor' do
end end
end end
context 'when identified as a spam' do context 'when identified as spam' do
before do before do
WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "true", status: 200) WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "true", status: 200)
end end
...@@ -66,7 +66,7 @@ shared_examples_for 'snippet editor' do ...@@ -66,7 +66,7 @@ shared_examples_for 'snippet editor' do
end end
end end
context 'when not identified as a spam' do context 'when not identified as spam' do
before do before do
WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "false", status: 200) WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "false", status: 200)
end end
......
...@@ -49,6 +49,12 @@ describe Gitlab::Database::BatchCount do ...@@ -49,6 +49,12 @@ describe Gitlab::Database::BatchCount do
[1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(5) } [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(5) }
end end
it 'will raise an error if distinct count is requested' do
expect do
described_class.batch_count(model.distinct(column))
end.to raise_error 'Use distinct count for optimized distinct counting'
end
context 'in a transaction' do context 'in a transaction' do
let(:in_transaction) { true } let(:in_transaction) { true }
......
...@@ -383,20 +383,21 @@ describe Issues::CreateService do ...@@ -383,20 +383,21 @@ describe Issues::CreateService do
context 'when recaptcha was verified' do context 'when recaptcha was verified' do
let(:log_user) { user } let(:log_user) { user }
let(:spam_logs) { create_list(:spam_log, 2, user: log_user, title: 'Awesome issue') } let(:spam_logs) { create_list(:spam_log, 2, user: log_user, title: 'Awesome issue') }
let(:target_spam_log) { spam_logs.last }
before do before do
opts[:recaptcha_verified] = true opts[:recaptcha_verified] = true
opts[:spam_log_id] = spam_logs.last.id opts[:spam_log_id] = target_spam_log.id
expect(Spam::AkismetService).not_to receive(:new) expect(Spam::AkismetService).not_to receive(:new)
end end
it 'does no mark an issue as a spam ' do it 'does not mark an issue as spam' do
expect(issue).not_to be_spam expect(issue).not_to be_spam
end end
it 'an issue is valid ' do it 'issue is valid ' do
expect(issue.valid?).to be_truthy expect(issue).to be_valid
end end
it 'does not assign a spam_log to an issue' do it 'does not assign a spam_log to an issue' do
...@@ -431,7 +432,7 @@ describe Issues::CreateService do ...@@ -431,7 +432,7 @@ describe Issues::CreateService do
end end
context 'when issuables_recaptcha_enabled feature flag is true' do context 'when issuables_recaptcha_enabled feature flag is true' do
it 'marks an issue as a spam ' do it 'marks an issue as spam' do
expect(issue).to be_spam expect(issue).to be_spam
end end
...@@ -444,7 +445,7 @@ describe Issues::CreateService do ...@@ -444,7 +445,7 @@ describe Issues::CreateService do
.to have_spam_log(title: issue.title, description: issue.description, user_id: user.id, noteable_type: 'Issue') .to have_spam_log(title: issue.title, description: issue.description, user_id: user.id, noteable_type: 'Issue')
end end
it 'assigns a spam_log to an issue' do it 'assigns a spam_log to the issue' do
expect(issue.spam_log).to eq(SpamLog.last) expect(issue.spam_log).to eq(SpamLog.last)
end end
end end
...@@ -454,7 +455,7 @@ describe Issues::CreateService do ...@@ -454,7 +455,7 @@ describe Issues::CreateService do
stub_feature_flags(allow_possible_spam: true) stub_feature_flags(allow_possible_spam: true)
end end
it 'does not mark an issue as a spam ' do it 'does not mark an issue as spam' do
expect(issue).not_to be_spam expect(issue).not_to be_spam
end end
...@@ -480,7 +481,7 @@ describe Issues::CreateService do ...@@ -480,7 +481,7 @@ describe Issues::CreateService do
end end
end end
it 'does not mark an issue as a spam ' do it 'does not mark an issue as spam' do
expect(issue).not_to be_spam expect(issue).not_to be_spam
end end
......
...@@ -47,23 +47,6 @@ describe MergeRequests::MergeService do ...@@ -47,23 +47,6 @@ describe MergeRequests::MergeService do
expect(note.note).to include 'merged' expect(note.note).to include 'merged'
end end
it 'is idempotent' do
repository = project.repository
commit_count = repository.commit_count
merge_commit = merge_request.merge_commit.id
# a first invocation of execute is performed on the before block
service.execute(merge_request)
expect(merge_request.merge_error).to be_falsey
expect(merge_request).to be_valid
expect(merge_request).to be_merged
expect(repository.commits_by(oids: [merge_commit]).size).to eq(1)
expect(repository.commit_count).to eq(commit_count)
expect(merge_request.in_progress_merge_commit_sha).to be_nil
end
context 'when squashing' do context 'when squashing' do
let(:merge_params) do let(:merge_params) do
{ commit_message: 'Merge commit message', { commit_message: 'Merge commit message',
......
...@@ -17,6 +17,7 @@ describe MergeRequests::PostMergeService do ...@@ -17,6 +17,7 @@ describe MergeRequests::PostMergeService do
it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do
# Cache the counter before the MR changed state. # Cache the counter before the MR changed state.
project.open_merge_requests_count project.open_merge_requests_count
merge_request.update!(state: 'merged')
service = described_class.new(project, user, {}) service = described_class.new(project, user, {})
......
...@@ -137,24 +137,6 @@ describe MergeRequests::SquashService do ...@@ -137,24 +137,6 @@ describe MergeRequests::SquashService do
include_examples 'the squash succeeds' include_examples 'the squash succeeds'
end end
context 'when the merge request has already been merged' do
let(:merge_request) { merge_request_with_one_commit }
it 'checks the side-effects for multiple calls' do
merge_request.mark_as_merged
expect(service).to be_idempotent
expect { IdempotentWorkerHelper::WORKER_EXEC_TIMES.times { service.execute } }.not_to raise_error
end
it 'idempotently returns a success' do
merge_request.mark_as_merged
result = service.execute
expect(result).to match(status: :success, squash_sha: merge_request.diff_head_sha)
end
end
context 'git errors' do context 'git errors' do
let(:merge_request) { merge_request_with_only_new_files } let(:merge_request) { merge_request_with_only_new_files }
let(:error) { 'A test error' } let(:error) { 'A test error' }
......
...@@ -76,7 +76,7 @@ describe Snippets::CreateService do ...@@ -76,7 +76,7 @@ describe Snippets::CreateService do
shared_examples 'spam check is performed' do shared_examples 'spam check is performed' do
shared_examples 'marked as spam' do shared_examples 'marked as spam' do
it 'marks a snippet as spam ' do it 'marks a snippet as spam' do
expect(snippet).to be_spam expect(snippet).to be_spam
end end
......
...@@ -91,9 +91,7 @@ describe Spam::SpamCheckService do ...@@ -91,9 +91,7 @@ describe Spam::SpamCheckService do
end end
it 'updates spam log' do it 'updates spam log' do
subject expect { subject }.to change { existing_spam_log.reload.recaptcha_verified }.from(false).to(true)
expect(existing_spam_log.reload.recaptcha_verified).to be_truthy
end end
end end
...@@ -137,7 +135,7 @@ describe Spam::SpamCheckService do ...@@ -137,7 +135,7 @@ describe Spam::SpamCheckService do
it 'marks as spam' do it 'marks as spam' do
subject subject
expect(issue.reload.spam).to be_truthy expect(issue).to be_spam
end end
end end
...@@ -147,7 +145,7 @@ describe Spam::SpamCheckService do ...@@ -147,7 +145,7 @@ describe Spam::SpamCheckService do
it 'does not mark as spam' do it 'does not mark as spam' do
subject subject
expect(issue.spam).to be_falsey expect(issue).not_to be_spam
end end
end end
end end
......
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