neo4j的密码交易是否破裂?

Tas*_*ios 21 transactions neo4j cypher node-neo4j

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
Run Code Online (Sandbox Code Playgroud)

还有一个约束:

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

Constraints
  ON (id:Id) ASSERT id.uuid IS UNIQUE
Run Code Online (Sandbox Code Playgroud)

并初始化DB (:Id{uuid:1})以启动这种快乐.

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

这是有效的,直到我们并行尝试.

代码在没有uuid的情况下开始返回数据.经过大量的调查,结果是查询的写入部分(CREATE ...)以某种方式从MATCH接收:Id,没有.uuid(或其他)属性.这不应该是可能的.这是在这些节点上运行的唯一代码.

最奇怪的(也许)的事情是,如果我救inodeid,以找到在数据库节点,它确实存在,并有.uuid属性.

为了隔离这种行为,我编写了一个PoC:neo4j-transaction-test使用nodejs运行应该非常简单.

它基本上是比上面的代码更一点点位-尝试创建标识,设置prev_label,prev_nodeidprev_uuid以前的节点的(I)值.它为在localhost: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)
Run Code Online (Sandbox Code Playgroud)

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

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[]
Run Code Online (Sandbox Code Playgroud)

这是可以预期的,并且它们被重试.如果我们用每秒几次的请求"抨击"服务器(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
Run Code Online (Sandbox Code Playgroud)

(并且最终,我的意思是几乎立即)节点返回,没有uuid,prev_uuid或prev_label.this_nodeid和prev_nodeid指的是neo4j的内部id.如果我们查看它们,从previous(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
Run Code Online (Sandbox Code Playgroud)

它应该是应有的..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
Run Code Online (Sandbox Code Playgroud)

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

我已经尝试重新启动,擦除数据目录,擦除数据目录后重新启动,清除日志(没有什么有趣的,甚至无聊但在正确的时间 - 当上述情况发生时).我现在正处于质疑我对这应该如何运作的理解的程度.

这是12.04与neo4j 2.1.1.更多版本信息Neo4j启动/关闭日志.

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

Mat*_*rne 2

我们注意到 Neo4J 在并发事务写入期间存在类似问题,Neo4J 2.2.5 中引入了一个修复程序来解决这个问题(我们有企业支持并提出了一个问题来解决这个问题)。您可能没有遇到完全相同的问题,但可能值得使用 2.2.5 再次尝试,看看它是否仍然是一个问题。

就像你说的,有更好的方法来生成 id。另外,您应该使用MAX获取最新版本而不是LIMITORDER BY,但这也不是重点;-)。

祝你好运。