Gremlin:ConcurrentModificationException 和多线程

Gremlin: ConcurrentModificationException and multithreading

我的应用程序尚未上线,因此我正在测试 Gremlin 查询的性能,然后再投入生产。

为了测试,我使用了一个查询,该查询将边从一个顶点添加到 300 个其他顶点。它做了更多的事情,但这是简单的描述。我添加了这个提到的 300 工作量只是为了测试。

如果我 运行 一个接一个地查询 300 次,则需要将近 3 分钟才能完成并创建 90.000 个边 (300 x 300)。

我很担心,因为如果我有 60.000 个用户同时使用我的应用程序,他们可能会在 2 分钟内使用此查询创建 90.000 个边,而同时 60.000 个用户在我的应用程序中并不多案件。 如果我同时拥有 100 万用户,我将需要许多满负荷运行的服务器,这超出了我的预算。

然后我注意到当我的测试执行时 CPU 并没有显示太多 activity,我不知道为什么,我不知道数据库内部是如何工作的。 所以我认为也许更真实的情况是同时调用我的所有查询,因为当我尝试测试时,真实用户会发生这种情况,我得到 ConcurrentModificationException.

据我所知,发生此错误是因为同时在 2 个查询中读取或写入边或顶点,这在我的应用程序中经常发生,因为所有用户顶点都在更改连接一直到相同的 4 个顶点,这种“碰撞”将一直发生。

我正在本地使用 gremlin 服务器 3.4.8 进行测试,使用带有 Node.js 的套接字进行连接。我的计划是在投入生产时使用 AWS Neptune 作为我的数据库。

我该怎么做才能重拾希望?我不知道关于这个主题的非常重要的事情,因为我不知道图形数据库内部是如何工作的。

编辑

我使用“指数退避”方法实现了一个逻辑,在收到错误时重试查询请求。它修复了 ConcurrentModificationException 但是在同时发送多个查询时 Gremlin Server 中存在很多问题,这表明 Gremlin Server 完全不支持多线程并且不稳定,我们应该按照响应所述在其他 Gremlin 兼容数据库中尝试多线程。我遇到了查询返回的数据随机不一致的情况,以及 NegativeArraySize 和来自数据库的其他随机内容等错误,请注意这一点,不要浪费时间认为您的代码可能像我遇到的那样被窃听。

虽然 TinkerPop 和 Gremlin 试图提供与供应商无关的体验,但他们实际上只在界面级别这样做。因此,虽然您可能能够 运行 在 JanusGraph、Neptune、CosmosDB 等中执行相同的查询,但您可能会发现性能存在差异,具体取决于查询的性质和相关图的程度能够优化该查询。

对于您的情况,请考虑 TinkerGraph,因为您在本地 运行 进行测试。 TinkerGraph 是一个没有事务功能的 in-memory 图,并且未被证明对于写入是线程安全的。如果您对其施加繁重的写入工作负载,我可以设想 ConcurrentModificationException 很容易生成。现在考虑 JanusGraph。如果您已经用它测试了繁重的写入工作负载,您可能会发现如果您的架构需要一个唯一的 属性 键并且必须修改您的代码才能执行,那么您会遇到大量 TemporaryLockingException 错误具有指数退避的事务重试。

这里的要点是,如果您的目标图是 Neptune 并且您进行了一次遍历,您已经测试了正确性并且现在担心性能,那么可能是时候在 Neptune 上进行负载测试以查看那里是否出现任何问题。

I'm worried because if I have like 60.000 users using my application at the same time they probably are going to be creating 90.000 edges in 2 minutes using this query and 60.000 users at the same time is not much in my case. If I have 1 million users at the same time I'm going to be needing many servers at full capacity, that is out of my budget.

您需要制定一个现实的测试计划。 60,000 个用户同时按下“提交”以触发此查询真的会发生什么吗?或者更可能是您有 100,000 名用户正在阅读,并且也许每半秒就有 3 名用户碰巧点击“提交”。

您的图表增长率似乎相当高,并且您在此处描述的预期用途将使您的图表很快进入数十亿边的类别(更不用说您可能拥有的任何其他写入)。您是否在具有数十亿条边的图上测试了您的读取工作负载?你有没有在海王星上明确地测试过?您是否考虑过如何维护 multi-billion 边图(例如,在需要新功能时更改架构,确保其正确增长等)?

所有这些问题都是反问,只是为了让你思考你的方向。祝你好运!

虽然已经有一个可接受的答案,但我想建议另一种方法来处理这个问题。

想法是弄清楚您是否真的需要在图形上进行同步写入。我建议当您收到请求时,只需使用此请求中的属性来获取 sub-graph / neighbors,然后继续执行业务逻辑。

同时将事件放入 SQS 或其他东西中,让异步系统处理写入 - 比如 AWS Lambda。因为在 SQS + Lambda 中,您可以根据系统的舒适度来决定写并发性(足够低,您的查询不会导致上述异常)。

进一步建议:您的写入爆炸半径异常高 - 您的查询在写入时不应触及那么多节点。您可以尝试将一些边转换为顶点以减小半径。然后,在插入节点时,您只需要为以前是边的顶点创建一条边,而不是为该节点相关的所有顶点创建数百条边。我希望这是有道理的。