Commit d16e33c6 authored by Alex Kalderimis's avatar Alex Kalderimis

Feature flag changes

Safer use of flags, by checking them in the service and the worker.

Allow groups to be used as feature flag gates.
parent a52cd650
# frozen_string_literal: true
class IssueRebalancingService
MAX_ISSUE_COUNT = 100_000
MAX_ISSUE_COUNT = 10_000
TooManyIssues = Class.new(StandardError)
attr_reader :issue
......@@ -12,6 +12,9 @@ class IssueRebalancingService
# rubocop: disable CodeReuse/ActiveRecord
def execute
gates = [issue.project, issue.project.group].compact
return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) }
base = Issue.relative_positioning_query_base(issue)
n = base.count
......@@ -36,23 +39,25 @@ class IssueRebalancingService
start = 0 - (gaps / 2) * gap_size
indexed = base.reorder(:relative_position, :id).pluck(:id).each_with_index
indexed.each_slice(500) do |pairs|
values = pairs.map do |id, index|
"(#{id}, #{start + (index * gap_size)})"
end.join(', ')
Issue.connection.exec_query(<<~SQL, "rebalance issue positions")
WITH cte(cte_id, new_pos) AS (
SELECT *
FROM (VALUES #{values}) as t (id, pos)
)
UPDATE #{Issue.table_name}
SET relative_position = cte.new_pos
FROM cte
WHERE cte_id = id
SQL
Issue.transaction do
indexed = base.reorder(:relative_position, :id).pluck(:id).each_with_index
indexed.each_slice(500) do |pairs|
values = pairs.map do |id, index|
"(#{id}, #{start + (index * gap_size)})"
end.join(', ')
Issue.connection.exec_query(<<~SQL, "rebalance issue positions")
WITH cte(cte_id, new_pos) AS (
SELECT *
FROM (VALUES #{values}) as t (id, pos)
)
UPDATE #{Issue.table_name}
SET relative_position = cte.new_pos
FROM cte
WHERE cte_id = id
SQL
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -22,7 +22,8 @@ module Issues
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999)).freeze
def rebalance_if_needed(issue)
return unless Feature.enabled?(:rebalance_issues, issue.project)
gates = [issue.project, issue.project.group].compact
return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) }
return unless issue
return if issue.relative_position.nil?
return if NO_REBALANCING_NEEDED.cover?(issue.relative_position)
......
......@@ -18,6 +18,9 @@ class IssueRebalancingWorker # rubocop:disable Scalability/IdempotentWorker
private
def rebalance(issue)
gates = [issue.project, issue.project.group].compact
return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) }
IssueRebalancingService.new(issue).execute
end
end
......@@ -57,4 +57,36 @@ RSpec.describe IssueRebalancingService do
service.execute
end.not_to change { issues_in_position_order.map(&:id) }
end
it 'does nothing if the feature flag is disabled' do
stub_feature_flags(rebalance_issues: false)
issue = project.issues.first
issue.project
issue.project.group
old_pos = issue.relative_position
service = described_class.new(issue)
expect { service.execute }.not_to exceed_query_limit(0)
expect(old_pos).to eq(issue.reload.relative_position)
end
it 'acts if the flag is enabled for the project' do
issue = create(:issue, project: project, author: user, relative_position: max_pos)
stub_feature_flags(rebalance_issues: issue.project)
service = described_class.new(issue)
expect { service.execute }.to change { issue.reload.relative_position }
end
it 'acts if the flag is enabled for the group' do
issue = create(:issue, project: project, author: user, relative_position: max_pos)
project.update!(group: create(:group))
stub_feature_flags(rebalance_issues: issue.project.group)
service = described_class.new(issue)
expect { service.execute }.to change { issue.reload.relative_position }
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