Hibernate
February 4, 2022

Hibernate Search: отключение проверки доступности кластера Elasticsearch при запуске приложения

Используемые версии библиотек:

  • Hibernate Search 6.0.2.Final
  • Spring Boot 2.3.7.RELEASE

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

Для работы с ним мы используем Hibernate Search и используем лишь малую его часть — нам просто необходимо строить индекс, а затем делать к нему обращения во время набора текста в определённых полях на UI. То есть своеобразная подстановка значений по типу "search-as-you-type". Связи индекса с вводимыми пользователем где-то в приложении значениями нет, то есть можно грубо сказать что он живёт отдельной жизнью.

Итак, если попытаться запустить приложение без необходимых модификаций и с недоступным Elasticsearch, можно увидеть примерно следующее сообщение об ошибке:

HSEARCH400080: Unable to detect the Elasticsearch version running on the cluster: HSEARCH400007: Elasticsearch request failed: The requested name is valid, but no data of the requested type was found (eslb.*.ru)

Первым делом появляется вариант отключения этой проверки версии на старте. И возможно появляется вариант отключения всей интеграции с Elasticsearch, а в случае когда он будет доступен — инициализации. Так вот это вообще не вариант. Да, действительно, отключить интеграцию с Hibernate Search можно через задание параметра hibernate.search.enabled = false, но отключив его таким образом на этапе запуска приложения, потом, во время жизненного цикла, Search уже так просто назад не включится.

Этот вариант нам совсем не подходит.

Переходим к документации и находим раздел "11.4.8 Version" в котором говорится, что для того, чтобы выключить проверку версии при запуске, необходимо задать 2 параметра:

  • hibernate.search.backend.version_check.enabled = false
  • hibernate.search.backend.version (достаточно первых 2 цифр)

Добавляем в наш application.yml:

spring:
  jpa:
    properties:
      hibernate:
        search:
          backend:
            version: 7.8
            version_check:
              enabled: false

Пробуем выполнить запуск, снова ошибка:

HSEARCH400034: Unable to retrieve index metadata from Elasticsearch: HSEARCH400007: Elasticsearch request failed: The requested name is valid, but no data of the requested type was found (eslb.dev.ppod.cbr.ru)

На этот раз связана она с тем, что при инициализации Hibernate Search пытается узнать что делать с индексами и их схемами. Для того, чтобы изменить это поведение, необходимо установить параметр hibernate.search.schema_management.strategy:

spring:
  jpa:
    properties:
      hibernate:
        search:
          schema_management:
            strategy: none

После таких манипуляций приложение будет запускаться и не падать вне зависимости от доступности кластера Elasticsearch, однако индексация и управление её схемой работать не будет.

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

  1. Выполнить индексацию руками
  2. Попросить Hibernate Search запустить индексацию, когда Elasticsearch станет доступен

Пойдя по второму пути развития событий можно привести в пример следующий код:

log.info("Начало индексации");
SearchSession searchSession = Search.session(entityManager);
SearchSchemaManager schemaManager = searchSession.schemaManager();
schemaManager.createOrValidate();
searchSession.massIndexer(HistoryRecords.class)
        .batchSizeToLoadObjects(BATCH_SIZE)
        .startAndWait();
log.info("Индексация закончена успешно");

Здесь мы задали SearchSchemaManager и установили для него необходимую нам стратегию работы с индексами (и их схемами) -- create or validate. Стоит помнить, что если в случае вызова данного кода кластер Elasticsearch недоступен, приложение выдаст закономерную ошибку.

Итак, подводя итог вышесказанному можно сказать следующее.

Для того, чтобы модуль, который использует Hibernate Search не падал при запуске в случае недоступности кластера Elasticsearch, необходимо:

Решение очень простое, но для более сложных use-case не подойдёт (в случае когда изменения в БД предполагают последующие обновления в индексе). Для того, чтобы обрабатывать и такие ситуации, необходимо обратиться к outbox-polling coordination strategy. Она позволит сохранять события изменений entity в БД и асинхронно обрабатывать их в фоновом потоке. Также для работы этого функционала необходимо сообщить Hibernate Search повторить неудавшиеся события как только Elasticsearch станет доступен. Outbox polling coordination strategy — неплохой вариант, но у него есть свои плюсы и минусы с которыми можно ознакомиться здесь.

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

Надеюсь, было полезно!