Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2018-11-23 16:06:04 +0300
committerSean McGivern <sean@gitlab.com>2018-11-23 16:06:04 +0300
commiteb15e4dfc2953925cb4d029d007608a8d39bf97e (patch)
treed167107896b36501507d49357da795f068eb9f61 /app/models/concerns/relative_positioning.rb
parent3b601b82997a9bde956aff8e2628b012fcaa539e (diff)
Speed up setting of relative position
1. When every issue has a relative position set, we don't need to perform any updates, or calculate the maximum position in the parent. 2. If we do need to calculate the maximum position in the parent, many parents (specifically, groups with lots of projects) leads to a slow query where only the index on issues.relative_position is used, not the index on issues.project_id. Adding the GROUP BY forces Postgres to use both indices.
Diffstat (limited to 'app/models/concerns/relative_positioning.rb')
-rw-r--r--app/models/concerns/relative_positioning.rb46
1 files changed, 32 insertions, 14 deletions
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index 045bf392ac8..46d2c345758 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -14,10 +14,12 @@ module RelativePositioning
class_methods do
def move_to_end(objects)
- parent_ids = objects.map(&:parent_ids).flatten.uniq
- max_relative_position = in_parents(parent_ids).maximum(:relative_position) || START_POSITION
objects = objects.reject(&:relative_position)
+ return if objects.empty?
+
+ max_relative_position = objects.first.max_relative_position
+
self.transaction do
objects.each do |object|
relative_position = position_between(max_relative_position, MAX_POSITION)
@@ -55,22 +57,21 @@ module RelativePositioning
end
end
- def min_relative_position
- self.class.in_parents(parent_ids).minimum(:relative_position)
+ def min_relative_position(&block)
+ calculate_relative_position('MIN', &block)
end
- def max_relative_position
- self.class.in_parents(parent_ids).maximum(:relative_position)
+ def max_relative_position(&block)
+ calculate_relative_position('MAX', &block)
end
def prev_relative_position
prev_pos = nil
if self.relative_position
- prev_pos = self.class
- .in_parents(parent_ids)
- .where('relative_position < ?', self.relative_position)
- .maximum(:relative_position)
+ prev_pos = max_relative_position do |relation|
+ relation.where('relative_position < ?', self.relative_position)
+ end
end
prev_pos
@@ -80,10 +81,9 @@ module RelativePositioning
next_pos = nil
if self.relative_position
- next_pos = self.class
- .in_parents(parent_ids)
- .where('relative_position > ?', self.relative_position)
- .minimum(:relative_position)
+ next_pos = min_relative_position do |relation|
+ relation.where('relative_position > ?', self.relative_position)
+ end
end
next_pos
@@ -165,4 +165,22 @@ module RelativePositioning
status
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def calculate_relative_position(calculation)
+ # When calculating across projects, this is much more efficient than
+ # MAX(relative_position) without the GROUP BY, due to index usage:
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/54276#note_119340977
+ relation = self.class
+ .in_parents(parent_ids)
+ .order(Gitlab::Database.nulls_last_order('position', 'DESC'))
+ .limit(1)
+ .group(self.class.parent_column)
+
+ relation = yield relation if block_given?
+
+ relation
+ .pluck(self.class.parent_column, "#{calculation}(relative_position) AS position")
+ .first&.
+ last
+ end
end