Neo4j Cypher Merge查询隔离级别

时间:2014-09-01 09:53:02

标签: neo4j spring-data-neo4j

前几天我遇到了这个Gist http://www.neo4j.org/graphgist?8012859,并认为在HA群集中生成orderIds会很方便。我创建了一个简单的实现,我的单元测试失败了,因为count字段没有以线程安全的方式提供唯一值。

Andres Taylor在2.0 version of the documentation中有一条评论

  

我们将在2.0中更多地关注隔离。这个问题应该在最终版本发布之前修复。

我正在使用Neo4J嵌入式2.1.3,Neo HA和Spring Data Neo4J 3.1.4,虽然我在发行说明中找不到任何明确的Provisional Feature警告已被删除。

我相信MATCH查询的CREATE部分是孤立发生的,但我相信ON MATCH部分正在对陈旧数据进行操作,获取对单个唯一节点的写锁定,但在等待之后不再重新读取数据收购。查看transaction documentation看起来默认隔离是READ COMMITTED,因为我的Merge查询的性质(组合的读/写语义 - nid.count = nid.count + 1)我应该在执行之前手动获取独占锁手术?

代码段:

节点:

@NodeEntity
public class UniqueId {

  @GraphId
  private Long id;

  @Indexed(indexType=IndexType.LABEL, unique=true)
  private String type;

  private long count;

  //getters/setters/stuff
}

存储库:

public interface UniqueIdRepository extends GraphRepository<UniqueId> {

  @Query(value = "MERGE (nid:UniqueId{type:{0}}) " +
                 "ON CREATE SET nid:_UniqueId, nid.count = 1 " +
                 "ON MATCH SET nid.count = nid.count + 1 " +
                 "RETURN nid.count")
  public long generateUid(String type);
}

服务方式:

@Override
@Transactional
public Long generateOrderId() {
    return idRepository.generateUid("Order");
}

简单单元测试:

@Test
public void testLots() throws Exception {       
    final Map<Long, Object> ids = new ConcurrentHashMap<>();

    Thread t1 = new GenerateRunnable(200, ids);
    Thread t2 = new GenerateRunnable(200, ids);
    Thread t3 = new GenerateRunnable(200, ids);
    Thread t4 = new GenerateRunnable(200, ids);
    Thread t5 = new GenerateRunnable(200, ids);

    //Run method now just contains...
    // for (int i = 0; i < iterations; ++i) {
    //      Long uid = idService.generateOrderId();
    //      objects.put(uid, PRESENT);
    //      System.out.println(uid);
    //  }

    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();

    assertThat(ids.size(), equalTo(1000));
}

虽然他运行方法正在使用该服务,但我还直接调用了存储库,使用SDN neo4j模板运行查询并直接使用ExecutionEngine - 在后面的情况下手动创建和提交事务。 / p>

我确实开始了一个conversation in the Google Group,迈克尔在查询语义中给我一个错误的帮助,但是我把对话带到了这里,因为我相信我的代码中有错误,或者我的理解而不是Neo4J本身存在问题。

如果我能正确阅读Scala,我可能有更好的机会通过自己完成这项工作,所以我会把它放在我的学习列表中,但是我注意到在调试时我在TransactionBoundQueryContext $ NodeOperations中输入等待状态.setProperty(此时已通过MergeNodeAction的findNodes组件获取并释放锁定,现在处理onMatch.foreach(...)代码MergeNodeAction:86)

我有其他生成此值的机制,但这看起来非常简洁,我做错了什么?

1 个答案:

答案 0 :(得分:0)

所以我不知道这是不是一个错误,或者我是否以一种无意的方式使用MERGE,可能我的问题很漫长而且漫无目的地让那些知道更好的人......无论如何,我我们最终手动锁定了我想要保持控制的节点:

Transaction tx = template.getGraphDatabase().beginTx();

try {
    tx.acquireWriteLock(lockNode);
    long id = idRepository.generateUid(TYPE_ORDER);
    tx.success();
    return id;
}
finally {
    tx.close();
}

generateUid与原始问题中的查询相同。当然,我负责访问和更新值,而不是将其推迟到数据库引擎,但我正在以这种方式生成唯一的增量ID。