当有两个并发事务t1
和t2
时(我将跳过样板文件,假设我按规定做所有事情):
线程A:t1
:
it1 = db.findNodes(label);
it1.forEach(node -> println(node.hasLabel(label))
线程B:t2
:
it2 = db.findNodes(label);
it2.forEach(node -> node.removeLabel(label))
现在,从我的角度来看,我们这里有一个巨大的不一致性:如果线程a的执行速度比线程B慢,那么在这一点上,我们检查a中的节点是否有标签label
,结果将是false
!
作为一名开发人员,我知道由于迭代器是懒惰的,这是可以预测的,我看到了这种行为背后的原因,但作为一名API用户,我真的很恼火,因为我不能100%确定我请求的具有label
的节点是否没有它!
此外,可能存在这样的情况,即不可能在任何实体上获得写锁,以保护所有这些节点不受并发修改的影响,因此即使使用一些优秀的工具,我也无法保持一致性。
我真的不认为这是一个bug,而是一个疯狂的功能。然而,我真的很高兴知道是否有任何解决方案可以帮助我解决我的问题。
更新:以下是这种伪竞赛条件的发生方式:
Before: create 100 nodes with :Label
A: get iterator for all nodes with :Label
B: get iterator for all nodes with :Label
A: consume e.g. 50 nodes
B: remove labels from all nodes, commit
A: see the rest of the nodes as the ones not having :Label
你有一个棘手的问题——既然没有人试图回答,我就试着回答。
我认为答案在于neo4j如何处理交易的细节。这个处理事务隔离的特定链接对我来说似乎非常相关,它说:
Neo4j中的事务使用读提交隔离级别意味着他们将在提交数据后立即看到数据,而不会查看其他尚未提交的事务中的数据。这隔离类型比序列化弱,但提供了显著的性能优势,同时足以应对压倒性优势大多数情况下。
我想当然地认为,您删除这些标签是在交易中发生的。我的阅读表明,在线程B全部完成之前,线程a中没有一个标签可以更改。这是因为您可能会从许多节点中删除标签,但在提交删除事务之前,这些标签对任何其他线程来说都不是真实的/可见的。至少应该是这样。
因此,这里的竞争条件是线程A在执行线程B时启动,但在提交线程B之前启动。
我认为你最好的答案可能来自该链接的第二段:
此外,Neo4j Java API(请参阅高级用法)允许显式锁定节点和关系。使用锁提供了机会通过获得和显式释放锁。例如,如果对公共节点或关系,则所有事务都将在那把锁—从而给出串行化隔离级别的效果。
在线程B内部,您可能希望获取正在修改的节点的读锁。该锁将在事务提交时释放。
我不能百分之百肯定这个答案,但我认为这是有道理的。如果有更有经验的人可以改进或反驳,请加入。