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