Верно ли, что в нашем приложении мы используем как MassIndexer, так и ручную индексацию из Hibernate Search? - PullRequest
0 голосов
/ 24 января 2019

Недавно я присоединился к проекту, использующему Hibernate Search.

Я подозреваю, что в нашем приложении есть сбой, который вызывает игнорирование вновь проиндексированных данных другим фоновым заданием из-за использования FullTextEntityManager в 2 местах:

1) При выполнении поиска целевых данныхиз пользовательского интерфейса мы используем MassIndexer для индексации данных при первом поисковом запросе, и все последующие поисковые запросы не будут вызывать переиндексацию:

private final AtomicBoolean initialized = new AtomicBoolean(false);
...
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() {
    final FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);

    if (initialized.get()) {
        return fullTextEntityManager;
    } else {
        synchronized (initialized) {
            if (!initialized.getAndSet(true)) {
                try {
                    fullTextEntityManager.createIndexer().startAndWait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            return fullTextEntityManager;
        }
    }
}

2) В фоновом задании:

@Scheduled(initialDelay = 1_000, fixedDelay = 5_000)
private void indexAuditValues() {
    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);
}

Недавно наши пользователи сообщили, что новые данные не появляются на странице поиска, возможно ли это из-за использования 2 FullTextEntityManager s в 2 отдельных потоках, которые не синхронизированы?Если да, то как это можно решить?

Мы используем файл Spring boot, Hibernate Search, Lucene и храним индексы в файловой системе.Объекты помечены @Indexed, а доступные для поиска поля помечены @Field.

1 Ответ

0 голосов
/ 25 января 2019

Я не уверен, что это было частью вашего вопроса, но я все же поясню: 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...