【问题标题】:ConcurrentModificationException in amazon neptune using gremlin javascript language variant亚马逊海王星中的 ConcurrentModificationException 使用 gremlin javascript 语言变体
【发布时间】:2021-11-11 17:54:27
【问题描述】:

我正在尝试使用promise.all() 在块中检查并插入 1000 个顶点。代码如下:

public async createManyByKey(label: string, key: string, properties: object[]): Promise<T[]> {
    const promises = [];
    const allVertices = __.addV(label);
    const propKeys: Array<string> = Object.keys(properties[0]);
     
    for(const propKey of propKeys){
      allVertices.property(propKey, __.select(propKey));
    }

    const chunkedProperties = chunk(properties, 5); // [["demo-1", "demo-2", "demo-3", "demo-4", "demo-5"], [...], ...]
    
    for(const property of chunkedProperties){
        const singleQuery = this.g.withSideEffect('User', property)
       .inject(property)
       .unfold().as('data')
       .coalesce(__.V().hasLabel(label).where(eq('data')).by(key).by(__.select(key)), allVertices).iterate();

       promises.push(singleQuery);
     }

    const result = await Promise.all(promises);

    return result;
  }

此代码引发 ConcurrentModificationException。需要帮助来解决/改进此问题。

【问题讨论】:

    标签: javascript gremlin amazon-neptune gremlinjs aws-neptune


    【解决方案1】:

    我不太确定您正在使用的数据和参数,但我需要稍微修改一下您的查询,以使其能够使用我方便的数据集(航线),如下所示。我这样做是为了帮助我思考您的查询在做什么。我不得不更改第二个by 步骤。我不确定那是如何工作的。

    gremlin> g.inject(['AUS','ATL','XXX']).unfold().as('d').
    ......1>   coalesce(__.V().hasLabel('airport').limit(10).
    ......2>            where(eq('d')).
    ......3>              by('code').
    ......4>              by(), 
    ......5>            constant('X'))  
    ==>v['3']
    ==>v['1']
    ==>X 
    

    虽然像这样的查询单独运行良好,但一旦您开始运行多个异步承诺(包含查询中的变异步骤),可能发生的情况是一个承诺试图访问被锁定的图形的一部分另一个。即使我认为执行更“并发”而不是真正的“并行”,如果一个承诺由于 IO 等待允许另一个运行而产生,但如果前一个承诺已经在数据库中锁定,下一个承诺也可能会失败需要。在您的情况下,因为您有一个 coalesce 引用具有给定标签和属性的所有顶点,这可能会导致冲突锁定。如果您在每次 for 循环迭代之后 await 而不是在一个大的 Promise.all 中完成所有操作,也许效果会更好。

    要记住的其他一点是,无论如何,此查询都会有些昂贵,因为对于每个 for 循环迭代,中间遍历 V 将发生五次(在您的示例中)。这是因为注入数据的 unfold 取自大小为 5 的块,因此会产生五个遍历器,每个遍历器都从查看 V 开始。

    2021 年 11 月 17 日编辑

    正如在 cmets 中所讨论的,我怀疑最佳路径实际上是使用多个查询。第一个查询只是对您可能要添加的所有 ID 执行 g.V(id1,id2,...)。让它返回找到的 ID 列表。从要添加的集合中删除那些。接下来将添加部分分成批次并在没有coalesce 的情况下进行,因为您现在知道这些元素不存在。这很可能是减少锁定和避免 CME(异常)的最佳方法。除非其他人可能也在尝试并行添加它们,否则我认为我会采用这种方法。

    【讨论】:

    • 如果promise.all() 无法按预期工作,那么我该如何提高性能。我尝试在单个查询中插入 1000 个顶点,耗时超过 2 分钟。性能如下:- - 100 个顶点 - 1.2 秒 - 300 个顶点 - 20 秒 - 500 个顶点 - 45 秒 - 900 个顶点 - 1 分 30 秒 有什么办法可以提高这个性能
    • 在探索选项之前,您是否能够验证在每次 for 循环迭代后执行 await 是否消除了异常?
    • 我试过它以同步/顺序的方式执行。所以它一直等到每个循环完成,当涉及到大数字时,这会更慢。
    • 通常,获得良好写入性能的最佳方法是多线程方法,其中每个查询一次添加 50 到 100 个顶点 - 与您尝试的方法没有什么不同。在您的情况下,使事情复杂化的是,由于coalesce 步骤,特别是hasLabel(label),您的查询可能会锁定图表的大部分,因此完全异步/多线程方法将出现异常问题.中间遍历V,正如您在分析查询时看到的那样,也导致访问的顶点大量扇出。
    • 在这种特定情况下,最好运行 2 个查询,其中第一个查找所有不存在的顶点,第二个添加它们而不需要 coalesce
    猜你喜欢
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 2018-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 1970-01-01
    相关资源
    最近更新 更多