简体   繁体   English

neo4j的密码交易是否破裂?

[英]Are neo4j's cypher transactions broken?

TL;DR: I am either losing my mind, or neo4j's transactions are slightly broken. TL; DR:我要么失去理智,要么neo4j的交易稍有破产。 It looks like uncommitted nodes are available outside of committed transactions, with missing properties - or something equally weird. 看起来未提交的节点在提交的事务之外可用,缺少属性 - 或者同样奇怪的东西。

Our node.js app uses neo4j. 我们的node.js应用程序使用neo4j。 A part of it has to generate unique IDs. 它的一部分必须生成唯一的ID。 We have the following cypher query that is meant to locate the last :Id -type node and attempt to commit a new :Id node with last_uuid+1 . 我们有以下cypher查询,用于查找最后一个:Id类型节点并尝试使用last_uuid+1提交new :Id节点。

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

There is also a constraint: 还有一个约束:

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

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

And the DB is initialized with an (:Id{uuid:1}) to kick-start this joy. DB用一个(:Id{uuid:1})初始化,以启动这种快乐。

The app code basically retries the above query until it is successful. 应用程序代码基本上会重试上述查询,直到成功为止。 If two or more Id creation requests hit at the same time, only one of them will go through, the rest of them would fail and be retried by the app code. 如果两个或多个Id创建请求同时命中,其中只有一个将通过,其余的将失败并由应用程序代码重试。

This worked, until we tried it in parallel. 这是有效的,直到我们并行尝试。

The code started returning data without a uuid. 代码在没有uuid的情况下开始返回数据。 After a lot of investigation, it turns out that the write part of the query (CREATE...) is somehow receiving an :Id from MATCH which has no .uuid (or other) properties. 经过大量的调查,结果是查询的写入部分(CREATE ...)以某种方式从MATCH接收:Id,没有.uuid(或其他)属性。 This shouldn't be possible. 这不应该是可能的。 This is the only code that operates on those nodes. 这是在这些节点上运行的唯一代码。

The strangest (perhaps) thing, is that if I save i 's nodeid in order to locate that node in the DB, it actually exists and has a .uuid property. 最奇怪的(可能)是,如果我保存inodeid以便在DB中找到该节点,它实际上存在具有.uuid属性。

In order to isolate this behaviour I wrote a PoC: neo4j-transaction-test It should be really simple to run with nodejs. 为了隔离这种行为,我编写了一个PoC: neo4j-transaction-test使用nodejs运行应该非常简单。

It is basically a tad bit more than the above code - tries to create the Id, setting prev_label , prev_nodeid , and prev_uuid to the previous Node's (i) values. 它基本上比上面的代码多一点 - 尝试创建Id,将prev_labelprev_nodeidprev_uuid设置为先前Node的(i)值。 It runs the query for every GET request it receives on localhost:9339 and outputs: 它为在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)

when things start getting concurrent, queries may fail: 当事情开始并发时,查询可能会失败:

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[]

which is to be expected, and they are retried. 这是可以预期的,并且它们被重试。 If we "slam" the server with a couple of reqs per second ( ab -n 1000 -c 10 http://localhost:9339/ ), we will eventually see: 如果我们用每秒几次reqs( 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

(and by eventually, I mean almost instantly) A node comes back, without uuid, prev_uuid or prev_label. (并且最终,我的意思是几乎立即)节点返回,没有uuid,prev_uuid或prev_label。 this_nodeid and prev_nodeid refer to neo4j's internal id. this_nodeid和prev_nodeid指的是neo4j的内部id。 If we look these up, starting with the previous ( i ) Id node (by nodeid - 20): 如果我们查看它们,从前一个( 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

It is exactly as it should be. 它应该是应有的。 .uuid and all. .uuid和所有。 The new one is indeed created just as it is returned above: 新的确实是在上面返回时创建的:

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

No prev_label or prev_uuid. 没有prev_label或prev_uuid。 How is this possible? 这怎么可能? What am I missing? 我错过了什么? Is an incomplete :Id node leaking into my query? 是不完整的:Id节点泄漏到我的查询中?

I've tried restarting, wiping the data dir, restarting after wiping the data dir, scavenged the logs (nothing interesting, or even boring but at the right time - when the above is happening). 我已经尝试重新启动,擦除数据目录,擦除数据目录后重新启动,清除日志(没有什么有趣的,甚至无聊但在正确的时间 - 当上述情况发生时)。 I am now at the point where I am questioning my understanding of how this is supposed to work. 我现在正处于质疑我对这应该如何运作的理解的程度。

This is on 12.04 with neo4j 2.1.1. 这是12.04与neo4j 2.1.1。 More Version Info and Neo4j startup/shutdown logs . 更多版本信息Neo4j启动/关闭日志

I am aware that this is not an optimal way to create UUIDs. 我知道这不是创建UUID的最佳方式。 This question is about understanding how these results are possible if neo4j's transactions work as expected. 这个问题是关于如果neo4j的交易按预期工作,如何理解这些结果。

We noticed a similar issue in Neo4J during concurrent transactional writes and there was a fix introduced in Neo4J 2.2.5 to address this (we have Enterprise support and raised a ticket to get this fixed). 我们在并发事务写入期间注意到Neo4J中存在类似的问题,并且在Neo4J 2.2.5中引入了一个解决此问题的修复程序(我们有Enterprise支持并提出了修复此问题的票证)。 You may not have exactly the same problem, but it might be worth trying again using 2.2.5 to see if it's still an issue. 您可能没有完全相同的问题,但可能值得再次尝试使用2.2.5来查看它是否仍然存在问题。

Like you said, there are better ways to generate ids. 就像你说的,有更好的方法来生成id。 Also, you should use MAX to get the latest instead of LIMIT and ORDER BY , but that's besides the point also ;-). 此外,您应该使用MAX来获取最新的而不是LIMITORDER BY ,但除此之外还有;-)。

Good luck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM