From: Marius Balteanu Date: Sat, 18 Nov 2023 22:27:35 +0000 (+0000) Subject: Add concurrent subtask removal test to cover corrupted nested sets (#39437). X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=92e779cafab1407f79206c89b8e8934b1ea1fe6e;p=redmine.git Add concurrent subtask removal test to cover corrupted nested sets (#39437). Patch by Jens Krämer. git-svn-id: https://svn.redmine.org/redmine/trunk@22461 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- diff --git a/test/unit/issue_nested_set_concurrency_test.rb b/test/unit/issue_nested_set_concurrency_test.rb index 0c3f9e6a8..93027c490 100644 --- a/test/unit/issue_nested_set_concurrency_test.rb +++ b/test/unit/issue_nested_set_concurrency_test.rb @@ -74,6 +74,49 @@ class IssueNestedSetConcurrencyTest < ActiveSupport::TestCase assert_equal (2..61).to_a, children_bounds end + def test_concurrent_subtask_removal + with_settings :notified_events => [] do + root = Issue.generate! + 60.times do + Issue.generate! :parent_issue_id => root.id + end + # pick 40 random subtask ids + child_ids = Issue.where(root_id: root.id, parent_id: root.id).pluck(:id) + ids_to_remove = child_ids.sample(40).shuffle + ids_to_keep = child_ids - ids_to_remove + # remove these from the set, using four parallel threads + threads = [] + ids_to_remove.each_slice(10) do |ids| + threads << Thread.new do + ActiveRecord::Base.connection_pool.with_connection do + begin + ids.each do |id| + Issue.find(id).update(parent_id: nil) + end + rescue => e + Thread.current[:exception] = e.message + end + end + end + end + threads.each do |thread| + thread.join + assert_nil thread[:exception] + end + assert_equal 20, Issue.where(parent_id: root.id).count + Issue.where(id: ids_to_remove).each do |issue| + assert_nil issue.parent_id + assert_equal issue.id, issue.root_id + assert_equal 1, issue.lft + assert_equal 2, issue.rgt + end + root.reload + assert_equal [1, 42], [root.lft, root.rgt] + children_bounds = root.children.sort_by(&:lft).map {|c| [c.lft, c.rgt]}.flatten + assert_equal (2..41).to_a, children_bounds + end + end + private def threaded(count, &block)