Проблемы с памятью у ScrollableCursor в Payara/EclipseLink с Postgres

Я использую Payara 4.1 (Eclipselink 2.6), Postgres 9.6.

Я пытаюсь использовать ScrollableCursor для доступа к результатам данных, которые могут быть довольно большими. При тестировании мы получили ошибку java.lang.OutOfMemoryError при попытке выполнить запрос, поэтому я выполнил некоторые действия по устранению неполадок с дампом кучи с меньшим объемом данных и обнаружил, что org.postgresql.jdbc.PgResultSet занимает много памяти в строках ArrayList.

Основываясь на документации EclipseLink, вы просто предоставляете один подсказка для получения ScrollableCursor, но после присоединения исходного кода драйвера JDBC Postgres и отладки я обнаружил, что он будет загружать весь результат данных, если мы не установим размер выборки, поэтому я добавил QueryHints.JDBC_FETCH_SIZE. Однако я обнаружил, что он по-прежнему извлекает все результаты запроса и изначально сохраняет их в памяти.

В org.postgresql.v3.QueryExecutorImpl sendOneQuery устанавливает для начальных строк размер выборки только в том случае, если установлен флаг QUERY_FORWARD_CURSOR.

В org.postgresql.jdbc.PgStatement executeInternal он устанавливает флаг QUERY_FORWARD_CURSOR только в том случае, если есть размер выборки, набор результатов нельзя прокручивать, соединение не является автоматическим и набор результатов нельзя удерживать.

При отладке мое соединение настроено на автоматическую фиксацию, хотя я звоню из @TransactionAttribute(TransactionAttributeType.REQUIRED) в @Stateless ejb, вызываемом из другого метода @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) в другом @Stateless ejb.

Я не понимаю, почему в моем запросе используется новое соединение, по умолчанию настроенное на автоматическую фиксацию. Раньше я использовал 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();
        }
    }
}

person Tim    schedule 15.11.2017    source источник


Ответы (1)


Насколько я могу судить, это ошибка драйвера Postgres JDBC. В качестве обходного пути я также смог передать диспетчер сущностей и развернуть соединение в диспетчере сущностей перед получением результата запроса, и это сделало соединение не автоматическим, так что оно использовало предоставленный размер выборки и не получило весь запрос результат в памяти.

Я думаю, причина, по которой это работает, заключается в том, что распаковка соединения делает транзакцию грязной, а затем курсор считывается с использованием соединения для записи вместо нового соединения для чтения с автоматической фиксацией.

    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();
        }
    }
}

Хорошо, на основании этой документации я предполагаю, что драйвер postgres может работать по назначению, и это действительно проблема с payara/eclipselink, позволяющая использовать соединение с автоматической фиксацией с подсказкой eclipselink.cursor.scrollable.

person Tim    schedule 17.11.2017