Skip to content

Commit

Permalink
GH-1643: Add More ConsumerStoppedEvent.Reasons
Browse files Browse the repository at this point in the history
Resolves #1643

**cherry-pick to 2.5.x**
  • Loading branch information
garyrussell authored and artembilan committed Nov 24, 2020
1 parent 1140d5e commit 2f2010d
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public class ConsumerStoppedEvent extends KafkaEvent {

private static final long serialVersionUID = 1L;

/**
* Reasons for stopping a consumer.
* @since 2.5.9
*
*/
public enum Reason {

/**
Expand All @@ -44,6 +49,18 @@ public enum Reason {
*/
FENCED,

/**
* An authorization exception occurred.
* @since 2.5.10
*/
AUTH,

/**
* No offset found for a partition and no reset policy.
* @since 2.5.10
*/
NO_OFFSET,

/**
* A {@link java.lang.Error} was thrown.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,24 @@ private void publishConsumerStoppingEvent(Consumer<?, ?> consumer) {

private void publishConsumerStoppedEvent(@Nullable Throwable throwable) {
if (getApplicationEventPublisher() != null) {
Reason reason;
if (throwable instanceof Error) {
reason = Reason.ERROR;
}
else if (throwable instanceof StopAfterFenceException || throwable instanceof FencedInstanceIdException) {
reason = Reason.FENCED;
}
else if (throwable instanceof AuthorizationException) {
reason = Reason.AUTH;
}
else if (throwable instanceof NoOffsetForPartitionException) {
reason = Reason.NO_OFFSET;
}
else {
reason = Reason.NORMAL;
}
getApplicationEventPublisher().publishEvent(new ConsumerStoppedEvent(this, this.thisOrParentContainer,
throwable instanceof Error
? Reason.ERROR
: throwable instanceof StopAfterFenceException
? Reason.FENCED
: Reason.NORMAL));
reason));
}
}

Expand Down Expand Up @@ -1066,12 +1078,14 @@ public void run() { // NOSONAR complexity
catch (NoOffsetForPartitionException nofpe) {
this.fatalError = true;
ListenerConsumer.this.logger.error(nofpe, "No offset and no reset policy");
exitThrowable = nofpe;
break;
}
catch (AuthorizationException ae) {
if (this.authorizationExceptionRetryInterval == null) {
ListenerConsumer.this.logger.error(ae, "Authorization Exception and no authorizationExceptionRetryInterval set");
this.fatalError = true;
exitThrowable = ae;
break;
}
else {
Expand All @@ -1087,6 +1101,7 @@ public void run() { // NOSONAR complexity
this.fatalError = true;
ListenerConsumer.this.logger.error(fie, "'" + ConsumerConfig.GROUP_INSTANCE_ID_CONFIG
+ "' has been fenced");
exitThrowable = fie;
break;
}
catch (StopAfterFenceException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import org.springframework.kafka.event.ConsumerPausedEvent;
import org.springframework.kafka.event.ConsumerResumedEvent;
import org.springframework.kafka.event.ConsumerStoppedEvent;
import org.springframework.kafka.event.ConsumerStoppedEvent.Reason;
import org.springframework.kafka.event.ConsumerStoppingEvent;
import org.springframework.kafka.event.NonResponsiveConsumerEvent;
import org.springframework.kafka.listener.ContainerProperties.AckMode;
Expand Down Expand Up @@ -2726,16 +2727,19 @@ void testFatalErrorOnAuthorizationException() throws Exception {
KafkaMessageListenerContainer<Integer, String> container =
new KafkaMessageListenerContainer<>(cf, containerProps);

AtomicReference<ConsumerStoppedEvent.Reason> reason = new AtomicReference<>();
CountDownLatch stopped = new CountDownLatch(1);

container.setApplicationEventPublisher(e -> {
if (e instanceof ConsumerStoppedEvent) {
reason.set(((ConsumerStoppedEvent) e).getReason());
stopped.countDown();
}
});

container.start();
assertThat(stopped.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(reason.get()).isEqualTo(Reason.AUTH);
container.stop();
}

Expand Down
2 changes: 2 additions & 0 deletions src/reference/asciidoc/kafka.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2655,6 +2655,8 @@ In addition, the `ConsumerStoppedEvent` has the following additional property:
** `NORMAL` - the consumer stopped normally (container was stopped).
** `ERROR` - a `java.lang.Error` was thrown.
** `FENCED` - the transactional producer was fenced and the `stopContainerWhenFenced` container property is `true`.
** `AUTH` - an `AuthorizationException` was thrown and the `authorizationExceptionRetryInterval` is not configured.
** `NO_OFFSET` - there is no offset for a partition and the `auto.offset.reset` policy is `none`.

You can use this event to restart the container after such a condition:

Expand Down

0 comments on commit 2f2010d

Please sign in to comment.