apache / incubator-hugegraph

A graph database that supports more than 100+ billion data, high performance and scalability (Include OLTP Engine & REST-API & Backends)
https://hugegraph.apache.org
Apache License 2.0
2.58k stars 512 forks source link

并发查询时 /gremlin接口返回的结果与预期不符合 #2486

Open fantengfeiNana opened 3 months ago

fantengfeiNana commented 3 months ago

Bug Type (问题类型)

gremlin (结果不合预期)

Before submit

Environment (环境信息)

Expected & Actual behavior (期望与实际表现)

我们现在使用hugegraph1.2.0版本作为底层应用,当我们 使用一个线程池并发执行g.V().count()【获取顶点个数】和.g.E().limit(9999)【获取所有的边集合】时,发现接口返回的数据与请求的预期不一致,导致进一步解析时无法正确解析,请问/gremlin接口是异步么,还是需要控制访问方式?直接现象就是g.E().limit(9999) 返回的结果是 g.V().count()执行的结果 当我使用自动化测试时,并发执行时,hugegraph也是偶现异常 image

image

请问我该如何解决此类问题?

Vertex/Edge example (问题点 / 边数据举例)

No response

Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)

No response

dosubot[bot] commented 3 months ago

你好,@fantengfeiNana!很高兴再次看到你的问题,希望这次我们也能一起找到解决方案。

根据您的描述,我理解您在使用HugeGraph 1.2.0版本时,通过/gremlin接口并发执行g.V().count()和.g.E().limit(9999)时,返回的数据与预期不一致。这可能是由于HugeGraph的设计和配置,以及图数据库并发操作的特性导致的。以下是可能的原因和解决策略:

  1. 事务隔离和一致性:HugeGraph,像许多图数据库,使用事务进行操作。当执行并发操作时,特别是读写操作,事务的隔离级别可能会影响读取的数据的一致性。如果一个线程正在更新图形,而另一个线程正在读取,根据隔离级别,读操作可能会看到图形的部分状态。

  2. 速率限制:提供的代码显示HugeGraph对读写操作都有速率限制(this.writeRateLimiterthis.readRateLimiter)。如果并发操作超过这些限制,可能会导致限流,这可能会影响操作的执行和完成,可能导致不一致或意外的结果。

  3. 缓存一致性:HugeGraph使用缓存机制(CachedGraphTransactionCachedSchemaTransaction)来提高性能。在并发场景中,可能会出现缓存一致性问题,其中一个线程更新图形,但另一个线程看到的缓存没有及时更新,导致读取不一致。

  4. 后端存储一致性:后端存储的一致性模型也可能影响并发操作的结果。根据后端存储如何处理并发操作,可能会在反映一个操作的更新到另一个操作看到的视图中有延迟。

解决策略

关于/gremlin接口的行为,根据提供的代码,该接口的请求处理方式(同步或异步)是由底层服务器框架(例如Servlet,Netty)和HugeGraph服务器的配置决定的,而不是在提供的代码片段中明确定义的。然而,@POST@GET等注解的存在以及@Context用于注入请求相关信息的使用,表明这个API是基于JAX-RS(Jakarta RESTful Web Services)构建的。JAX-RS支持同步和异步处理。使用哪种方式通常取决于服务器如何设置以及应用程序如何设计来处理请求。

在提供的代码中,postget方法没有使用JAX-RS的异步特性,如AsyncResponse。因此,仅根据这段代码,看起来请求是同步处理的。然而,如果没有关于服务器设置的额外上下文,或者在应用程序的其他地方没有异步配置或实现,就无法明确地说明并发行为。

关于HugeGraph如何在并发场景中确保缓存一致性,HugeGraph通过组合缓存失效,清除和事件监听机制来确保缓存一致性。具体来说,CachedGraphTransaction类监听与缓存和存储更改相关的事件。它订阅存储事件(如初始化,清除和截断)和缓存事件(如失效和清除缓存项)。这是在listenChanges方法中完成的,其中它为存储和缓存事件注册事件监听器。

在接收到特定事件后,系统会使缓存项失效或清除。例如,当一个顶点或边被更新或删除时,相应的缓存项会被使失效,以确保后续的读取从后端存储中获取更新的数据。这是在notifyChanges方法中处理的,该方法根据更改的类型(例如,顶点或边的更新/删除)触发缓存失效或清除。

系统根据某些条件决定是否缓存查询的结果,例如结果集的大小和查询的类型。对于顶点,它检查顶点是否有低于某个阈值的属性数量(MAX_CACHE_PROPS_PER_VERTEX)。对于边,它限制每个查询缓存的边的数量(MAX_CACHE_EDGES_PER_QUERY),并避免对过大或需要分页的查询进行缓存。

在变更提交到后端存储后,缓存会相应地更新。如果顶点被更新或删除,它们的缓存项要么被更新(如果它们仍然满足缓存条件),要么被使失效。如果任何顶点或边发生变化,边缓存会被清除,以确保边查询获取最新的数据。这是在commitMutation2Backend方法中处理的。

系统使用事件中心(EventHub)来通知感兴趣的组件关于缓存相关的事件。这允许一个解耦的架构,其中组件可以对缓存更改做出反应,而无需与缓存管理逻辑紧密集成。

总的来说,HugeGraph通过事件驱动的缓存失效和清除,基于查询特性的条件缓存,以及后端变更后的主动缓存更新的复杂机制,确保了并发场景中的缓存一致性。这种方法有助于保持缓存数据与底层数据存储的一致性,即使在并发读写操作的情况下也是如此。

关于您提到的异常,我在HugeGraph的issue中找到了一个类似的问题[Bug] Incorrect return value of the same query,这个问题在2022年11月6日的最后一次更新中仍然是开放的。建议的解决方案是减少数据集到3条边,问题仍然存在。这可能与您遇到的问题有关,您可以参考这个issue的讨论。

希望这些信息对您有所帮助。如果您有任何其他问题或需要进一步的澄清,请随时提问。

Sources

#### About Dosu This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant. If you want to continue the conversation, start your reply with @dosu-bot.
imbajin commented 3 months ago

Could u provide a MVP way to reproduce the problem?

And also could refer https://github.com/apache/incubator-hugegraph/issues/2187#issuecomment-1511248248 to use limit(-1).count()