Commit 37cdb0f5 authored by Adam Hegyi's avatar Adam Hegyi

Optimize issue rebalancing UPDATE query

This change updates the issue rebalancing UPDATE query by sorting the
updatable issues by id. This will improve the data locality a bit and
probably speed up random I/O.
parent bea68b43
...@@ -17,10 +17,16 @@ class IssueRebalancingService ...@@ -17,10 +17,16 @@ class IssueRebalancingService
start = RelativePositioning::START_POSITION - (gaps / 2) * gap_size start = RelativePositioning::START_POSITION - (gaps / 2) * gap_size
if Feature.enabled?(:issue_rebalancing_optimization)
Issue.transaction do
sort_pairs_by_id(start).each_slice(100) { |pairs| assign_positions_without_position_calculation(pairs) }
end
else
Issue.transaction do Issue.transaction do
indexed_ids.each_slice(100) { |pairs| assign_positions(start, pairs) } indexed_ids.each_slice(100) { |pairs| assign_positions(start, pairs) }
end end
end end
end
private private
...@@ -32,13 +38,30 @@ class IssueRebalancingService ...@@ -32,13 +38,30 @@ class IssueRebalancingService
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord def sort_pairs_by_id(start)
indexed_ids.map do |id, index|
[id, start + (index * gap_size)]
end.sort_by(&:first)
end
def assign_positions_without_position_calculation(pairs_with_position)
values = pairs_with_position.map do |id, index|
"(#{id}, #{index})"
end.join(', ')
run_update_query(values)
end
def assign_positions(start, positions) def assign_positions(start, positions)
values = positions.map do |id, index| values = positions.map do |id, index|
"(#{id}, #{start + (index * gap_size)})" "(#{id}, #{start + (index * gap_size)})"
end.join(', ') end.join(', ')
Issue.connection.exec_query(<<~SQL, "rebalance issue positions") run_update_query(values)
end
def run_update_query(values)
Issue.connection.exec_query(<<~SQL, "rebalance issue positions batches ordered by id")
WITH cte(cte_id, new_pos) AS ( WITH cte(cte_id, new_pos) AS (
SELECT * SELECT *
FROM (VALUES #{values}) as t (id, pos) FROM (VALUES #{values}) as t (id, pos)
...@@ -49,7 +72,6 @@ class IssueRebalancingService ...@@ -49,7 +72,6 @@ class IssueRebalancingService
WHERE cte_id = id WHERE cte_id = id
SQL SQL
end end
# rubocop: enable CodeReuse/ActiveRecord
def issue_count def issue_count
@issue_count ||= base.count @issue_count ||= base.count
......
---
name: issue_rebalancing_optimization
introduced_by_url:
rollout_issue_url:
milestone: '13.9'
type: development
group: group::plan
default_enabled: false
...@@ -32,6 +32,7 @@ RSpec.describe IssueRebalancingService do ...@@ -32,6 +32,7 @@ RSpec.describe IssueRebalancingService do
project.reload.issues.reorder(relative_position: :asc).to_a project.reload.issues.reorder(relative_position: :asc).to_a
end end
shared_examples 'IssueRebalancingService shared examples' do
it 'rebalances a set of issues with clumps at the end and start' do it 'rebalances a set of issues with clumps at the end and start' do
all_issues = start_clump + unclumped + end_clump.reverse all_issues = start_clump + unclumped + end_clump.reverse
service = described_class.new(project.issues.first) service = described_class.new(project.issues.first)
...@@ -98,4 +99,21 @@ RSpec.describe IssueRebalancingService do ...@@ -98,4 +99,21 @@ RSpec.describe IssueRebalancingService do
expect { described_class.new(issue).execute }.to raise_error(described_class::TooManyIssues) expect { described_class.new(issue).execute }.to raise_error(described_class::TooManyIssues)
end end
end
context 'when issue_rebalancing_optimization feature flag is on' do
before do
stub_feature_flags(issue_rebalancing_optimization: true)
end
it_behaves_like 'IssueRebalancingService shared examples'
end
context 'when issue_rebalancing_optimization feature flag is on' do
before do
stub_feature_flags(issue_rebalancing_optimization: false)
end
it_behaves_like 'IssueRebalancingService shared examples'
end
end end
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