-
Notifications
You must be signed in to change notification settings - Fork 243
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HSEARCH-2945 Use a default mass indexing monitor configurer
- Loading branch information
1 parent
fc702c6
commit 452f7ee
Showing
17 changed files
with
547 additions
and
79 deletions.
There are no files selected for viewing
236 changes: 236 additions & 0 deletions
236
...bernate/search/integrationtest/mapper/pojo/massindexing/MassIndexingDefaultMonitorIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.search.integrationtest.mapper.pojo.massindexing; | ||
|
||
import static org.assertj.core.api.Fail.fail; | ||
|
||
import java.lang.invoke.MethodHandles; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
import org.hibernate.search.engine.backend.work.execution.DocumentCommitStrategy; | ||
import org.hibernate.search.engine.backend.work.execution.DocumentRefreshStrategy; | ||
import org.hibernate.search.engine.cfg.EngineSettings; | ||
import org.hibernate.search.integrationtest.mapper.pojo.testsupport.loading.PersistenceTypeKey; | ||
import org.hibernate.search.integrationtest.mapper.pojo.testsupport.loading.StubEntityLoadingBinder; | ||
import org.hibernate.search.integrationtest.mapper.pojo.testsupport.loading.StubLoadingContext; | ||
import org.hibernate.search.mapper.pojo.loading.mapping.annotation.EntityLoadingBinderRef; | ||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId; | ||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; | ||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; | ||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity; | ||
import org.hibernate.search.mapper.pojo.standalone.mapping.SearchMapping; | ||
import org.hibernate.search.mapper.pojo.standalone.massindexing.MassIndexer; | ||
import org.hibernate.search.mapper.pojo.standalone.session.SearchSession; | ||
import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock; | ||
import org.hibernate.search.util.impl.integrationtest.mapper.pojo.standalone.StandalonePojoMappingSetupHelper; | ||
import org.hibernate.search.util.impl.test.extension.ExpectedLog4jLog; | ||
|
||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
import org.apache.logging.log4j.Level; | ||
|
||
class MassIndexingDefaultMonitorIT { | ||
|
||
private static final int COUNT = 100; | ||
@RegisterExtension | ||
public final BackendMock backendMock = BackendMock.create(); | ||
|
||
@RegisterExtension | ||
public final StandalonePojoMappingSetupHelper setupHelper = | ||
StandalonePojoMappingSetupHelper.withBackendMock( MethodHandles.lookup(), backendMock ); | ||
|
||
@RegisterExtension | ||
public ExpectedLog4jLog logged = ExpectedLog4jLog.create(); | ||
|
||
private final StubLoadingContext loadingContext = new StubLoadingContext(); | ||
|
||
private SearchMapping setup(String failureHandler) { | ||
backendMock.expectAnySchema( Book.NAME ); | ||
|
||
SearchMapping mapping = setupHelper.start() | ||
.expectCustomBeans() | ||
.withPropertyRadical( EngineSettings.BACKGROUND_FAILURE_HANDLER, failureHandler ) | ||
.setup( Book.class ); | ||
|
||
backendMock.verifyExpectationsMet(); | ||
|
||
initData(); | ||
|
||
return mapping; | ||
} | ||
|
||
@ValueSource(booleans = { true, false }) | ||
@ParameterizedTest | ||
void skipTotalCount(boolean doCounts) { | ||
SearchMapping mapping = setup( null ); | ||
|
||
logged.expectEvent( Level.INFO, "Mass indexing complete in ", ". Indexed 100/100 entities" ); | ||
if ( doCounts ) { | ||
logged.expectEvent( Level.INFO, "Mass indexing is going to index 100 entities" ).once(); | ||
} | ||
else { | ||
logged.expectEvent( Level.INFO, "Mass indexing is going to index 100 entities" ).never(); | ||
} | ||
|
||
try ( SearchSession searchSession = mapping.createSession() ) { | ||
MassIndexer indexer = searchSession.massIndexer() | ||
// Simulate passing information to connect to a DB, ... | ||
.context( StubLoadingContext.class, loadingContext ); | ||
|
||
CompletableFuture<?> indexingFuture = new CompletableFuture<>(); | ||
indexingFuture.completeExceptionally( new SimulatedFailure( "Indexing error" ) ); | ||
|
||
// add operations on indexes can follow any random order, | ||
// since they are executed by different threads | ||
BackendMock.DocumentWorkCallListContext expectWorks = backendMock.expectWorks( | ||
Book.NAME, DocumentCommitStrategy.NONE, DocumentRefreshStrategy.NONE | ||
); | ||
for ( int i = 0; i < COUNT; i++ ) { | ||
final String id = Integer.toString( i ); | ||
expectWorks | ||
.add( id, b -> b | ||
.field( "title", "TITLE_" + id ) | ||
.field( "author", "AUTHOR_" + id ) | ||
); | ||
} | ||
|
||
// purgeAtStart and mergeSegmentsAfterPurge are enabled by default, | ||
// so we expect 1 purge, 1 mergeSegments and 1 flush calls in this order: | ||
backendMock.expectIndexScaleWorks( Book.NAME, searchSession.tenantIdentifierValue() ) | ||
.purge() | ||
.mergeSegments() | ||
.flush() | ||
.refresh(); | ||
|
||
try { | ||
indexer.monitor( f -> f.countEntitiesToIndexOnStart( doCounts ) ) | ||
.startAndWait(); | ||
} | ||
catch (InterruptedException e) { | ||
fail( "Unexpected InterruptedException: " + e.getMessage() ); | ||
} | ||
} | ||
|
||
backendMock.verifyExpectationsMet(); | ||
} | ||
|
||
@ValueSource(booleans = { true, false }) | ||
@ParameterizedTest | ||
void totalCountAtStart(boolean doCounts) { | ||
SearchMapping mapping = setup( null ); | ||
|
||
logged.expectEvent( Level.INFO, "Mass indexing complete in ", ". Indexed 100/100 entities" ); | ||
logged.expectEvent( Level.INFO, "Mass indexing is going to index 100 entities" ).once(); | ||
if ( doCounts ) { | ||
logged.expectEvent( Level.INFO, | ||
"Mass indexing is going to index approx. 100 entities ([ Book ]). Actual number may change once the indexing starts." ) | ||
.once(); | ||
} | ||
else { | ||
logged.expectEvent( Level.INFO, | ||
"Mass indexing is going to index approx. 100 entities ([ Book ]). Actual number may change once the indexing starts." ) | ||
.never(); | ||
} | ||
|
||
try ( SearchSession searchSession = mapping.createSession() ) { | ||
MassIndexer indexer = searchSession.massIndexer() | ||
// Simulate passing information to connect to a DB, ... | ||
.context( StubLoadingContext.class, loadingContext ); | ||
|
||
CompletableFuture<?> indexingFuture = new CompletableFuture<>(); | ||
indexingFuture.completeExceptionally( new SimulatedFailure( "Indexing error" ) ); | ||
|
||
// add operations on indexes can follow any random order, | ||
// since they are executed by different threads | ||
BackendMock.DocumentWorkCallListContext expectWorks = backendMock.expectWorks( | ||
Book.NAME, DocumentCommitStrategy.NONE, DocumentRefreshStrategy.NONE | ||
); | ||
for ( int i = 0; i < COUNT; i++ ) { | ||
final String id = Integer.toString( i ); | ||
expectWorks | ||
.add( id, b -> b | ||
.field( "title", "TITLE_" + id ) | ||
.field( "author", "AUTHOR_" + id ) | ||
); | ||
} | ||
|
||
// purgeAtStart and mergeSegmentsAfterPurge are enabled by default, | ||
// so we expect 1 purge, 1 mergeSegments and 1 flush calls in this order: | ||
backendMock.expectIndexScaleWorks( Book.NAME, searchSession.tenantIdentifierValue() ) | ||
.purge() | ||
.mergeSegments() | ||
.flush() | ||
.refresh(); | ||
|
||
try { | ||
indexer.monitor( f -> f.countAllEntitiesBeforeStart( doCounts ) ) | ||
.startAndWait(); | ||
} | ||
catch (InterruptedException e) { | ||
fail( "Unexpected InterruptedException: " + e.getMessage() ); | ||
} | ||
} | ||
|
||
backendMock.verifyExpectationsMet(); | ||
} | ||
|
||
private void initData() { | ||
for ( int i = 0; i < COUNT; i++ ) { | ||
persist( new Book( i, "TITLE_" + i, "AUTHOR_" + i ) ); | ||
} | ||
} | ||
|
||
private void persist(Book book) { | ||
loadingContext.persistenceMap( Book.PERSISTENCE_KEY ).put( book.id, book ); | ||
} | ||
|
||
@SearchEntity(name = Book.NAME, | ||
loadingBinder = @EntityLoadingBinderRef(type = StubEntityLoadingBinder.class)) | ||
@Indexed(index = Book.NAME) | ||
public static class Book { | ||
|
||
public static final String NAME = "Book"; | ||
public static final PersistenceTypeKey<Book, Integer> PERSISTENCE_KEY = | ||
new PersistenceTypeKey<>( Book.class, Integer.class ); | ||
|
||
@DocumentId | ||
private Integer id; | ||
|
||
@GenericField | ||
private String title; | ||
|
||
@GenericField | ||
private String author; | ||
|
||
public Book() { | ||
} | ||
|
||
public Book(Integer id, String title, String author) { | ||
this.id = id; | ||
this.title = title; | ||
this.author = author; | ||
} | ||
|
||
public Integer getId() { | ||
return id; | ||
} | ||
|
||
public String getTitle() { | ||
return title; | ||
} | ||
|
||
public String getAuthor() { | ||
return author; | ||
} | ||
} | ||
|
||
private static class SimulatedFailure extends RuntimeException { | ||
SimulatedFailure(String message) { | ||
super( message ); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.