提问人:WobiHH 提问时间:11/2/2023 最后编辑:WobiHH 更新时间:11/7/2023 访问量:61
在不同事务中插入相同类型的顶点时的 Neptune ConcurrentModificationException
Neptune ConcurrentModificationException when inserting same type of vertices in different transaction
问:
我们正在使用 AWS Neptune 数据库,目前我们遇到了两个用户同时将某些内容保存到数据库的问题。
在这张图片中,您可以以简化的方式看到我们的模型。我们有带有标签“A”的根顶点,然后可以有多个“B”顶点,这些顶点又可以有多个“C”顶点,然后是一堆相连的顶点,请参阅此处的图形模型
我们所做的是启动一个事务,添加 C、D、E、F、G 顶点和边,提交事务。
当现在两个用户同时点击完全独立的“A”顶点的“保存”时,就会出现问题。 用户 1 想要为顶点 A (ID: 4711) 保存一个新的子图,用户 2 想要为顶点 A (ID: 4712) 保存一个新的子图。这个子图是完全独立的,它们之间没有联系。我的第一个假设是,在顶点 A (ID: 4711) 下添加子图对在不同的顶点 A (ID: 4712) 下添加子图没有影响。 添加操作可能需要几秒钟。现在,如果两个事务同时尝试添加一个子图,我们会得到以下异常:
Caused by: java.util.concurrent.CompletionException: org.apache.tinkerpop.gremlin.driver.exception.ResponseException: {"detailedMessage":"Failed to complete Insert operation for a Vertex due to conflicting concurrent operations. Please retry. 0 transactions are currently rolling back.","requestId":"64be5fdb-4208-4729-bc68-dccfecbfc87f","code":"ConcurrentModificationException"}
Caused by: org.apache.tinkerpop.gremlin.driver.exception.ResponseException: {"detailedMessage":"Failed to complete Insert operation for a Vertex due to conflicting concurrent operations. Please retry. 0 transactions are currently rolling back.","requestId":"64be5fdb-4208-4729-bc68-dccfecbfc87f","code":"ConcurrentModificationException"}
我在事务中的调试器中停了下来,并尝试在 Jupyter Notebook 上添加顶点。调试器停止时,我无法添加顶点“A”。但是插入一个新的顶点“XYZ”是没有问题的,它以前在图中没有使用过。
我假设添加顶点会阻止其他事务的索引。这是我在文档中找到的:
我想说的是,我们使用 SERIALIZABLE 作为隔离级别:https://docs.aws.amazon.com/neptune/latest/userguide/transactions-isolation-levels.html: READ UNCOMMITTED – 允许所有三种类型的交互(即脏读取、不可重复读取和幻像读取)。 READ COMMITTED – 脏读取是不可能的,但不可重复和幻像读取是可能的。 可重复读取 – 脏读取和不可重复读取都是不可能的,但幻像读取仍然是可能的。 **可序列化 **– 三种类型的交互现象都不会发生。
https://docs.aws.amazon.com/neptune/latest/userguide/access-graph-gremlin-transactions.html: 无会话只读查询在 SNAPSHOT 隔离下执行,但在显式事务中运行的只读查询在 SERIALIZABLE 隔离下执行。与在 SNAPSHOT 隔离下运行的查询不同,在 SERIALIZABLE 隔离下执行的只读查询会产生更高的开销,并且可能会阻止或被并发写入阻止。
查询如下所示:
g.V("f6e9a3ed-5b2e-400a-83f4-074af0fee71f").fold().coalesce(unfold(),addV("C").property(single,"status",'"abc"').property(T.id,"f6e9a3ed-5b2e-400a-83f4-074af0fee71f")).id().fold()
我只是认为合并可能是这篇文章中的问题:使用 gremlin javascript 语言变体的 Amazon Neptune 中的 ConcurrentModificationException,但我试图在事务中停止我的调试器并刚刚执行 g.addV(“C”) 并遇到了同样的问题。
是否可以同时为不同的图添加子图?
经过一些更深入的调查,我发现我们事务中的第一个查询会阻止整个数据库。你知道为什么这个查询会阻止一切吗?
g.V().hasLabel("A").has("v",4711).outE("s").inV()
.hasLabel("B").hasId("c4...a","a0...f","ac...3")
.outE("h").inV()
.bothE("sv","ev")
.bothV().not(hasLabel("C")).simplePath()
.barrier()
.repeat(outE().not(hasLabel("sv","ev")).simplePath().inV())
.until(or(outE().count().is(0),hasLabel("C"),hasLabel("AP","AC").bothE("sv","ev").count().is(P.gt(0))))
.path().unfold().dedup().or(hasLabel("sp").has("ev"),hasLabel("ev")).barrier()
.drop()
答:
我相信你所看到的是间隙锁[1]的效果。如果您使用的是基于会话的事务,则一旦您开始写入给定的顶点、边或属性,则存储这些值的关联索引中的间隙将被锁定。因此,任何其他试图写入同一间隙的东西都会遇到死锁情况。在 Neptune 中,死锁被视为“赢家优先”,因此在死锁情况下写入的任何后续请求都会引发 ConcurrentModificationException。
另一点需要注意的是,写入索引的值不是您在查询中写入的值。Neptune 使用字典并将给定值的字典 ID 存储在索引中。该字典值是非确定性的,不是您可以提前预测的。这使得试图避免 CME 有点困难。
目前对 CME 的指导是在应用程序中实施重试和指数退避策略 [2]。在大多数情况下,立即重试将成功。如果您有长时间运行的事务,则可能需要调整等待/重试期,或者为每次尝试的重试时间设置更陡峭的指数曲线。
[2] https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/retry-backoff.html
评论
has()
评论