From 60c931e6dbedeef3931eb8dc89bd4f5095f97f65 Mon Sep 17 00:00:00 2001 From: Mehmet Emin INAC <minac@gitlab.com> Date: Fri, 13 Aug 2021 23:24:26 +0300 Subject: [PATCH] Disable updating the historical vulnerability statistics by default Historical vulnerability statistics supposed to be updated once a day by a cronjob because updating them always for each update/insert of a vulnerability can reduce the throughput of our worker classes and API endpoints. With this change, to be able to observe the side effects of disabling the update logic of historical vulnerability statistics, we are moving it behind a feature flag. We will remove the logic and the feature flag eventually. Changelog: other EE: true --- doc/api/graphql/reference/index.md | 6 +- ee/app/graphql/ee/types/group_type.rb | 2 +- ee/app/graphql/ee/types/project_type.rb | 2 +- ee/app/graphql/ee/types/query_type.rb | 2 +- .../historical_statistics/update_service.rb | 10 ++- ...erability_statistics_always_consistent.yml | 8 +++ .../update_service_spec.rb | 69 ++++++++++++++----- 7 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 ee/config/feature_flags/development/keep_historical_vulnerability_statistics_always_consistent.yml diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d349e13450a80..3f9fd5c1d3424 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -479,7 +479,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ### `Query.vulnerabilitiesCountByDay` -Number of vulnerabilities per day for the projects on the current user's instance security dashboard. +The historical number of vulnerabilities per day for the projects on the current user's instance security dashboard. Returns [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnection). @@ -10004,7 +10004,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ##### `Group.vulnerabilitiesCountByDay` -Number of vulnerabilities per day for the projects in the group and its subgroups. +The historical number of vulnerabilities per day for the projects in the group and its subgroups. Returns [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnection). @@ -12608,7 +12608,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ##### `Project.vulnerabilitiesCountByDay` -Number of vulnerabilities per day for the project. +The historical number of vulnerabilities per day for the project. Returns [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnection). diff --git a/ee/app/graphql/ee/types/group_type.rb b/ee/app/graphql/ee/types/group_type.rb index a27ae88aa1f38..78f0012b76939 100644 --- a/ee/app/graphql/ee/types/group_type.rb +++ b/ee/app/graphql/ee/types/group_type.rb @@ -62,7 +62,7 @@ module GroupType field :vulnerabilities_count_by_day, ::Types::VulnerabilitiesCountByDayType.connection_type, null: true, - description: 'Number of vulnerabilities per day for the projects in the group and its subgroups.', + description: 'The historical number of vulnerabilities per day for the projects in the group and its subgroups.', resolver: ::Resolvers::VulnerabilitiesCountPerDayResolver field :vulnerability_grades, diff --git a/ee/app/graphql/ee/types/project_type.rb b/ee/app/graphql/ee/types/project_type.rb index e5014f649f6e2..0b5a6f373ee45 100644 --- a/ee/app/graphql/ee/types/project_type.rb +++ b/ee/app/graphql/ee/types/project_type.rb @@ -25,7 +25,7 @@ module ProjectType field :vulnerabilities_count_by_day, ::Types::VulnerabilitiesCountByDayType.connection_type, null: true, - description: 'Number of vulnerabilities per day for the project.', + description: 'The historical number of vulnerabilities per day for the project.', resolver: ::Resolvers::VulnerabilitiesCountPerDayResolver field :vulnerability_severities_count, ::Types::VulnerabilitySeveritiesCountType, null: true, diff --git a/ee/app/graphql/ee/types/query_type.rb b/ee/app/graphql/ee/types/query_type.rb index c67315000078b..d627bf9126d4d 100644 --- a/ee/app/graphql/ee/types/query_type.rb +++ b/ee/app/graphql/ee/types/query_type.rb @@ -34,7 +34,7 @@ module QueryType null: true, resolver: ::Resolvers::VulnerabilitiesCountPerDayResolver, description: <<~DESC - Number of vulnerabilities per day for the projects on the current user's instance security dashboard. + The historical number of vulnerabilities per day for the projects on the current user's instance security dashboard. DESC field :geo_node, ::Types::Geo::GeoNodeType, diff --git a/ee/app/services/vulnerabilities/historical_statistics/update_service.rb b/ee/app/services/vulnerabilities/historical_statistics/update_service.rb index b5f88d8da0499..a716c8c898db3 100644 --- a/ee/app/services/vulnerabilities/historical_statistics/update_service.rb +++ b/ee/app/services/vulnerabilities/historical_statistics/update_service.rb @@ -15,7 +15,7 @@ def initialize(project) # rubocop: disable CodeReuse/ActiveRecord def execute - return if vulnerability_statistic.blank? + return unless update_statistic? ::Vulnerabilities::HistoricalStatistic.safe_ensure_unique(retries: 1) do historical_statistic = vulnerability_historical_statistics.find_or_initialize_by(date: vulnerability_statistic.updated_at) @@ -29,6 +29,14 @@ def execute attr_reader :project delegate :vulnerability_statistic, :vulnerability_historical_statistics, to: :project + + def update_statistic? + keep_statistics_always_consistent? && vulnerability_statistic.present? + end + + def keep_statistics_always_consistent? + Feature.enabled?(:keep_historical_vulnerability_statistics_always_consistent, project) + end end end end diff --git a/ee/config/feature_flags/development/keep_historical_vulnerability_statistics_always_consistent.yml b/ee/config/feature_flags/development/keep_historical_vulnerability_statistics_always_consistent.yml new file mode 100644 index 0000000000000..672b73cb8a2b0 --- /dev/null +++ b/ee/config/feature_flags/development/keep_historical_vulnerability_statistics_always_consistent.yml @@ -0,0 +1,8 @@ +--- +name: keep_historical_vulnerability_statistics_always_consistent +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68189 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338508 +milestone: '14.2' +type: development +group: group::threat insights +default_enabled: false diff --git a/ee/spec/services/vulnerabilities/historical_statistics/update_service_spec.rb b/ee/spec/services/vulnerabilities/historical_statistics/update_service_spec.rb index 13cab41437a6c..ece101263fe61 100644 --- a/ee/spec/services/vulnerabilities/historical_statistics/update_service_spec.rb +++ b/ee/spec/services/vulnerabilities/historical_statistics/update_service_spec.rb @@ -24,34 +24,69 @@ describe '#execute' do subject(:update_stats) { described_class.new(project).execute } - context 'when the statistic is not empty' do - before do - create(:vulnerability_statistic, project: project, low: 2) - end + around do |example| + travel_to(Date.current) { example.run } + end - context 'when there is already a record in the database' do - around do |example| - travel_to(Date.current) { example.run } + context 'when the `keep_historical_vulnerability_statistics_always_consistent` feature is enabled' do + context 'when the statistic is not empty' do + before do + create(:vulnerability_statistic, project: project, low: 2) end - it 'changes the existing historical statistic entity' do - historical_statistic = create(:vulnerability_historical_statistic, project: project, letter_grade: 'c') + context 'when there exists a record in the database' do + it 'changes 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.letter_grade }.from('c').to('b') + .and change { historical_statistic.reload.low }.to(2) + end + end - expect { update_stats }.to change { historical_statistic.reload.letter_grade }.from('c').to('b') - .and change { historical_statistic.reload.low }.to(2) + context 'when there exists no record in the database' do + it 'creates a new record in the database' do + expect { update_stats }.to change { Vulnerabilities::HistoricalStatistic.count }.by(1) + end end end - context 'when there is no existing record in the database' do - it 'creates a new record in the database' do - expect { update_stats }.to change { Vulnerabilities::HistoricalStatistic.count }.by(1) + context 'when the statistic is empty' do + it 'does not create any historical statistic entity' do + expect { update_stats }.not_to change { Vulnerabilities::Statistic.count } end end end - context 'when the statistic is empty' do - it 'does not create any historical statistic entity' do - expect { update_stats }.not_to change { Vulnerabilities::Statistic.count } + context 'when the `keep_historical_vulnerability_statistics_always_consistent` feature is disabled' do + before do + stub_feature_flags(keep_historical_vulnerability_statistics_always_consistent: false) + end + + context 'when the statistic is not empty' do + before do + create(:vulnerability_statistic, project: project, low: 2) + end + + context 'when there exists a record in the database' do + it 'does not change the existing historical statistic entity' do + historical_statistic = create(:vulnerability_historical_statistic, project: project, letter_grade: 'c') + + expect { update_stats }.to not_change { historical_statistic.reload.letter_grade }.from('c') + .and not_change { historical_statistic.reload.low }.from(0) + end + end + + context 'when there exists no record in the database' do + it 'does not create a new record in the database' do + expect { update_stats }.not_to change { Vulnerabilities::HistoricalStatistic.count } + end + end + end + + context 'when the statistic is empty' do + it 'does not create any historical statistic entity' do + expect { update_stats }.not_to change { Vulnerabilities::Statistic.count } + end end end end -- GitLab