Я не уверен, что это было частью вашего вопроса, но я все же поясню: FullTextEntityManager
можно использовать в двух отдельных потоках, если вы используете другой менеджер сущностей.И если вы используете Spring, очень вероятно, что вы используете.Так что все в порядке.
Основная проблема, которую я вижу в вашей настройке, заключается в том, что потенциально два метода могут выполняться одновременно (если первый поисковый запрос отправляется до или во время первой запланированной индексации).Но в этом случае вы предпочтете получить дубликаты документов в своем индексе, а не отсутствующие документы (из-за способа работы массового индексатора).Так что я действительно не знаю, что происходит.
Я бы посоветовал держаться подальше от ленивого выполнения массового индексирования в методе запроса и, что более важно, чтобы избежать ожидания потенциально длительной операции (массовое индексирование)в потоках запросов: это серьезный анти-шаблон.
В идеале вам следует массово индексировать только при повторном развертывании приложения (когда клиент не использует приложение) и повторно использовать индекс послезапустить снова.Таким образом, вам никогда не придется заставлять запросы ждать массовой индексации: к тому времени, когда кто-нибудь получит доступ к приложению, все уже будет проиндексировано.
Но вы этого не делали, поэтому я предполагаю, что у вас естьпричины.Если вы действительно хотите переиндексировать все при запуске и заблокировать поисковые запросы, пока массовое индексирование не закончено, то что-то вроде ниже должно быть безопаснее.Может быть, не безупречный (на самом деле это зависит от вашей модели: я не знаю, могут ли быть обновлены значения аудита), но безопаснее.
1) При выполнении поиска целевых данных из пользовательского интерфейса блокируйте запрос доначальное индексирование закончено [еще раз, это плохая идея, но каждому свое].
// Assuming the background job class is named "IndexInitializer"
@Autowired
IndexInitializer indexInitializer;
...
public FullTextQuery buildTransactionSearchQuery(SearchRequestDTO request) {
final FullTextEntityManager fullTextEntityManager = getFullTextEntityManager();
final Query expression = buildTransactionSearchExpression(request.getFilter(), fullTextEntityManager);
final FullTextQuery query = fullTextEntityManager.createFullTextQuery(expression, Transaction.class);
return query;
}
...
private FullTextEntityManager getFullTextEntityManager() {
indexInitializer.awaitInitialIndexing();
return Search.getFullTextEntityManager(entityManager);
}
2) В фоновом задании используйте индексатор массы на первом тике и добавочную индексациюна каждом последующем отметке:
private final CountDownLatch initialIndexingsRemaining = new CountDownLatch(1);
public void awaitInitialIndexing() {
initialIndexingsRemaining.await();
}
@Scheduled(initialDelay = 0, fixedDelay = 5_000)
private void indexAuditValues() {
if (isInitialIndexingDone()) {
doIncrementalIndexing();
} else {
doInitialIndexing();
}
}
private boolean isInitialIndexingDone() {
return initialIndexingsRemaining.await(0, TimeUnit.NANOSECONDS);
}
private void doInitialIndexing() {
// Synchronization is only necessary here if the scheduled method may be called again before the previous execution is over. Not sure it's possible?
synchronized (this) {
if (isInitialIndexingDone()) {
return;
}
try {
fullTextEntityManager.createIndexer().startAndWait();
initialIndexingsRemaining.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private void doIncrementalIndexing() {
Instant previousRunTime = ...; // assume data is set
Instant currentTime = ...;
int page = 0;
boolean hasMore = true;
while (hasMore) {
hasMore = hsIndexingService.indexAuditValues(previousRunTime, currentTime, page++);
}
}
@Transactional(readOnly = true)
public boolean indexAuditValues(Instant previousRunTime, Instant currentTime, int page) {
PageRequest pageRequest = return new PageRequest(page, batchSize, Sort.Direction.ASC, AUDIT_VALUE_SORT_COLUMN);
Page<AuditValue> pageResults = auditValueRepository.findByAuditTransactionLastModifiedDateBetween(previousRunTime, currentTime, pageRequest);
FullTextEntityManager fullTextEntityManager = getFullTextEntityManager();
List<AuditValue> content = pageResults.getContent();
content.forEach(fullTextEntityManager::index); // here we do index the data
return pageResults.hasNext();
}
private FullTextEntityManager getFullTextEntityManager() {
return Search.getFullTextEntityManager(entityManager);
}
В дополнение к этому вы также можете заменить ручную периодическую индексацию на автоматическую индексацию на лету: Hibernate Search автоматически обновит индекс при сохранении сущностей /обновлен / удален в Hibernate ORM.