实体节点未在事务内更新

Entity node not updated inside transaction

在外部服务器设置中使用 SDN 3.3.1 和 Neo4j 2.1.7。

每当我执行以下执行模式时,我都会遇到 java.net.SocketTimeoutException: Read timed out

  1. 检索实体:MyEntity entity = myRepository.findByUuid(uuid);
  2. 删除该实体的关系:neo4jOperations.query("match (e{uuid:"theuuid"})<-[r:THE_RELATION]-() delete r);
  3. 更改实体的 属性 : entity.setTitle("New title");
  4. 保存实体 : myRepository.save(entity); --> 等了很久才得到 SocketTimeoutException

经过一些分析,在我看来 1.AND 内部执行 http 调用,正如您在此处看到的那样Neo4j http 日志提取:

127.0.0.1 - - [20/août/2015:10:47:57 +0200] "POST /db/data/transaction HTTP/1.1" 201 659 "-" "neo4j-rest-graphdb/0" 
127.0.0.1 - - [20/août/2015:10:47:57 +0200] "GET /db/data/node/135/relationships/in/WITH_ROLE HTTP/1.1" 200 391 "-" "neo4j-rest-graphdb/0" 
127.0.0.1 - - [20/août/2015:10:47:57 +0200] "GET /db/data/node/149 HTTP/1.1" 200 1780 "-" "neo4j-rest-graphdb/0" 
127.0.0.1 - - [20/août/2015:10:47:57 +0200] "GET /db/data/node/135/relationships/out/DEFAULT_ENTITY HTTP/1.1" 200 401 "-" "neo4j-rest-graphdb/0" 
127.0.0.1 - - [20/août/2015:10:47:57 +0200] "GET /db/data/node/227 HTTP/1.1" 200 1884 "-" "neo4j-rest-graphdb/0" 
127.0.0.1 - - [20/août/2015:10:47:57 +0200] "GET /db/data/node/135/relationships/in/WITH_PENDING_ROLE HTTP/1.1" 200 2 "-" "neo4j-rest-graphdb/0"

所以我的第一个问题是:

那么,2.中的删除操作在事务内部正确完成:

127.0.0.1 - - [20/août/2015:10:47:58 +0200] "POST /db/data/transaction/2720 HTTP/1.1" 200 254 "-" "neo4j-rest-graphdb/0" 

3.显然没有生成http请求。

最后,点 4. 在 "blocked" 一段时间(准确地说是 30 秒)后生成 SocketTimeoutException,最后生成 2 个 http 请求:

127.0.0.1 - - [20/août/2015:10:48:28 +0200] "DELETE /db/data/transaction/2720 HTTP/1.1" 200 26 "-" "neo4j-rest-graphdb/0"
127.0.0.1 - - [20/août/2015:10:48:28 +0200] "PUT /db/data/node/135/properties HTTP/1.1" 204 0 "-" "neo4j-rest-graphdb/0"

delete是因为事务被回滚了,我觉得PUT对应的是save()

在我看来,由于 PUT 在事务之外,所以发生了死锁! 所以,主要问题是:

谢谢:)

EDIT : Added SDN config

<beans ...>
  <tx:annotation-driven />
  <context:component-scan base-package="com.myproject.mw.neo4j.service,com.myproject.mw.neo4j.aspect" />
  <aop:aspectj-autoproxy />
  <!-- External database config -->
  <bean id="graphDatabaseServiceExternal" class="org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase"
    lazy-init="true">
      <constructor-arg index="0" value="${neo4j.path}" />
  </bean>

  <alias name="graphDatabaseService${neo4j.mode}" alias="graphDatabaseService" />
  <neo4j:config base-package="com.myproject.mw.neo4j.domain"
    graphDatabaseService="graphDatabaseService" />
  <neo4j:repositories base-package="com.myproject.mw.neo4j.repository" />
</beans>

EDIT 2 : Added entities definition and simple test to reproduce

请注意,我的 Neo4j 服务器在本地主机上。

测试:

@Test
@Transactional
@Rollback(false)
public void testEntityNotFound() {
    BusinessCard card = genericRepository.findByUuid("4bce6162-bc6f-bce2-bc71-bc70b63dff00");
    Entity entity = genericRepository.findByUuid("04947df8-0e9e-4471-a2f9-9af509fb5889");
    neo4jOperations.createRelationshipBetween(neo4jOperations.getNode(entity.getId()),
            neo4jOperations.getNode(card.getId()), "FAKE2", null);
    neo4jOperations.save(card);
}

名片实体:

public class BusinessCard extends GenericNode {
    @Indexed
    private String title;
    @RelatedTo(type = RelationNames.WITH_ROLE, direction = Direction.INCOMING)
    private User user;
    @RelatedTo(type = RelationNames.WITH_PENDING_ROLE, direction = Direction.INCOMING)
    private User pendingUser;
    @RelatedTo(type = RelationNames.DEFAULT_ENTITY)
    private Entity defaultEntity;
}

和Entity实体:

public class Entity extends GenericNode {
    private String logo;
    @Indexed
    private String entityName;
    @RelatedTo(type = RelationNames.IS_IN_CITY)
    private CityNode mainCity;
    @RelatedTo(type = RelationNames.IS_IN_COUNTRY)
    private CountryNode mainCountry;
    @RelatedTo(type = RelationNames.IS_IN_COUNTRIES)
    private Set<CountryNode> countries;
    @Fetch
    @RelatedToVia(type = RelationNames.TRANSLATION)
    private Set<EntityToEntityTranslatedContent> entityTranslatedContent;
    @Fetch
    @RelatedTo(type = RelationNames.HAS_KEY_STAGES)
    private KeyStage lastKeyStage;
    @GraphProperty(propertyType = Long.class)
    private Date lastProfileUpdateDateTime;
    private Integer profilCompletionIndice;
    @RelatedToVia(type = RelationNames.WORKS_AT, direction = Direction.INCOMING)
    private Set<BusinessCardWorkAt> employees = new HashSet<>();
...+34 more fields...
}

我假设您正在使用 org.springframework.data.neo4j.rest.SpringRestGraphDatabase,它现在已被弃用,并且不正确支持事务。 (参见 this 生成 PUT 的代码)

有一个新的基于密码的数据库 org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase,它可以满足您的需求。

您还想查看 4.x 版本,这是对 SDN 的彻底改革,专注于以更有效的方式远程使用 neo4j 而不是 rest。它应该很快就会正式发布。

此博客提供了更多解释 http://jexp.de/blog/2014/12/spring-data-neo4j-improving-remoting-performance/

那么你的实体出了点问题,它似乎花了很多时间向服务器写入数据。来自您的线程转储:

at org.neo4j.rest.graphdb.ExecutingRestRequest.put(ExecutingRestRequest.java:155)
at org.neo4j.rest.graphdb.RestAPIImpl.setPropertiesOnEntity(RestAPIImpl.java:633)
at org.neo4j.rest.graphdb.entity.RestEntity.flush(RestEntity.java:189)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesTo(SourceStateTransmitter.java:109)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:179)

对于转储 2:

at org.neo4j.rest.graphdb.ExecutingRestRequest.put(ExecutingRestRequest.java:155)
at org.neo4j.rest.graphdb.RestAPIImpl.setPropertiesOnEntity(RestAPIImpl.java:633)
at org.neo4j.rest.graphdb.entity.RestEntity.flush(RestEntity.java:189)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesTo(SourceStateTransmitter.java:109)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:179)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:247)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:235)
at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:365)

肯定还有别的原因,因为

org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase

应该使用带有 GET、PUT、DELETE 等的普通 Rest 端点,而只是简单的 Cypher。

您还应该检查您的服务器的延迟和带宽,SDN3 仍然很繁琐。

所以你应该分享你的实体的代码,你到底做了什么before/when你调用了那个保存以及有多少数据被发送到服务器。

为了获得更好的 SDN + 服务器体验,我建议查看 SDN4,它是为此目的从头开始编写的。 SDN4 RC2 上周五发布:

http://docs.spring.io/spring-data/neo4j/docs/4.0.0.RC2/reference/html/

这已在 SDN 3.4 中修复(参见 https://jira.spring.io/browse/DATAGRAPH-699)。 此 jira 票证内容是一个不同的主题,但您可以依赖修改后的标题 "Implement missing operations for Cypher based REST-API separately"。 从 3.4 开始,使用@Transactional 时不再有事务 REST 请求。