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
数据库初始化为(:Id{uuid:1})
,以启动这种快乐。
应用程序代码基本上会重试上述查询,直到成功为止。如果两个或多个Id创建请求同时命中,其中只有一个将通过,其余的将失败并由应用程序代码重试。
这很有效,直到我们并行尝试。
代码在没有uuid的情况下开始返回数据。经过大量的调查,结果是查询的写入部分(CREATE ...)以某种方式从MATCH接收:Id,没有.uuid(或其他)属性。这不应该是可能的。这是在这些节点上运行的唯一代码。
最奇怪的(可能)是,如果我保存i
的{{1}}以便在数据库中找到该节点,它实际上存在并且有一个.uuid property。
为了隔离这种行为,我编写了一个PoC:neo4j-transaction-test使用nodejs运行应该非常简单。
它基本上比上面的代码多一点 - 尝试创建Id,将nodeid
,prev_label
和prev_nodeid
设置为前一个Node的(i)值。它为在localhost上接收的每个GET请求运行查询:9339并输出:
prev_uuid
当事情开始并发时,查询可能会失败:
> 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/
(最终,我的意思是几乎立即)一个节点回来了,没有uuid,prev_uuid或prev_label。 this_nodeid和prev_nodeid指的是neo4j的内部id。如果我们查看这些内容,请从上一个(...
59 1412127103011 'GET'
23 1412127103024 'ERROR - EMPTY UUID' '{"this_nodeid":22,"prev_nodeid":20,"label":"Test"}'
Error: Empty UUID received
)Id节点开始(通过nodeid - 20):
i
它应该是应有的。 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
没有prev_label或prev_uuid。这怎么可能?我错过了什么?是不完整的:Id节点泄漏到我的查询中?
我已经尝试重新启动,擦除数据目录,在擦除数据目录后重新启动,清除日志(没有什么有趣的,甚至是无聊但在正确的时间 - 当上述情况发生时)。我现在正在质疑我对这应该如何运作的理解。
这是12.04与neo4j 2.1.1。 More Version Info和Neo4j startup/shutdown logs。
我知道这不是创建UUID的最佳方式。这个问题是关于如果neo4j的交易按预期工作,如何理解这些结果。
答案 0 :(得分:1)
我们在并发事务写入期间注意到Neo4J中存在类似问题,并且在Neo4J 2.2.5中引入了修复程序以解决此问题(我们有Enterprise支持并提出了修复此问题的凭证)。您可能没有完全相同的问题,但是可能值得再次尝试使用2.2.5来查看它是否仍然存在问题。
就像你说的,有更好的方法来生成id。此外,您应该使用MAX
来获取最新信息而不是LIMIT
和ORDER BY
,但除了这一点之外,还应该使用; - )。