Commit 84e39afc authored by Stan Hu's avatar Stan Hu

Merge branch '338254-replace-safe-ensure-unique-with-upsert' into 'master'

Remove subtransaction from HistoricalStatistics

See merge request gitlab-org/gitlab!68124
parents 1dae49eb d3205996
...@@ -3,7 +3,40 @@ ...@@ -3,7 +3,40 @@
module Vulnerabilities module Vulnerabilities
module HistoricalStatistics module HistoricalStatistics
class UpdateService class UpdateService
VULNERABILITY_STATISTIC_ATTRIBUTES = %w(total critical high medium low unknown info letter_grade).freeze UPSERT_SQL = <<~SQL
INSERT INTO vulnerability_historical_statistics
(project_id, total, info, unknown, low, medium, high, critical, letter_grade, date, created_at, updated_at)
(%{stats_sql})
ON CONFLICT (project_id, date)
DO UPDATE SET
total = EXCLUDED.total,
info = EXCLUDED.info,
unknown = EXCLUDED.unknown,
low = EXCLUDED.low,
medium = EXCLUDED.medium,
high = EXCLUDED.high,
critical = EXCLUDED.critical,
letter_grade = EXCLUDED.letter_grade,
updated_at = EXCLUDED.updated_at
SQL
STATS_SQL = <<~SQL
SELECT
project_id,
total,
info,
unknown,
low,
medium,
high,
critical,
letter_grade,
updated_at AS date,
now() AS created_at,
now() AS updated_at
FROM vulnerability_statistics
WHERE project_id = %{project_id}
SQL
def self.update_for(project) def self.update_for(project)
new(project).execute new(project).execute
...@@ -13,22 +46,17 @@ module Vulnerabilities ...@@ -13,22 +46,17 @@ module Vulnerabilities
@project = project @project = project
end end
# rubocop: disable CodeReuse/ActiveRecord
def execute def execute
return unless update_statistic? return unless update_statistic?
::Vulnerabilities::HistoricalStatistic.safe_ensure_unique(retries: 1) do ApplicationRecord.connection.execute(upsert_sql)
historical_statistic = vulnerability_historical_statistics.find_or_initialize_by(date: vulnerability_statistic.updated_at)
historical_statistic.update(vulnerability_statistic.attributes.slice(*VULNERABILITY_STATISTIC_ATTRIBUTES))
end
end end
# rubocop: enable CodeReuse/ActiveRecord
private private
attr_reader :project attr_reader :project
delegate :vulnerability_statistic, :vulnerability_historical_statistics, to: :project delegate :vulnerability_statistic, to: :project
def update_statistic? def update_statistic?
keep_statistics_always_consistent? && vulnerability_statistic.present? keep_statistics_always_consistent? && vulnerability_statistic.present?
...@@ -37,6 +65,14 @@ module Vulnerabilities ...@@ -37,6 +65,14 @@ module Vulnerabilities
def keep_statistics_always_consistent? def keep_statistics_always_consistent?
Feature.enabled?(:keep_historical_vulnerability_statistics_always_consistent, project) Feature.enabled?(:keep_historical_vulnerability_statistics_always_consistent, project)
end end
def upsert_sql
UPSERT_SQL % { stats_sql: stats_sql }
end
def stats_sql
STATS_SQL % { project_id: project.id }
end
end end
end end
end end
...@@ -48,6 +48,12 @@ RSpec.describe Vulnerabilities::HistoricalStatistics::UpdateService do ...@@ -48,6 +48,12 @@ RSpec.describe Vulnerabilities::HistoricalStatistics::UpdateService do
expect { update_stats }.to change { Vulnerabilities::HistoricalStatistic.count }.by(1) expect { update_stats }.to change { Vulnerabilities::HistoricalStatistic.count }.by(1)
end end
end end
it 'changes the updated_at timestamp of the existing historical statistic entity' do
historical_statistic = create(:vulnerability_historical_statistic, project: project, letter_grade: 'c')
expect { update_stats }.to change { historical_statistic.reload.updated_at }
end
end end
context 'when the statistic is empty' do context 'when the statistic is empty' do
......
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