Skip to content

Commit ceb7771

Browse files
authored
Merge pull request #23495 from opf/fix/improve_speed_of_backlog_filtering
Fix/improve speed of backlog filtering
2 parents e6d02ca + 3aac600 commit ceb7771

20 files changed

Lines changed: 457 additions & 165 deletions

modules/backlogs/app/components/backlogs/backlog_component.html.erb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,18 @@ See COPYRIGHT and LICENSE files for more details.
7575
end
7676
%>
7777

78-
<%= render Backlogs::BucketComponent.with_collection(buckets, project:) %>
79-
78+
<% buckets.each do |bucket| %>
79+
<%=
80+
render Backlogs::BucketComponent.new(
81+
project:,
82+
backlog_bucket: bucket,
83+
work_packages: work_packages_for(bucket)
84+
)
85+
%>
86+
<% end %>
8087
<%=
8188
render Backlogs::InboxComponent.new(
82-
work_packages: inbox_work_packages,
89+
work_packages: work_packages_for_inbox,
8390
project: project
8491
)
8592
%>

modules/backlogs/app/components/backlogs/backlog_component.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ class BacklogComponent < ApplicationComponent
3434
include OpTurbo::Streamable
3535
include CommonHelper
3636

37-
attr_reader :inbox_work_packages, :buckets, :project, :current_user
37+
attr_reader :work_packages_by_backlog_id, :buckets, :project, :current_user
3838

39-
def initialize(inbox_work_packages:,
40-
buckets:,
39+
def initialize(buckets:,
40+
work_packages_by_backlog_id:,
4141
project:,
4242
current_user: User.current)
4343
super()
4444

45-
@inbox_work_packages = inbox_work_packages
45+
@work_packages_by_backlog_id = work_packages_by_backlog_id
4646
@buckets = buckets
4747
@project = project
4848
@current_user = current_user
@@ -55,7 +55,15 @@ def wrapper_uniq_by
5555
private
5656

5757
def total
58-
@total ||= inbox_work_packages.count + (buckets&.sum { it.work_packages.size } || 0)
58+
@total ||= work_packages_by_backlog_id.values.sum(&:count)
59+
end
60+
61+
def work_packages_for_inbox
62+
work_packages_by_backlog_id[nil] || []
63+
end
64+
65+
def work_packages_for(bucket)
66+
work_packages_by_backlog_id[bucket.id] || []
5967
end
6068
end
6169
end

modules/backlogs/app/components/backlogs/bucket_component.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,16 @@ class BucketComponent < ApplicationComponent
3434
include OpTurbo::Streamable
3535
include CommonHelper
3636

37-
with_collection_parameter :backlog_bucket
38-
3937
attr_reader :backlog_bucket, :work_packages, :project, :current_user
4038

41-
def initialize(backlog_bucket:, project:, current_user: User.current)
39+
def initialize(backlog_bucket:, project:, work_packages: nil, current_user: User.current)
4240
super()
4341

4442
@backlog_bucket = backlog_bucket
4543
@project = project
4644
@current_user = current_user
47-
@work_packages = backlog_bucket.displayed_work_packages
45+
@work_packages = work_packages || backlog_bucket.displayed_work_packages
46+
.includes(:status, :type, :assigned_to, :priority, :parent)
4847
end
4948

5049
def wrapper_uniq_by

modules/backlogs/app/components/backlogs/inbox_component.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,7 @@ def show_more_label
6969
end
7070

7171
def last_omitted_id
72-
if work_packages.respond_to?(:reverse_order)
73-
work_packages.reverse_order.offset(tail_size).limit(1).pick(:id)
74-
else
75-
work_packages[-(tail_size + 1)]&.id
76-
end
72+
work_packages[-(tail_size + 1)]&.id
7773
end
7874

7975
private

modules/backlogs/app/components/backlogs/sprint_component.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def initialize(sprint:, project:, work_packages: nil, current_user: User.current
4646
@project = project
4747
@current_user = current_user
4848
@active_sprint_ids = active_sprint_ids
49-
@work_packages = work_packages || sprint.work_packages_for(project).includes(:status, :type, :assigned_to, :priority,
50-
:parent)
49+
@work_packages = work_packages || sprint.work_packages_for(project)
50+
.includes(:status, :type, :assigned_to, :priority, :parent)
5151
end
5252

5353
def wrapper_uniq_by

modules/backlogs/app/components/backlogs/sprints_component.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ See COPYRIGHT and LICENSE files for more details.
6363
<%= render Backlogs::SprintComponent.new(
6464
sprint:,
6565
project:,
66-
work_packages: work_packages_by_sprint_id[sprint.id],
66+
work_packages: work_packages_by_sprint_id[sprint.id] || [],
6767
active_sprint_ids: active_sprint_ids
6868
) %>
6969
<% end %>

modules/backlogs/app/controllers/backlogs/backlog_controller.rb

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
module Backlogs
3232
class BacklogController < BaseController
3333
include ::WorkPackages::WithSplitView
34+
include Backlogs::Concerns::ContainerLoading
3435

3536
current_menu_item %i[show details] do
3637
:backlog
@@ -39,7 +40,7 @@ class BacklogController < BaseController
3940
def show
4041
case turbo_frame_request_id
4142
when "backlogs_container"
42-
load_backlogs
43+
load_container_data
4344

4445
render partial: "backlogs/backlog/backlog_list", layout: false
4546
else
@@ -51,7 +52,7 @@ def details
5152
if turbo_frame_request?
5253
render "work_packages/split_view", layout: false
5354
else
54-
load_backlogs
55+
load_container_data
5556

5657
render "backlogs/backlog/show"
5758
end
@@ -62,22 +63,5 @@ def details
6263
def split_view_base_route
6364
project_backlogs_backlog_path(@project, request.query_parameters)
6465
end
65-
66-
def load_backlogs
67-
@backlog_buckets = BacklogBucket.for_project(@project)
68-
69-
@sprints = Sprint.for_project(@project)
70-
.not_completed
71-
.order_by_date
72-
.includes(:project, :task_boards)
73-
74-
@work_packages_by_sprint_id = WorkPackage
75-
.where(sprint: @sprints, project: @project)
76-
.includes(:type, :status, :assigned_to, :priority, :parent)
77-
.order_by_position
78-
.group_by(&:sprint_id)
79-
@active_sprint_ids = @sprints.select(&:active?).map(&:id)
80-
@inbox_work_packages = WorkPackage.backlogs_inbox_for(project: @project)
81-
end
8266
end
8367
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# frozen_string_literal: true
2+
3+
# -- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
# ++
30+
31+
module Backlogs::Concerns
32+
module ContainerLoading
33+
extend ActiveSupport::Concern
34+
35+
def load_container_data
36+
load_sprint_data
37+
load_backlog_data
38+
end
39+
40+
def load_sprint_data
41+
@sprints = Sprint.for_project(@project)
42+
.not_completed
43+
.order_by_date
44+
.includes(:project, :task_boards)
45+
@active_sprint_ids = @sprints.select(&:active?).map(&:id)
46+
47+
@work_packages_by_sprint_id = WorkPackage
48+
.where(sprint: @sprints, project: @project)
49+
.includes(:type, :status, :assigned_to, :priority, :parent)
50+
.order_by_position
51+
.group_by(&:sprint_id)
52+
end
53+
54+
def load_backlog_data
55+
@backlog_buckets = BacklogBucket.for_project(@project)
56+
57+
# Includes the work packages of both the buckets and the inbox.
58+
# This has the drawback of loading more work packages than are displayed in the inbox as pagination
59+
# will only show the top 50 and lowest 10 work packages.
60+
# But doing only a single query (+ includes) to the database has its benefits, and currently this seems quicker.
61+
@work_packages_by_backlog_id = WorkPackage
62+
.in_backlog_for(project: @project)
63+
.includes(:type, :status, :assigned_to, :priority, :parent)
64+
.group_by(&:backlog_bucket_id)
65+
end
66+
end
67+
end

modules/backlogs/app/controllers/backlogs/work_packages_controller.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
module Backlogs
3232
class WorkPackagesController < BaseController
3333
include OpTurbo::ComponentStream
34+
include Backlogs::Concerns::ContainerLoading
3435

3536
before_action :load_work_package
3637

@@ -112,10 +113,11 @@ def sprint_component(sprint:)
112113
end
113114

114115
def backlog_component
115-
inbox_work_packages = WorkPackage.backlogs_inbox_for(project: @project)
116-
buckets = BacklogBucket.for_project(@project)
116+
load_backlog_data
117117

118-
Backlogs::BacklogComponent.new(inbox_work_packages:, buckets:, project: @project)
118+
Backlogs::BacklogComponent.new(buckets: @backlog_buckets,
119+
work_packages_by_backlog_id: @work_packages_by_backlog_id,
120+
project: @project)
119121
end
120122

121123
def load_work_package
@@ -133,7 +135,7 @@ def displayed_work_packages
133135
elsif @work_package.backlog_bucket_id?
134136
@work_packages.merge(@work_package.backlog_bucket.displayed_work_packages)
135137
else
136-
@work_packages.merge(WorkPackage.backlogs_inbox_for(project: @project))
138+
@work_packages.merge(WorkPackage.in_inbox_for(project: @project))
137139
end
138140
end
139141

modules/backlogs/app/models/backlog_bucket.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ class BacklogBucket < ApplicationRecord
5252
validates :name, :project, presence: true
5353

5454
def self.for_project(project)
55-
where(project:).order_alphabetically.includes(displayed_work_packages: %i[assigned_to priority parent])
55+
where(project:).order_alphabetically
5656
end
5757
end

0 commit comments

Comments
 (0)