NPE при сборе из параллельного потока - PullRequest
2 голосов
/ 25 октября 2019

У меня есть список из примерно 40 000 объектов в среднем (максимально до 80 КБ), завернутый весной Page

Page<People> people = //loaded data using PeopleRepository of "People" entity

Затем создается карта для экспорта записей в файл Excel

List<Map<String, String>> excelData = Streams.batches(people, 500)
            .parallel()
            .map(PageImple::new) //Converting each chunk in a page
            .map(this::buildDTO) // Creating DTO objects from people entities
            .map(Slice::getContent)
            .flatMap(Collection::stream)
            .map(this::buildExportData)
            .collect(                    <----
                Collectors.toList()
             );

Над фрагментом кода время от времени выкидывается следующий NPE. Это выброшено из основной библиотеки Java. Кто-нибудь может дать намек, что может быть причиной проблемы? Это связано с параллельным потоком? Нужен ли какой-либо другой механизм для параллельной потоковой передачи?

Исключение

    java.lang.NullPointerException: null
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(DelegatingAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)
        at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
        at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735)
        at java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:714)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)

В другом, но похожем сценарии я также наблюдал следующее исключение:

    org.hiberate.AssertionFailure: bug adding collection twice
        at org.hibernate.engine.internal.StatefulPersistenceContext.addCollection(StatefulPersistenceContext.java:851)
        at org.hibernate.engine.internal.StatefulPersistenceContext.addInitializedConnection(StatefulPersistenceContext.java:890)
         // -- Will add remaiing part of the stack trace if required.

Edit 2 (последние новости!) Мы обнаружили такое же поведение в двух местах. У них обоих есть одна общая линия, загружающая отношения от использования Ленивой выборки. Подтверждение сущности выглядит следующим образом:

@ManyToMany(fetch=Fetch.Lazy)
@JoinTable(.. ... ...)
private Set<Classification> classifications

Что-то идет не так, если используется параллельный поток с отложенной выборкой?

Редактировать 1 После еще одного расследования, если я посмотрюна java.util.concurrent.ForkJointTask line#291 код выглядит следующим образом:

try {
    completed = exec();
} catch(Throwable rex) {
    return setExceptionalCompletion(rex);
}

Похоже, это означает, что он не смог выполнить один из блоков карты. Поэтому, как @Eugene спросил в комментарии, является ли это единственной трассировкой стека, ответ - нет. Существует раздел «Вызвано» . Вот оно,

    Caused By: java.lang.NullPointerException: null
        at org.hibernate.engine.loading.internal.LoadContexts.cleanup(LoadContexts.java:81)
        at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:202)
        at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154)
        at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:249)
        at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:212)
        at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:123)
        at org.hibernate.loader.plan.exec.process.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:122)
        at org.hibernate.loader.plan.exec.process.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86)
        ...
        at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2004)
        ...
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
...