Commit fbbe5ccd authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'speed-up-relative-positioning' into 'master'

Speed up setting of relative position

See merge request gitlab-org/gitlab-ce!23324
parents 204f6a83 eb15e4df
......@@ -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 @@ def position_between(pos_before, pos_after)
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 @@ def next_relative_position
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 @@ def save_positionable_neighbours
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
......@@ -99,6 +99,10 @@ class << self
alias_method :in_parents, :in_projects
end
def self.parent_column
:project_id
end
def self.reference_prefix
'#'
end
......
---
title: Speed up issue board lists in groups with many projects
merge_request:
author:
type: performance
......@@ -14,6 +14,14 @@
expect(issue.prev_relative_position).to eq nil
expect(issue1.next_relative_position).to eq nil
end
it 'does not perform any moves if all issues have their relative_position set' do
issue.update!(relative_position: 1)
expect(issue).not_to receive(:save)
Issue.move_to_end([issue])
end
end
describe '#max_relative_position' do
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment