diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7605f10b14e..9225f04ad79 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,7 +44,7 @@ workflow: # For the 2-hourly scheduled pipelines, we set specific variables. - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "2-hourly"' variables: - RUBY_VERSION: "3.0" + RUBY_VERSION: "2.7" CRYSTALBALL: "true" # For `$CI_DEFAULT_BRANCH` branch, create a pipeline (this includes on schedules, pushes, merges, etc.). - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' diff --git a/.rubocop_todo/cop/static_translation_definition.yml b/.rubocop_todo/cop/static_translation_definition.yml index 04893f40631..0a4f99e8a93 100644 --- a/.rubocop_todo/cop/static_translation_definition.yml +++ b/.rubocop_todo/cop/static_translation_definition.yml @@ -25,13 +25,3 @@ Cop/StaticTranslationDefinition: - 'ee/app/models/incident_management/escalation_policy.rb' - 'ee/app/models/incident_management/escalation_rule.rb' - 'ee/app/models/vulnerabilities/read.rb' - - 'ee/lib/ee/gitlab/quick_actions/epic_actions.rb' - - 'ee/lib/ee/gitlab/quick_actions/issue_actions.rb' - - 'ee/lib/ee/gitlab/quick_actions/issue_and_merge_request_actions.rb' - - 'ee/lib/ee/gitlab/quick_actions/merge_request_actions.rb' - - 'lib/gitlab/quick_actions/commit_actions.rb' - - 'lib/gitlab/quick_actions/issuable_actions.rb' - - 'lib/gitlab/quick_actions/issue_actions.rb' - - 'lib/gitlab/quick_actions/issue_and_merge_request_actions.rb' - - 'lib/gitlab/quick_actions/merge_request_actions.rb' - - 'lib/gitlab/quick_actions/relate_actions.rb' diff --git a/lib/gitlab/quick_actions/commit_actions.rb b/lib/gitlab/quick_actions/commit_actions.rb index 49f5ddf24eb..661e768ffc4 100644 --- a/lib/gitlab/quick_actions/commit_actions.rb +++ b/lib/gitlab/quick_actions/commit_actions.rb @@ -8,7 +8,7 @@ module Gitlab included do # Commit only quick actions definitions - desc _('Tag this commit.') + desc { _('Tag this commit.') } explanation do |tag_name, message| if message.present? _("Tags this commit to %{tag_name} with \"%{message}\".") % { tag_name: tag_name, message: message } diff --git a/lib/gitlab/quick_actions/issuable_actions.rb b/lib/gitlab/quick_actions/issuable_actions.rb index 4bac0643a91..259d9e38d65 100644 --- a/lib/gitlab/quick_actions/issuable_actions.rb +++ b/lib/gitlab/quick_actions/issuable_actions.rb @@ -55,7 +55,7 @@ module Gitlab @updates[:state_event] = 'reopen' end - desc _('Change title') + desc { _('Change title') } explanation do |title_param| _('Changes the title to "%{title_param}".') % { title_param: title_param } end @@ -72,7 +72,7 @@ module Gitlab @updates[:title] = title_param end - desc _('Add label(s)') + desc { _('Add label(s)') } explanation do |labels_param| labels = find_label_references(labels_param) @@ -91,7 +91,7 @@ module Gitlab run_label_command(labels: find_labels(labels_param), command: :label, updates_key: :add_label_ids) end - desc _('Remove all or specific label(s)') + desc { _('Remove all or specific label(s)') } explanation do |labels_param = nil| label_references = labels_param.present? ? find_label_references(labels_param) : [] if label_references.any? @@ -128,7 +128,7 @@ module Gitlab @execution_message[:unlabel] = remove_label_message(label_references) end - desc _('Replace all label(s)') + desc { _('Replace all label(s)') } explanation do |labels_param| labels = find_label_references(labels_param) "Replaces all labels with #{labels.join(' ')} #{'label'.pluralize(labels.count)}." if labels.any? @@ -144,9 +144,9 @@ module Gitlab run_label_command(labels: find_labels(labels_param), command: :relabel, updates_key: :label_ids) end - desc _('Add a to do') - explanation _('Adds a to do.') - execution_message _('Added a to do.') + desc { _('Add a to do') } + explanation { _('Adds a to do.') } + execution_message { _('Added a to do.') } types Issuable condition do quick_action_target.persisted? && @@ -156,9 +156,9 @@ module Gitlab @updates[:todo_event] = 'add' end - desc _('Mark to do as done') - explanation _('Marks to do as done.') - execution_message _('Marked to do as done.') + desc { _('Mark to do as done') } + explanation { _('Marks to do as done.') } + execution_message { _('Marked to do as done.') } types Issuable condition do quick_action_target.persisted? && @@ -168,7 +168,7 @@ module Gitlab @updates[:todo_event] = 'done' end - desc _('Subscribe') + desc { _('Subscribe') } explanation do _('Subscribes to this %{quick_action_target}.') % { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } @@ -186,7 +186,7 @@ module Gitlab @updates[:subscription_event] = 'subscribe' end - desc _('Unsubscribe') + desc { _('Unsubscribe') } explanation do _('Unsubscribes from this %{quick_action_target}.') % { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } @@ -204,7 +204,7 @@ module Gitlab @updates[:subscription_event] = 'unsubscribe' end - desc _('Toggle emoji award') + desc { _('Toggle emoji award') } explanation do |name| _("Toggles :%{name}: emoji award.") % { name: name } if name end @@ -226,22 +226,22 @@ module Gitlab end end - desc _("Append the comment with %{shrug}") % { shrug: SHRUG } + desc { _("Append the comment with %{shrug}") % { shrug: SHRUG } } params '' types Issuable substitution :shrug do |comment| "#{comment} #{SHRUG}" end - desc _("Append the comment with %{tableflip}") % { tableflip: TABLEFLIP } + desc { _("Append the comment with %{tableflip}") % { tableflip: TABLEFLIP } } params '' types Issuable substitution :tableflip do |comment| "#{comment} #{TABLEFLIP}" end - desc _('Set severity') - explanation _('Sets the severity') + desc { _('Set severity') } + explanation { _('Sets the severity') } params '1 / S1 / Critical' types Issue condition do diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb index e9ef4157aa3..189627506f3 100644 --- a/lib/gitlab/quick_actions/issue_actions.rb +++ b/lib/gitlab/quick_actions/issue_actions.rb @@ -8,7 +8,7 @@ module Gitlab included do # Issue only quick actions definition - desc _('Set due date') + desc { _('Set due date') } explanation do |due_date| _("Sets the due date to %{due_date}.") % { due_date: due_date.strftime('%b %-d, %Y') } if due_date end @@ -32,9 +32,9 @@ module Gitlab end end - desc _('Remove due date') - explanation _('Removes the due date.') - execution_message _('Removed the due date.') + desc { _('Remove due date') } + explanation { _('Removes the due date.') } + execution_message { _('Removed the due date.') } types Issue condition do quick_action_target.persisted? && @@ -46,7 +46,7 @@ module Gitlab @updates[:due_date] = nil end - desc _('Move issue from one column of the board to another') + desc { _('Move issue from one column of the board to another') } explanation do |target_list_name| label = find_label_references(target_list_name).first _("Moves issue to %{label} column in the board.") % { label: label } if label @@ -78,7 +78,7 @@ module Gitlab @execution_message[:board_move] = message end - desc _('Mark this issue as a duplicate of another issue') + desc { _('Mark this issue as a duplicate of another issue') } explanation do |duplicate_reference| _("Marks this issue as a duplicate of %{duplicate_reference}.") % { duplicate_reference: duplicate_reference } end @@ -102,7 +102,7 @@ module Gitlab @execution_message[:duplicate] = message end - desc _('Clone this issue') + desc { _('Clone this issue') } explanation do |project = quick_action_target.project.full_path| _("Clones this issue, without comments, to %{project}.") % { project: project } end @@ -137,7 +137,7 @@ module Gitlab @execution_message[:clone] = message end - desc _('Move this issue to another project.') + desc { _('Move this issue to another project.') } explanation do |path_to_project| _("Moves this issue to %{path_to_project}.") % { path_to_project: path_to_project } end @@ -161,7 +161,7 @@ module Gitlab @execution_message[:move] = message end - desc _('Make issue confidential') + desc { _('Make issue confidential') } explanation do _('Makes this issue confidential.') end @@ -178,7 +178,7 @@ module Gitlab @updates[:confidential] = true end - desc _('Create a merge request') + desc { _('Create a merge request') } explanation do |branch_name = nil| if branch_name _("Creates branch '%{branch_name}' and a merge request to resolve this issue.") % { branch_name: branch_name } @@ -205,8 +205,8 @@ module Gitlab } end - desc _('Add Zoom meeting') - explanation _('Adds a Zoom meeting.') + desc { _('Add Zoom meeting') } + explanation { _('Adds a Zoom meeting.') } params '' types Issue condition do @@ -222,9 +222,9 @@ module Gitlab @updates.merge!(result.payload) if result.payload end - desc _('Remove Zoom meeting') - explanation _('Remove Zoom meeting.') - execution_message _('Zoom meeting removed') + desc { _('Remove Zoom meeting') } + explanation { _('Remove Zoom meeting.') } + execution_message { _('Zoom meeting removed') } types Issue condition do @zoom_service = zoom_link_service @@ -235,8 +235,8 @@ module Gitlab @execution_message[:remove_zoom] = result.message end - desc _('Add email participant(s)') - explanation _('Adds email participant(s).') + desc { _('Add email participant(s)') } + explanation { _('Adds email participant(s).') } params 'email1@example.com email2@example.com (up to 6 emails)' types Issue condition do @@ -264,8 +264,8 @@ module Gitlab end end - desc _('Promote issue to incident') - explanation _('Promotes issue to incident') + desc { _('Promote issue to incident') } + explanation { _('Promotes issue to incident') } types Issue condition do quick_action_target.persisted? && @@ -285,8 +285,8 @@ module Gitlab end end - desc _('Add customer relation contacts') - explanation _('Add customer relation contact(s).') + desc { _('Add customer relation contacts') } + explanation { _('Add customer relation contact(s).') } params '[contact:contact@example.com] [contact:person@example.org]' types Issue condition do @@ -300,8 +300,8 @@ module Gitlab @updates[:add_contacts] = contact_emails.split(' ') end - desc _('Remove customer relation contacts') - explanation _('Remove customer relation contact(s).') + desc { _('Remove customer relation contacts') } + explanation { _('Remove customer relation contact(s).') } params '[contact:contact@example.com] [contact:person@example.org]' types Issue condition do diff --git a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb index 4a75fa0a571..a0faf8dd460 100644 --- a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb +++ b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb @@ -8,7 +8,7 @@ module Gitlab included do # Issue, MergeRequest: quick actions definitions - desc _('Assign') + desc { _('Assign') } explanation do |users| _('Assigns %{assignee_users_sentence}.') % { assignee_users_sentence: assignee_users_sentence(users) } end @@ -81,7 +81,7 @@ module Gitlab end end - desc _('Set milestone') + desc { _('Set milestone') } explanation do |milestone| _("Sets the milestone to %{milestone_reference}.") % { milestone_reference: milestone.to_reference } if milestone end @@ -103,7 +103,7 @@ module Gitlab @updates[:milestone_id] = milestone.id if milestone end - desc _('Remove milestone') + desc { _('Remove milestone') } explanation do _("Removes %{milestone_reference} milestone.") % { milestone_reference: quick_action_target.milestone.to_reference(format: :name) } end @@ -121,7 +121,7 @@ module Gitlab @updates[:milestone_id] = nil end - desc _('Copy labels and milestone from other issue or merge request in this project') + desc { _('Copy labels and milestone from other issue or merge request in this project') } explanation do |source_issuable| _("Copy labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference } end @@ -143,7 +143,7 @@ module Gitlab end end - desc _('Set time estimate') + desc { _('Set time estimate') } explanation do |time_estimate| formatted_time_estimate = format_time_estimate(time_estimate) _("Sets time estimate to %{time_estimate}.") % { time_estimate: formatted_time_estimate } if formatted_time_estimate @@ -167,7 +167,7 @@ module Gitlab end end - desc _('Add or subtract spent time') + desc { _('Add or subtract spent time') } explanation do |time_spent, time_spent_date| spend_time_message(time_spent, time_spent_date, false) end @@ -194,9 +194,9 @@ module Gitlab end end - desc _('Remove time estimate') - explanation _('Removes time estimate.') - execution_message _('Removed time estimate.') + desc { _('Remove time estimate') } + explanation { _('Removes time estimate.') } + execution_message { _('Removed time estimate.') } types Issue, MergeRequest condition do quick_action_target.persisted? && @@ -206,9 +206,9 @@ module Gitlab @updates[:time_estimate] = 0 end - desc _('Remove spent time') - explanation _('Removes spent time.') - execution_message _('Removed spent time.') + desc { _('Remove spent time') } + explanation { _('Removes spent time.') } + execution_message { _('Removed spent time.') } condition do quick_action_target.persisted? && current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) @@ -218,9 +218,9 @@ module Gitlab @updates[:spend_time] = { duration: :reset, user_id: current_user.id } end - desc _("Lock the discussion") - explanation _("Locks the discussion.") - execution_message _("Locked the discussion.") + desc { _("Lock the discussion") } + explanation { _("Locks the discussion.") } + execution_message { _("Locked the discussion.") } types Issue, MergeRequest condition do quick_action_target.persisted? && @@ -231,9 +231,9 @@ module Gitlab @updates[:discussion_locked] = true end - desc _("Unlock the discussion") - explanation _("Unlocks the discussion.") - execution_message _("Unlocked the discussion.") + desc { _("Unlock the discussion") } + explanation { _("Unlocks the discussion.") } + execution_message { _("Unlocked the discussion.") } types Issue, MergeRequest condition do quick_action_target.persisted? && diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb index 2d1341e9869..6137e257837 100644 --- a/lib/gitlab/quick_actions/merge_request_actions.rb +++ b/lib/gitlab/quick_actions/merge_request_actions.rb @@ -117,7 +117,7 @@ module Gitlab @updates[:wip_event] = quick_action_target.draft? ? 'ready' : 'draft' end - desc _('Set target branch') + desc { _('Set target branch') } explanation do |branch_name| _('Sets target branch to %{branch_name}.') % { branch_name: branch_name } end @@ -138,8 +138,8 @@ module Gitlab @updates[:target_branch] = branch_name if project.repository.branch_exists?(branch_name) end - desc _('Submit a review') - explanation _('Submit the current review.') + desc { _('Submit a review') } + explanation { _('Submit the current review.') } types MergeRequest condition do quick_action_target.persisted? @@ -155,8 +155,8 @@ module Gitlab end end - desc _('Approve a merge request') - explanation _('Approve the current merge request.') + desc { _('Approve a merge request') } + explanation { _('Approve the current merge request.') } types MergeRequest condition do quick_action_target.persisted? && quick_action_target.can_be_approved_by?(current_user) @@ -169,8 +169,8 @@ module Gitlab @execution_message[:approve] = _('Approved the current merge request.') end - desc _('Unapprove a merge request') - explanation _('Unapprove the current merge request.') + desc { _('Unapprove a merge request') } + explanation { _('Unapprove the current merge request.') } types MergeRequest condition do quick_action_target.persisted? && quick_action_target.can_be_unapproved_by?(current_user) diff --git a/lib/gitlab/quick_actions/relate_actions.rb b/lib/gitlab/quick_actions/relate_actions.rb index 1de23523f01..4c8035f192e 100644 --- a/lib/gitlab/quick_actions/relate_actions.rb +++ b/lib/gitlab/quick_actions/relate_actions.rb @@ -7,7 +7,7 @@ module Gitlab include ::Gitlab::QuickActions::Dsl included do - desc _('Mark this issue as related to another issue') + desc { _('Mark this issue as related to another issue') } explanation do |related_reference| _('Marks this issue as related to %{issue_ref}.') % { issue_ref: related_reference } end diff --git a/lib/tasks/gitlab/db/lock_writes.rake b/lib/tasks/gitlab/db/lock_writes.rake index 1d1a67aa2a1..b57c2860fe3 100644 --- a/lib/tasks/gitlab/db/lock_writes.rake +++ b/lib/tasks/gitlab/db/lock_writes.rake @@ -11,12 +11,10 @@ namespace :gitlab do schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection) Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name| - connection.transaction do - if schemas_for_connection.include?(schema_name.to_sym) - drop_write_trigger(database_name, connection, table_name) - else - create_write_trigger(database_name, connection, table_name) - end + if schemas_for_connection.include?(schema_name.to_sym) + drop_write_trigger(database_name, connection, table_name) + else + create_write_trigger(database_name, connection, table_name) end end end @@ -57,7 +55,7 @@ namespace :gitlab do end def create_write_trigger(database_name, connection, table_name) - puts "#{database_name}: '#{table_name}'... Lock Writes".color(:red) + puts "#{database_name}: '#{table_name}'... Lock Writes".color(:yellow) sql = <<-SQL DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name}; CREATE TRIGGER #{write_trigger_name(table_name)} @@ -66,7 +64,9 @@ namespace :gitlab do FOR EACH STATEMENT EXECUTE FUNCTION #{TRIGGER_FUNCTION_NAME}(); SQL - connection.execute(sql) + with_retries(connection) do + connection.execute(sql) + end end def drop_write_trigger(database_name, connection, table_name) @@ -75,7 +75,41 @@ namespace :gitlab do DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name} SQL - connection.execute(sql) + with_retries(connection) do + connection.execute(sql) + end + end + + def with_retries(connection, &block) + with_statement_timeout_retries do + with_lock_retries(connection) do + yield + end + end + end + + def with_statement_timeout_retries(times = 5) + current_iteration = 1 + begin + yield + rescue ActiveRecord::QueryCanceled => err + puts "Retrying after #{err.message}" + + if current_iteration <= times + current_iteration += 1 + retry + else + raise err + end + end + end + + def with_lock_retries(connection, &block) + Gitlab::Database::WithLockRetries.new( + klass: "gitlab:db:lock_writes", + logger: Gitlab::AppLogger, + connection: connection + ).run(&block) end def write_trigger_name(table_name) diff --git a/spec/tasks/gitlab/db/lock_writes_rake_spec.rb b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb index da62d136c25..034c520887e 100644 --- a/spec/tasks/gitlab/db/lock_writes_rake_spec.rb +++ b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb @@ -106,6 +106,31 @@ RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_r main_connection.execute("truncate ci_build_needs") end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_build_needs" is write protected/) end + + it 'retries again if it receives a statement_timeout a few number of times' do + error_message = "PG::QueryCanceled: ERROR: canceling statement due to statement timeout" + call_count = 0 + allow(main_connection).to receive(:execute) do |statement| + if statement.include?("CREATE TRIGGER") + call_count += 1 + raise(ActiveRecord::QueryCanceled, error_message) if call_count.even? + end + end + run_rake_task('gitlab:db:lock_writes') + end + + it 'raises the exception if it happened many times' do + error_message = "PG::QueryCanceled: ERROR: canceling statement due to statement timeout" + allow(main_connection).to receive(:execute) do |statement| + if statement.include?("CREATE TRIGGER") + raise(ActiveRecord::QueryCanceled, error_message) + end + end + + expect do + run_rake_task('gitlab:db:lock_writes') + end.to raise_error(ActiveRecord::QueryCanceled) + end end context 'when unlocking writes' do