neo4j的Cypher事务是否存在问题?

21
TL;DR: 我可能要疯了,或者neo4j的事务出了点问题。看起来未提交的节点在已提交的事务外部是可用的,其中缺少属性 - 或者其他同样奇怪的情况。
我们的node.js应用程序使用neo4j。它的一部分必须生成唯一的ID。我们有以下Cypher查询,旨在定位最后一个 :Id 类型的节点,并尝试提交一个新的 :Id 节点,其值为last_uuid+1
MATCH (i:Id) WITH i ORDER BY i.uuid DESC LIMIT 1  #with it like a sub-return, will "run" the rest with the last i at read-time
CREATE (n:Id {label:"Test"}) 
SET n.uuid = i.uuid + 1
RETURN n

还有一个限制条件:

neo4j-sh (?)$ schema
Indexes
  ON :Id(uuid) ONLINE (for uniqueness constraint) 

Constraints
  ON (id:Id) ASSERT id.uuid IS UNIQUE

并且 DB 是用 (:Id{uuid:1}) 初始化的,以启动这个乐趣。

应用程序代码基本上会重试上述查询,直到它成功为止。如果两个或更多的 Id 创建请求同时到达,则只有一个将通过,其余的将失败并由应用程序代码重试。

这个方法一直运作良好,直到我们尝试并行操作时。

代码开始返回没有 uuid 的数据。经过大量调查,结果发现查询的写入部分(CREATE...)从 MATCH 中接收到一个没有 .uuid(或其他)属性的 :Id。这是不可能的。这是唯一在这些节点上操作的代码。

最奇怪(也许)的是,如果我保存 inodeid 以定位 DB 中的那个节点,实际上它存在,并且具有 .uuid 属性。

为了隔离这种行为,我编写了一个 PoC:neo4j-transaction-test 它应该很容易在 nodejs 上运行。

它基本上比上面的代码多一点 - 尝试创建 Id,设置 prev_labelprev_nodeidprev_uuid 为上一个节点(i)的值。它为每个接收到本地主机:9339 上的 GET 请求运行查询,并输出:

 > node server.js 
 * 1412125626667 Listening on 9339
 Req Id | Datetime | -> $uuid $nodeid
 1 1412125631677 'GET'       # When it first receives the GET request
 1 1412125631710 '->' 9 60   # When neo4j returns; numbers are $uuid $node_id)

当事情开始并发时,查询可能会失败:

3 1412125777096 '(retry) (0)' 'Node 64 already exists with label Id and property "uuid"=[13]'
4 1412125777098 '(retry) (0)' 'Node 64 already exists with label Id and property "uuid"=[13]'
de[]

这是可以预料的,它们已经被重试。如果我们每秒发送一些请求来“猛攻”服务器(ab -n 1000 -c 10 http://localhost:9339/),最终会看到:

...
59 1412127103011 'GET'
23 1412127103024 'ERROR - EMPTY UUID' '{"this_nodeid":22,"prev_nodeid":20,"label":"Test"}'

Error: Empty UUID received

(这里“eventually”的意思是几乎瞬间)一个节点回来了,但没有uuid、prev_uuid或prev_label。this_nodeid和prev_nodeid指的是neo4j的内部id。如果我们从之前(i)Id节点开始查找(通过nodeid-20):

neo4j-sh (?)$ match (i) where id(i)=20 return i;
+--------------------------------------------------------------------------------------------+
| i                                                                                          |
+--------------------------------------------------------------------------------------------+
| Node[20]{uuid:10,label:"Test",prev_label:"Test",prev_uuid:9,prev_nodeid:17,this_nodeid:20} |
+--------------------------------------------------------------------------------------------+
1 row
19 ms

它恰如其分,包括 .uuid。新的确实就像上面返回的那样创建:

neo4j-sh (?)$ match (i) where id(i)=22 return i;
+------------------------------------------------------+
| i                                                    |
+------------------------------------------------------+
| Node[22]{label:"Test",prev_nodeid:20,this_nodeid:22} |
+------------------------------------------------------+
1 row
17 ms

没有prev_label或prev_uuid。这怎么可能?我错过了什么?是不完整的:Id节点泄漏到我的查询中吗?

我尝试过重新启动、擦除数据目录、擦除数据目录后重新启动、清理日志(没有有趣的东西,甚至在正确的时间——当上述情况发生时也没有)。现在我已经开始质疑自己对它应该如何工作的理解了。

这是在12.04和neo4j 2.1.1上的。更多版本信息Neo4j启动/关闭日志

我知道这不是创建UUID的最佳方式。这个问题是关于理解如果neo4j的事务按预期工作,这些结果如何可能。


3
你是否使用了“合并(优质锁定)”而不是“创建”?你能否将Neo4J Node.js代码移除,并尝试直接访问事务端点以排除此问题(http://docs.neo4j.org/chunked/stable/rest-api-transactional.html)。我认为这个特定的(非官方)Node库会连接到旧的端点,因为你的Neo比较新,但是你的Node不是最新的版本。你也可以查看原始响应数据。 - JohnMark13
@TasosBitsios 这个问题有任何解决方案了吗? - JohnMark13
我理解@TasosBitsios的意思,但是既然你已经将代码添加到GIT中,那么将neo4j包装器取出并用POST替换为http://localhost:7474/db/data/cypherhttp://localhost:7474/db/data/transaction/commit与您的Cypher有效载荷。这将验证问题是否出现在一个或两个REST端点而不是Node库中。 - JohnMark13
好的,这可能有助于隔离问题。我到电脑前会进行更改。谢谢! - Tasos Bitsios
1
你有没有发布过问题?我现在也遇到了这个或非常相似的问题。我在https://github.com/neo4j/neo4j/issues/3864上开了一个问题。 - subvertallchris
显示剩余3条评论
1个回答

2
我们在Neo4J中发现了一个类似的问题,当进行并发事务写入时,Neo4J 2.2.5引入了修复程序来解决这个问题(我们拥有企业支持并提出了工单以解决此问题)。您可能没有完全相同的问题,但是尝试使用2.2.5再次尝试是否仍然存在问题可能是值得的。
像您所说,有更好的方法来生成ID。此外,您应该使用MAX而不是LIMITORDER BY来获取最新的内容,但这也不是重点;-)。
祝你好运。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接