前几天我遇到了这个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)
我有其他生成此值的机制,但这看起来非常简洁,我做错了什么?
答案 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。