Hibernate-Search flushToIndexes, вызывающий java.lang.OutOfMemoryError (пространство кучи) - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть приложение Spring, использующее Hibernate и подключенное к Elasticsearch через Hibernate-Search.
Чтобы упростить пример, я добавлю только необходимые аннотации и код.

У меня естьсущность A , содержащаяся в нескольких B сущностях (много, на самом деле ~ 8000).
B сущности также содержит много встроенных деталей (сущностей) C , E , ...).
Все эти объекты связаны с @ IndexedEmbedded и @ ContainedIn Hibernate-Searchаннотации (см. пример ниже).
Я создал службу, изменив поле объекта A и принудительно сбросив flushToIndexes .

При сбросе Hibernate-Search обновляет индекс A , а из-за @ ContainedIn распространяется по индексам 8000 B .Но для обновления индексов B , по какой-то причине, Hibernate-Search загружает каждый 8000 B объектов, связанных с объектом A одновременно, а также все детали, содержащиеся в нем.в B объектах ( C , E и т. д.).
Все это занимает много времени и заканчивается не более чем java.lang.OutOfMemoryError: Пространство кучи Java .


@Entity
@Table(name = "A")
@Indexed
public class A {

    @ContainedIn 
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "a") 
    private Set<B> bCollection;

    @Field
    @Column(name = "SOME_FIELD")
    private String someField;                            // Value updated in the service
}

@Entity
@Table(name = "B")
@Indexed
public class B {

    @IndexedEmbedded
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "A_ID")
    private A a;

    @IndexedEmbedded
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "b")
    @Fetch(FetchMode.JOIN)  
    private C c;                                         // Some other details

    @IndexedEmbedded
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "b")
    private Set<E> eCollection;                          // Some other details
}

// My service
aObject.setSomeField("some value");
fullTextSession.flushToIndexes();

Увеличение выделенной памяти JVM (с 8 ГБ до 24ГБ, что на самом деле много для ~ 10000 объектов) ничего не решило.Поэтому я предполагаю, что загрузка всего набора данных требует более 24 ГБ ...

Однако проблема кажется более сложной, чем выглядит ~
Это ошибка?Это распространено?Что я сделал не так ?Как я могу решить это?
Есть ли какая-то скрытая конфигурация Hibernate-Search, чтобы избежать такого поведения?

1 Ответ

0 голосов
/ 12 декабря 2018

Это ограничение поиска в спящем режиме.@ContainedIn будет относительно хорошо работать только для небольших ассоциаций;большие, такие как ваша, действительно вызовут загрузку всех связанных сущностей и будут работать плохо, или в худших случаях вызовут OOM.

Это еще не было исправлено, потому что проблема довольно сложная.Нам нужно использовать запросы вместо ассоциаций для @ContainedIn ( HSEARCH-1937 ), что было бы довольно просто.Но, что более важно, нам необходимо выполнить разбиение на части (периодическое очищение / очистка), которое будет иметь побочный эффект на пользовательский сеанс или будет выполнено вне пользовательской транзакции ( HSEARCH-2364 ), оба из которыхможет иметь неприятные последствия.

Обходной путь должен был бы к , а не добавить @ContainedIn к A.bCollection и обрабатывать переиндексацию вручную: https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#manual-index-changes

Аналогичното, что я упомянул в другом ответе , вы можете выбрать одну из двух стратегий:

  1. Простой путь: периодически переиндексировать все B сущности, используя массовый индексатор, например каждую ночь.
  2. Жесткий путь: всякий раз, когда изменяется A, сохраняйте информацию «эта сущность изменилась» где-то (это может быть так же просто, как сохранение «даты / времени последнего обновления» для сущности A или добавлениестрока в таблице событий).Параллельно попросите периодический процесс проверить изменения, загрузить затронутые объекты типа B и переиндексировать их.Желательно делать это партиями регулируемого размера, по одной транзакции на пакет, если вы можете (это позволит избежать некоторых головных болей).

Первое решение довольно простое, но имеет большой недостаток, что PersonИндекс будет до 24 часов устаревшим.В зависимости от вашего варианта использования, это может быть нормально, а может и нет.Это также может оказаться невозможным, если у вас много сущностей типа B (читай: миллионы), а полная переиндексация занимает больше, чем несколько минут.

Второе решение подвержено ошибкам, и вы в основном будете использовать HibernateПоиск работает, но он будет работать даже для очень больших таблиц, и задержка между изменением базы данных и переиндексацией будет намного короче.

...