]> source.dussan.org Git - redmine.git/commitdiff
Add concurrent subtask removal test to cover corrupted nested sets (#39437).
authorMarius Balteanu <marius.balteanu@zitec.com>
Sat, 18 Nov 2023 22:27:35 +0000 (22:27 +0000)
committerMarius Balteanu <marius.balteanu@zitec.com>
Sat, 18 Nov 2023 22:27:35 +0000 (22:27 +0000)
Patch by Jens Krämer.

git-svn-id: https://svn.redmine.org/redmine/trunk@22461 e93f8b46-1217-0410-a6f0-8f06a7374b81

test/unit/issue_nested_set_concurrency_test.rb

index 0c3f9e6a827983a2296dd158b9d6fd7319bb6f7e..93027c4904dda9263edf5ddf50d7826a80b50aa2 100644 (file)
@@ -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)