summaryrefslogtreecommitdiff
path: root/lib/gitlab/issuables_count_for_state.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/issuables_count_for_state.rb')
-rw-r--r--lib/gitlab/issuables_count_for_state.rb57
1 files changed, 55 insertions, 2 deletions
diff --git a/lib/gitlab/issuables_count_for_state.rb b/lib/gitlab/issuables_count_for_state.rb
index 7be54a214dd..945ab7f40c2 100644
--- a/lib/gitlab/issuables_count_for_state.rb
+++ b/lib/gitlab/issuables_count_for_state.rb
@@ -16,9 +16,12 @@ module Gitlab
end
# finder - The finder class to use for retrieving the issuables.
- def initialize(finder, project = nil)
+ # fast_fail - restrict counting to a shorter period, degrading gracefully on
+ # failure
+ def initialize(finder, project = nil, fast_fail: false)
@finder = finder
@project = project
+ @fast_fail = fast_fail
@cache = Gitlab::SafeRequestStore[CACHE_KEY] ||= initialize_cache
end
@@ -26,6 +29,10 @@ module Gitlab
self[state || :opened]
end
+ def fast_fail?
+ !!@fast_fail
+ end
+
# Define method for each state
STATES.each do |state|
define_method(state) { self[state] }
@@ -53,7 +60,53 @@ module Gitlab
end
def initialize_cache
- Hash.new { |hash, finder| hash[finder] = finder.count_by_state }
+ Hash.new { |hash, finder| hash[finder] = perform_count(finder) }
+ end
+
+ def perform_count(finder)
+ return finder.count_by_state unless fast_fail?
+
+ fast_count_by_state_attempt!
+
+ # Determining counts when referring to issuable titles or descriptions can
+ # be very expensive, and involve the database reading gigabytes of data
+ # for a relatively minor piece of functionality. This may slow index pages
+ # by seconds in the best case, or lead to a statement timeout in the worst
+ # case.
+ #
+ # In time, we may be able to use elasticsearch or postgresql tsv columns
+ # to perform the calculation more efficiently. Until then, use a shorter
+ # timeout and return -1 as a sentinel value if it is triggered
+ begin
+ ApplicationRecord.with_fast_statement_timeout do
+ finder.count_by_state
+ end
+ rescue ActiveRecord::QueryCanceled => err
+ fast_count_by_state_failure!
+
+ Gitlab::ErrorTracking.track_exception(
+ err,
+ params: finder.params,
+ current_user_id: finder.current_user&.id,
+ issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/249180'
+ )
+
+ Hash.new(-1)
+ end
+ end
+
+ def fast_count_by_state_attempt!
+ Gitlab::Metrics.counter(
+ :gitlab_issuable_fast_count_by_state_total,
+ "Count of total calls to IssuableFinder#count_by_state with fast failure"
+ ).increment
+ end
+
+ def fast_count_by_state_failure!
+ Gitlab::Metrics.counter(
+ :gitlab_issuable_fast_count_by_state_failures_total,
+ "Count of failed calls to IssuableFinder#count_by_state with fast failure"
+ ).increment
end
end
end