2017-11-15 27 views
1

저는 Payara 4.1 (Eclipselink 2.6), Postgres 9.6을 사용하고 있습니다.Postgres와 함께 Payara/EclipseLink의 ScrollableCursor에 메모리 문제가 발생했습니다.

ScrollableCursor를 사용하여 매우 큰 데이터 결과에 액세스하려고합니다. 테스트에서 java.lang.OutOfMemoryError를 실행하여 쿼리를 실행하려고 했으므로 적은 양의 데이터로 힙 덤프 문제 해결을 수행하고 ArrayList 행의 org.postgresql.jdbc.PgResultSet에서 많은 메모리를 차지했습니다.

EclipseLink documentation을 기반으로하면 ScrollableCursor를 얻는 한 가지 힌트 만 제공하지만 Postgres JDBC 드라이버 소스 코드를 첨부하고 디버깅을 한 후에 가져온 크기를 설정하지 않으면 전체 데이터 결과를로드하는 것으로 나타났습니다. 그래서 QueryHints.JDBC_FETCH_SIZE를 추가했습니다. 그래도 나는 전체 쿼리 결과를 가져 와서 처음에 메모리에 저장한다는 것을 알게되었습니다.

org.postgresql.v3.QueryExecutorImpl sendOneQuery에서는 QUERY_FORWARD_CURSOR 플래그가 설정된 경우에만 초기 행을 반입 크기로 설정합니다.

org.postgresql.jdbc.PgStatement executeInternal 가져 오기 크기가 있고 결과 집합을 스크롤 할 수 없으며 연결이 autoCommit가 아니며 결과 집합을 보유 할 수없는 경우에만 QUERY_FORWARD_CURSOR flage를 설정합니다.

다른 @Stateless ejb의 다른 @TransactionAttribute (TransactionAttributeType.REQUIRES_NEW) 메소드에서 호출 된 @Stateless ejb에서 @TransactionAttribute (TransactionAttributeType.REQUIRED)에서 호출하더라도 내 연결을 디버깅 할 때 자동 커밋으로 설정됩니다.

내 쿼리가 자동 연결을 기본값으로 사용하는 새로운 연결을 사용하는 것으로 보이는 이유가 표시되지 않습니다. QueryHints.READ_ONLY를 사용하기 전에는 트랜잭션이 아니므로 새 연결을 사용하고있을 가능성이 있지만이를 제거했습니다. 내 다른 쿼리 힌트 중 하나가 새로운 연결을 야기하고 있습니까? 내가 사용해야 할 힌트가 있습니까? Payara/Eclipselink와 Postgres에 버그가있어서 ScrollableCursor를 사용할 때 실제로 스크롤되지 않습니다.

private static <T> void executeScrollableQuery(@NotNull final Query query, final Consumer<T> recordConsumer) { 
    query.setHint(QueryHints.RESULT_SET_TYPE, ResultSetType.ForwardOnly) 
      .setHint(QueryHints.SCROLLABLE_CURSOR, HintValues.TRUE) 
      .setHint(QueryHints.MAINTAIN_CACHE, HintValues.FALSE).setHint(QueryHints.JDBC_FETCH_SIZE, 500); 

    ScrollableCursor cursor = null; 
    try { 
     cursor = (ScrollableCursor) query.getSingleResult(); 
     while (cursor.hasNext()) { 
      recordConsumer.accept((T) cursor.next()); 
     } 
    } finally { 
     if (cursor != null) { 
      cursor.close(); 
     } 
    } 
} 

답변

0

내가 알 수있는 한, Postgres JDBC 드라이버의 버그입니다. 해결 방법으로 엔티티 관리자를 전달할 수 있었고 쿼리 결과를 얻기 전에 엔티티 관리자에서 연결을 해제 할 수 있었고 이로 인해 연결이 자동 커밋되지 않아 제공된 가져 오기 크기를 사용하고 전체 쿼리를 얻지 못했습니다. 결과를 메모리에 저장합니다.

이것이 작동하는 이유는 연결을 풀면 트랜잭션이 더러워지고 커서가 새로운 자동 연결 읽기 연결 대신 쓰기 연결을 사용하여 읽는 것입니다. 내가 의도 한대로 포스트 그레스 드라이버가 작동 할 수있다 생각하고 정말이 eclipselink.cursor.scrollable와 함께 사용하도록 자동 커밋 연결을 허용 있다는 payara /는 EclipseLink에 문제의 this documentation에 따라

private static <T> void executeScrollableQuery(@NotNull final EntityManager em, @NotNull final Query query, 
     final Consumer<T> recordConsumer) { 
    query.setHint(QueryHints.RESULT_SET_TYPE, ResultSetType.ForwardOnly) 
      .setHint(QueryHints.SCROLLABLE_CURSOR, HintValues.TRUE) 
      .setHint(QueryHints.MAINTAIN_CACHE, HintValues.FALSE).setHint(QueryHints.JDBC_FETCH_SIZE, 500); 

    em.unwrap(Connection.class); 

    ScrollableCursor cursor = null; 
    try { 
     cursor = (ScrollableCursor) query.getSingleResult(); 
     while (cursor.hasNext()) { 
      recordConsumer.accept((T) cursor.next()); 
     } 
    } finally { 
     if (cursor != null) { 
      cursor.close(); 
     } 
    } 
} 

좋아, 힌트.