Skip to content

Commit

Permalink
HSEARCH-3735 Add an "entity indexing failure" to the failure handler
Browse files Browse the repository at this point in the history
  • Loading branch information
yrodiere committed Oct 24, 2019
1 parent daaa9ff commit fd84fe7
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 6 deletions.
11 changes: 8 additions & 3 deletions documentation/src/main/asciidoc/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,20 @@ Hibernate Search will call the `handle` methods whenever a failure occurs.
----
include::{sourcedir}/org/hibernate/search/documentation/reporting/failurehandler/MyFailureHandler.java[tags=include]
----
<1> `handle(FailureContext)` is called for failures that are not related to one index in particular.
<1> `handle(FailureContext)` is called for generic failures that do not fit any other specialized `handle` method.
<2> Get a description of the failing operation from the context.
<3> Get the throwable thrown when the operation failed from the context.
<4> Use the context-provided information to report the failure in any relevant way.
<5> `handle(IndexFailureContext)` is called for failures that are related to one index in particular.
<5> `handle(EntityIndexingFailureContext)` is called for failures occurring when indexing entities.
<6> On top of the failing operation and throwable,
the context also lists references to entities that could not be indexed correctly because of the failure.
<7> Use the context-provided information to report the failure in any relevant way.
<8> `handle(IndexFailureContext)` is called for failures that are related to one index in particular.
<9> On top of the failing operation and throwable,
the index context also lists operations that could not be committed because of the failure.
These are operations that were applied before the failure, but weren't committed yet.
<7> Use the context-provided information to report the failure in any relevant way.
<10> Use the context-provided information to report the failure in any relevant way.
[source, XML, indent=0, subs="+callouts"]
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.List;

import org.hibernate.search.engine.reporting.EntityIndexingFailureContext;
import org.hibernate.search.engine.reporting.FailureContext;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.reporting.IndexFailureContext;
Expand All @@ -33,15 +34,27 @@ public void handle(FailureContext context) { // <1>
}

@Override
public void handle(IndexFailureContext context) { // <5>
public void handle(EntityIndexingFailureContext context) { // <5>
String failingOperationDescription = context.getFailingOperation().toString();
Throwable throwable = context.getThrowable();
List<String> entityReferencesAsStrings = new ArrayList<>();
for ( Object entityReference : context.getEntityReferences() ) { // <6>
entityReferencesAsStrings.add( entityReference.toString() );
}

// ... report the failure ... // <7>
}

@Override
public void handle(IndexFailureContext context) { // <8>
String failingOperationDescription = context.getFailingOperation().toString();
Throwable throwable = context.getThrowable();
List<String> uncommittedOperationsDescriptions = new ArrayList<>();
for ( Object uncommittedOperation : context.getUncommittedOperations() ) { // <6>
for ( Object uncommittedOperation : context.getUncommittedOperations() ) { // <9>
uncommittedOperationsDescriptions.add( uncommittedOperation.toString() );
}

// ... report the failure ... // <7>
// ... report the failure ... // <10>
}
}
// end::include[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.reporting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Contextual information about a failure to index entities.
*/
public class EntityIndexingFailureContext extends FailureContext {

public static Builder builder() {
return new Builder();
}

private final List<Object> entityReferences;

private EntityIndexingFailureContext(Builder builder) {
super( builder );
this.entityReferences = builder.entityReferences == null
? Collections.emptyList() : Collections.unmodifiableList( builder.entityReferences );
}

/**
* @return A list of references to entities that may not be indexed correctly as a result of the failure.
* Never {@code null}, but may be empty.
* Use {@link Object#toString()} to get a textual representation of each reference,
* or cast it to the mapper-specific {@code EntityReference} type.
*/
public List<Object> getEntityReferences() {
return entityReferences;
}

public static class Builder extends FailureContext.Builder {

private List<Object> entityReferences;

private Builder() {
}

public void entityReference(Object entityReference) {
if ( entityReferences == null ) {
entityReferences = new ArrayList<>();
}
entityReferences.add( entityReference );
}

@Override
public EntityIndexingFailureContext build() {
return new EntityIndexingFailureContext( this );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ public interface FailureHandler {
*/
void handle(FailureContext context);

/**
* Handle the failure of entity indexing.
* <p>
* This method is expected to report the failure somewhere (logs, ...),
* then return as quickly as possible.
* Heavy error processing (sending emails, ...), if any, should be done asynchronously.
* <p>
* This method should <strong>never</strong> throw any exception:
* doing so will lead to undetermined behavior in Hibernate Search background threads.
*
* @param context Contextual information about the failure (throwable, operation, ...)
*/
void handle(EntityIndexingFailureContext context);

/**
* Handle the failure of an index operation.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.lang.invoke.MethodHandles;

import org.hibernate.search.engine.logging.impl.Log;
import org.hibernate.search.engine.reporting.EntityIndexingFailureContext;
import org.hibernate.search.engine.reporting.FailureContext;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.reporting.IndexFailureContext;
Expand All @@ -34,6 +35,16 @@ public void handle(FailureContext context) {
}
}

@Override
public void handle(EntityIndexingFailureContext context) {
try {
delegate.handle( context );
}
catch (Throwable t) {
log.failureInFailureHandler( t );
}
}

@Override
public void handle(IndexFailureContext context) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.lang.invoke.MethodHandles;
import java.util.List;

import org.hibernate.search.engine.reporting.EntityIndexingFailureContext;
import org.hibernate.search.engine.reporting.FailureContext;
import org.hibernate.search.engine.reporting.IndexFailureContext;
import org.hibernate.search.engine.reporting.FailureHandler;
Expand All @@ -29,6 +30,11 @@ public void handle(FailureContext context) {
log.exceptionOccurred( formatMessage( context ).toString(), context.getThrowable() );
}

@Override
public void handle(EntityIndexingFailureContext context) {
log.exceptionOccurred( formatMessage( context ).toString(), context.getThrowable() );
}

@Override
public void handle(IndexFailureContext context) {
log.exceptionOccurred( formatMessage( context ).toString(), context.getThrowable() );
Expand All @@ -50,6 +56,22 @@ private StringBuilder formatMessage(FailureContext context) {
return messageBuilder;
}

private StringBuilder formatMessage(EntityIndexingFailureContext context) {
final List<?> entityReferences = context.getEntityReferences();

final StringBuilder messageBuilder = formatMessage( (FailureContext) context );

if ( ! entityReferences.isEmpty() ) {
messageBuilder.append( "Entities that could not be indexed correctly:\n" );
for ( Object entityReference : entityReferences ) {
messageBuilder.append( entityReference );
messageBuilder.append( " " );
}
}

return messageBuilder;
}

private StringBuilder formatMessage(IndexFailureContext context) {
final List<?> uncommittedOperations = context.getUncommittedOperations();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static org.easymock.EasyMock.expectLastCall;
import static org.hamcrest.CoreMatchers.sameInstance;

import org.hibernate.search.engine.reporting.EntityIndexingFailureContext;
import org.hibernate.search.engine.reporting.FailureContext;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.reporting.IndexFailureContext;
Expand Down Expand Up @@ -63,6 +64,38 @@ Level.ERROR, sameInstance( failure ), "failure handler threw an exception"
verifyAll();
}

@Test
public void entityIndexingContext_runtimeException() {
RuntimeException failure = new SimulatedRuntimeException();

logged.expectEvent(
Level.ERROR, sameInstance( failure ), "failure handler threw an exception"
);

resetAll();
failureHandlerMock.handle( anyObject( EntityIndexingFailureContext.class ) );
expectLastCall().andThrow( failure );
replayAll();
wrapper.handle( EntityIndexingFailureContext.builder().build() );
verifyAll();
}

@Test
public void entityIndexingContext_error() {
Error failure = new SimulatedError();

logged.expectEvent(
Level.ERROR, sameInstance( failure ), "failure handler threw an exception"
);

resetAll();
failureHandlerMock.handle( anyObject( EntityIndexingFailureContext.class ) );
expectLastCall().andThrow( failure );
replayAll();
wrapper.handle( EntityIndexingFailureContext.builder().build() );
verifyAll();
}

@Test
public void indexContext_runtimeException() {
RuntimeException failure = new SimulatedRuntimeException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.util.impl.integrationtest.common.stub;

import org.hibernate.search.engine.reporting.EntityIndexingFailureContext;
import org.hibernate.search.engine.reporting.FailureContext;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.reporting.IndexFailureContext;
Expand All @@ -15,6 +16,7 @@ public class StubFailureHandler implements FailureHandler {

public static StaticCounters.Key CREATE = StaticCounters.createKey();
public static StaticCounters.Key HANDLE_GENERIC_CONTEXT = StaticCounters.createKey();
public static StaticCounters.Key HANDLE_ENTITY_INDEXING_CONTEXT = StaticCounters.createKey();
public static StaticCounters.Key HANDLE_INDEX_CONTEXT = StaticCounters.createKey();

public StubFailureHandler() {
Expand All @@ -26,6 +28,11 @@ public void handle(FailureContext context) {
StaticCounters.get().increment( HANDLE_GENERIC_CONTEXT );
}

@Override
public void handle(EntityIndexingFailureContext context) {
StaticCounters.get().increment( HANDLE_ENTITY_INDEXING_CONTEXT );
}

@Override
public void handle(IndexFailureContext context) {
StaticCounters.get().increment( HANDLE_INDEX_CONTEXT );
Expand Down

0 comments on commit fd84fe7

Please sign in to comment.