From e060bc2b1afa82ae8ae3d2f2ca74eca6a1e6eb5f Mon Sep 17 00:00:00 2001 From: Hardik Joshi Date: Thu, 30 Nov 2023 20:15:25 +0530 Subject: [PATCH] rel concurrency issue proving test cases --- .../query_proxy_methods_of_mass_updating.rb | 4 ++ .../persistence/query_factory_spec.rb | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb b/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb index e3d1ae71a..e5c5f7e8f 100644 --- a/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +++ b/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb @@ -51,6 +51,10 @@ def delete_all_rels def replace_with(node_or_nodes) node_or_nodes = Array(node_or_nodes).map { |arg| arg.is_a?(ActiveGraph::Node) ? arg : @model.find(arg) } original_ids = self.pluck(:id) + $concurrency_queue << 'ready' if $concurrency_test # completed read above so send signal in the queue + while $concurrency_test && $concurrency_test_wait # wait till other thread has also completed its read + sleep 1 + end delete_rels_for_nodes(original_ids, node_or_nodes.collect(&:id)) add_rels(node_or_nodes, original_ids) end diff --git a/spec/e2e/relationship/persistence/query_factory_spec.rb b/spec/e2e/relationship/persistence/query_factory_spec.rb index ac461bab0..49de7cb92 100644 --- a/spec/e2e/relationship/persistence/query_factory_spec.rb +++ b/spec/e2e/relationship/persistence/query_factory_spec.rb @@ -103,6 +103,59 @@ def self.count expect(from_node.reload.to_classes).to be_empty end + it 'does not create duplicate has_one relationship' do + from_node.save + to_node.save + begin + $concurrency_test = true + $concurrency_queue = Queue.new + $concurrency_test_wait = true + t1 = Thread.new { to_node.update(from_class: from_node) } + t2 = Thread.new { to_node.update(from_class: from_node) } + while $concurrency_queue.size < 2 + # wait till both thread have complted read query + sleep 1 + end + $concurrency_test_wait = false # make threads resume their work + [t1, t2].join # wait for the threads to finish + + # to_node.from_class only returns 1 result but in db there are two rels associated with this node + # this assertion fails with with result 2 proving the presence of bug + expect(ActiveGraph::Base.query("MATCH (node2:`ToClass`)<-[rel1:`HAS_REL`]-(from_class:`FromClass`) return from_class").to_a.size).to eq(1) + ensure + $concurrency_test = nil + $concurrency_test_wait = nil + $concurrency_queue = nil + end + end + + it 'does not create two rels with different nodes in has_one relationship' do + from_node.save + to_node.save + from_node_two = FromClass.create(name: 'foo-2') + begin + $concurrency_test = true + $concurrency_queue = Queue.new + $concurrency_test_wait = true + t1 = Thread.new { to_node.update(from_class: from_node) } + t2 = Thread.new { to_node.update(from_class: from_node_two) } + while $concurrency_queue.size < 2 + # wait till both thread have complted read query + sleep 1 + end + $concurrency_test_wait = false # make threads resume their work + [t1, t2].join # wait for the threads to finish + + # to_node.from_class only returns 1 result but in db there are two rels associated with this node + # this assertion fails with with result 2 proving the presence of bug + expect(ActiveGraph::Base.query("MATCH (node2:`ToClass`)<-[rel1:`HAS_REL`]-(from_class:`FromClass`) return from_class").to_a.size).to eq(1) + ensure + $concurrency_test = nil + $concurrency_test_wait = nil + $concurrency_queue = nil + end + end + it 'delets has_one rel from from_node when new relation is created' do to_node_two = ToClass.new(name: 'bar-2') Rel2Class.new(from_node: from_node, to_node: to_node, score: 10).save