Spring Data Neo4j:在没有DeadlockDetectedException

时间:2016-03-12 20:21:43

标签: java concurrency neo4j deadlock spring-data-neo4j

在我的项目中,我使用Spring Data Neo4j(3.4.2.RELEASE)和基于AspectJ的"高级映射"模式使用嵌入式数据库。 我还通过@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

使用基于AspectJ的事务管理

在我的应用程序中,如果发生对数据库的并发读/写操作,我遇到了死锁。我没有收到DeadlockDetectedException但是看到一个线程无限期地等待RWLock。在此之后不久,所有剩余的线程将在第一个线程上等待,并且应用程序停止,它不会从中恢复。其他线程也可能已经获得锁定,但我无法看到我不确定它。

锁定线程的堆栈跟踪:

Thread [Processing-XXX] (Suspended) 
owns: SeasonOfCompetitionRepositoryImpl  (id=134)   
waiting for: RWLock  (id=174)   
Object.wait(long) line: not available [native method]   
RWLock(Object).wait() line: 502 [local variables unavailable]   
RWLock.acquireWriteLock(Object) line: 388   
LockManagerImpl.getWriteLock(Object, Object) line: 66   
CommunityLockClient.acquireExclusive(Locks$ResourceType, long) line: 116    
LockingStatementOperations.relationshipCreate(KernelStatement, int, long, long) line: 287   
OperationsFacade.relationshipCreate(int, long, long) line: 866  
NodeProxy.createRelationshipTo(Node, RelationshipType) line: 559    
DelegatingGraphDatabase.getOrCreateRelationship(Node, Node, RelationshipType, Direction, Map<String,Object>) line: 298  
Neo4jTemplate.getOrCreateRelationship(Node, Node, RelationshipType, Direction, Map<String,Object>) line: 772    
RelationshipHelper.createSingleRelationship(Node, Node) line: 198   
RelationshipHelper.createAddedRelationships(Node, Set<Node>) line: 154  
RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor(RelatedToFieldAccessor).createAddedRelationships(Node, Set<Node>) line: 78 
RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor.setValue(Object, Object, MappingPolicy) line: 68   
NodeEntityState(DefaultEntityState<STATE>).setValue(Neo4jPersistentProperty, Object, MappingPolicy) line: 113   
DetachedEntityState<STATE>.setValue(Neo4jPersistentProperty, Object, MappingPolicy) line: 181   
DetachedEntityState<STATE>.setValue(Field, Object, MappingPolicy) line: 145 
SeasonOfCompetition.season_aroundBody21$advice(SeasonOfCompetition, SeasonOfCompetition, Season, JoinPoint, Neo4jNodeBacking, NodeBacked, Object, AroundClosure, JoinPoint) line: 266   
SeasonOfCompetition.setSeason_aroundBody22(SeasonOfCompetition, Season) line: 108   
SeasonOfCompetition$AjcClosure23.run(Object[]) line: 1  
AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(Object, AroundClosure) line: 66    
AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation() line: 72  
AnnotationTransactionAspect(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 281   
AnnotationTransactionAspect(AbstractTransactionAspect).ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(Object, AroundClosure, JoinPoint$StaticPart) line: 70    
SeasonOfCompetition.setSeason(Season) line: 95  
SeasonOfCompetitionRepositoryImpl.getOrCreate_aroundBody0(SeasonOfCompetitionRepositoryImpl, Season, Competition) line: 32  
SeasonOfCompetitionRepositoryImpl$AjcClosure1.run(Object[]) line: 1 
AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(Object, AroundClosure) line: 66    
AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation() line: 72  
AnnotationTransactionAspect(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 281   
AnnotationTransactionAspect(AbstractTransactionAspect).ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(Object, AroundClosure, JoinPoint$StaticPart) line: 70    
SeasonOfCompetitionRepositoryImpl.getOrCreate(Season, Competition) line: 26 

这是自定义RepositoryExtension的一部分,它应该为给定的参数创建唯一的SeasonOfCompetition

@Override
@Transactional
public synchronized SeasonOfCompetition getOrCreate(Season season, Competition competition)
{
    String uri = buildUri(season, competition);
    SeasonOfCompetition soc = getOrCreateByUri(uri);
    if(soc.getSeason() == null)
    {
        soc.setSeason(season);
    }
    if(soc.getCompetition() == null)
    {
        soc.setCompetition(competition);
    }
    return soc;
}

请注意,我已经使用@Transactionalsychronized玩了一下 - 我最初都没有使用它们,因为我认为(但不确定)他们这里不需要。

这是线程卡住的域实体SeasonOfCompetition的选择:

@NodeEntity
public class SeasonOfCompetition extends UriEntity
{
    @RelatedTo(type = "inSeason", direction = Direction.OUTGOING)
    private Season season;

    @Transactional
    public void setSeason(Season season)
    {
        this.season = season;
    }
}

我在这里做错了什么?我假设通过SDN访问图形数据库是线程安全的,并且除了使用事务之外我不需要任何特殊处理。 关于如何调试此建议或如何使其在此情况下抛出DeadlockDetectedException的建议也是受欢迎的。

我以前也有其他与并发相关的问题(重复的实体是唯一的,重复的关系等),并没有完全了解我还需要处理这个问题。关于如何为NodeEntities和Relationships实现线程安全的#getOrCreate的输入值得赞赏。

编辑: 我已尝试实施基于MERGE - 查询的#getOrCreate版本,该版本未使用@cybersam建议的任何自定义同步:

@Override
@Transactional
@SuppressWarnings("deprecation")
public T getOrCreateByUri(String uri)
{
    checkArgument(!StringUtils.isEmpty(uri));

    Class<T> clazz = getEntityType();
    String queryString = "MERGE (n:" + clazz.getSimpleName() + " {uri: {uri}}) RETURN n";
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("uri", uri);
    Node node = (Node) template.getGraphDatabase().queryEngine().query(queryString, parameters).single().get( "n" );
    template.postEntityCreation(node, clazz);
    T result = (T) template.convert(node, clazz);
    return result.persist();
}

然而,这无法创建唯一的节点。创建100个线程并并行调用它的简单测试失败,因为创建了多个节点。 这可能与result.persist()有关,但如果没有,则返回的实体不会附加到图表中,并且缺少某些信息。

1 个答案:

答案 0 :(得分:1)

neo4j documentation包含此警告:

  

重要

     

使用除锁之外的其他同步导致的死锁   由Neo4j管理仍然可以发生。自从Neo4j中的所有操作   除非另有说明,否则A​​PI是线程安全的,不需要   外部同步。其他需要同步的代码   应该以一种从不执行任何Neo4j的方式进行同步   同步块中的操作。

执行Neo4j操作的

getOrCreate()目前为synchronized。因此,死锁可能实际上是由您自己的代码的Java同步引起的。您需要重新设计代码,以避免在执行Neo4j操作时使用Java同步。