diff --git a/lib/active_graph/transactions.rb b/lib/active_graph/transactions.rb index 6e125879f..e8ef4ae4f 100644 --- a/lib/active_graph/transactions.rb +++ b/lib/active_graph/transactions.rb @@ -29,7 +29,7 @@ def read_transaction(**config, &block) alias transaction write_transaction def lock_node(node) - node.as(:n).query.remove('n._LOCK_').exec if tx&.open? || explicit_session&.open? + node.as(:n).query.remove('n._AGLOCK_').exec if tx&.open? || explicit_session&.open? end private diff --git a/spec/e2e/relationship/persistence/query_factory_spec.rb b/spec/e2e/relationship/persistence/query_factory_spec.rb index 6b696e360..f2911ea94 100644 --- a/spec/e2e/relationship/persistence/query_factory_spec.rb +++ b/spec/e2e/relationship/persistence/query_factory_spec.rb @@ -105,52 +105,24 @@ def self.count context 'concurrent update' do before do - allow(ActiveGraph::Base).to receive(:lock_node).and_wrap_original do |original, *args| + allow_any_instance_of(ActiveGraph::Node::Query::QueryProxy).to receive(:replace_with).and_wrap_original do |original, *args| $concurrency_queue << 'ready' Thread.stop original.call(*args) - $concurrency_queue << Thread.current - Thread.stop end end after { $concurrency_queue = nil } let!(:from_node) { FromClass.create(name: 'foo') } let!(:to_node) { ToClass.create(name: 'bar') } - let(:from_node_two) { FromClass.create(name: 'foo-2') } it 'does not create duplicate has_one relationship' do + count = 100 $concurrency_queue = Thread::Queue.new - t1 = Thread.new { to_node.update(from_class: from_node) } - t2 = Thread.new { to_node.update(from_class: from_node) } - sleep(0.1) until $concurrency_queue.size == 2 + threads = count.times.map { Thread.new { to_node.update(from_class: from_node) } } + sleep(0.1) until $concurrency_queue.size == count $concurrency_queue.clear - [t1, t2].each(&:run) - sleep(0.1) until $concurrency_queue.size == 1 && t1.status == 'sleep' && t2.status == 'sleep' - $concurrency_queue.pop.run - sleep(0.1) until !(t1.status && t2.status) - - expect(ActiveGraph::Base.query("MATCH (node2:`ToClass`)<-[rel1:`HAS_REL`]-(from_class:`FromClass`) return from_class").to_a.size).to eq(1) - (t1.status == 'sleep' ? t1.run : t2.run).join - expect(ActiveGraph::Base.query("MATCH (node2:`ToClass`)<-[rel1:`HAS_REL`]-(from_class:`FromClass`) return from_class").to_a.size).to eq(1) - end - - it 'does not create two rels with different nodes in has_one relationship' do - $concurrency_queue = Thread::Queue.new - t1 = Thread.new { to_node.update(from_class: from_node) } - t2 = Thread.new { to_node.update(from_class: from_node_two) } - sleep(0.1) until $concurrency_queue.size == 2 - $concurrency_queue.clear - [t1, t2].each(&:run) - sleep(0.1) until $concurrency_queue.size == 1 && t1.status == 'sleep' && t2.status == 'sleep' - $concurrency_queue.pop.run - sleep(0.1) until !(t1.status && t2.status) - - first_assigned_from_class, second_assigned_from_class = t1.status == 'sleep' ? [from_node_two, from_node] : [from_node, from_node_two] - - expect(ToClass.find(to_node.id).from_class.id).to eq(first_assigned_from_class.id) - (t1.status == 'sleep' ? t1.run : t2.run).join - - expect(to_node.reload.from_class.id).to eq(second_assigned_from_class.id) + threads.each(&:run) + threads.each(&:join) expect(ActiveGraph::Base.query("MATCH (node2:`ToClass`)<-[rel1:`HAS_REL`]-(from_class:`FromClass`) return from_class").to_a.size).to eq(1) end end