Class: Users::AssignedIssuesCountService

Inherits:
BaseCountService show all
Defined in:
app/services/users/assigned_issues_count_service.rb

Instance Method Summary collapse

Methods inherited from BaseCountService

#count, #count_stored?, #delete_cache, #raw?, #refresh_cache, #relation_for_count, #update_cache_for_key

Constructor Details

#initialize(current_user:, max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT) ⇒ AssignedIssuesCountService

Returns a new instance of AssignedIssuesCountService.



5
6
7
8
# File 'app/services/users/assigned_issues_count_service.rb', line 5

def initialize(current_user:, max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT)
  @current_user = current_user
  @max_limit = max_limit
end

Instance Method Details

#cache_keyObject



10
11
12
# File 'app/services/users/assigned_issues_count_service.rb', line 10

def cache_key
  ['users', @current_user.id, 'max_assigned_open_issues_count']
end

#cache_optionsObject



14
15
16
# File 'app/services/users/assigned_issues_count_service.rb', line 14

def cache_options
  { force: false, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD }
end

#uncached_countObject

rubocop: disable CodeReuse/ActiveRecord



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'app/services/users/assigned_issues_count_service.rb', line 19

def uncached_count
  # When a user has many assigned issues, counting them all can be very slow.
  # As a workaround, we will short-circuit the counting query once the count reaches some threshold.
  #
  # Concretely, given a threshold, say 100 (= max_limit),
  # iterate through the first 100 issues, sorted by ID desc, assigned to the user using `issue_assignees` table.
  # For each issue iterated, use IssuesFinder to check if the issue should be counted.
  initializer = IssueAssignee
    .select(:issue_id).joins(", LATERAL (#{finder_constraint.to_sql}) as issues")
    .where(user_id: @current_user.id)
    .order(issue_id: :desc)
    .limit(1)
  recursive_finder = initializer.where("issue_assignees.issue_id < assigned_issues.issue_id")

  cte = <<~SQL
    WITH RECURSIVE assigned_issues AS (
      (
        #{initializer.to_sql}
      )
      UNION ALL
      (
        SELECT next_assigned_issue.issue_id
        FROM assigned_issues,
          LATERAL (
            #{recursive_finder.to_sql}
          ) next_assigned_issue
      )
    ) SELECT COUNT(*) FROM (SELECT * FROM assigned_issues LIMIT #{@max_limit}) issues
  SQL

  ApplicationRecord.connection.execute(cte).first["count"]
end