Skip to content

Align default behavior of @RetryableTopic and RetryTopicConfigurationBuilder with documentation #3706

Open
@bky373

Description

@bky373

In what version(s) of Spring for Apache Kafka are you seeing this issue?

Since 3.2.0

Describe the bug

The default behavior of @RetryableTopic (SINGLE_TOPIC) differs from both RetryTopicConfigurationBuilder (MULTIPLE_TOPICS) and the Spring Kafka documentation, which describes using separate topics. Unifying all to SINGLE_TOPIC makes sense but needs documentation updates and clarification of changes.

  • Current result
$ kafka-topics --list --bootstrap-server localhost:9092
__consumer_offsets
retryable-annotation-topic
retryable-annotation-topic-dlt
retryable-annotation-topic-retry
  • Expected result (based on docs)
$ kafka-topics --list --bootstrap-server localhost:9092
__consumer_offsets
retryable-annotation-topic
retryable-annotation-topic-dlt
retryable-annotation-topic-retry-0
retryable-annotation-topic-retry-1

I would like to share three points regarding this issue.

  1. Inconsistency with Documentation
  2. Inconsistency with the Builder
  3. Direction for Defaults

1. Inconsistency with Documentation

The Spring Kafka documentation describes the default behavior for non-blocking retries as creating separate retry topics.

  • Example 1: How the Pattern Works

    If the message processing fails again the message will be forwarded to the next retry topic, and the pattern is repeated ..

  • Example 2: Topic Naming

    The default behavior is to create separate retry topics for each attempt, appended with an index value: retry-0, retry-1, …​, retry-n.

However, when using @RetryableTopic, the default behavior is to reuse the same topic for identical intervals. This means that without specifying any attributes (defaulting to FixedBackOff with attempts=3 and delay=1000ms), the retry topic will be reused. This change was introduced with PR 3058, which updated the default SameIntervalTopicReuseStrategy from MULTIPLE_TOPICS to SINGLE_TOPIC.
It would be ideal for the documentation and default behavior to be consistent.

2. Inconsistency with the Builder

As you know, non-blocking retries can also be configured using RetryTopicConfigurationBuilder instead of @RetryableTopic.
The default behavior of RetryTopicConfigurationBuilder.sameIntervalTopicReuseStrategy should align with the default behavior of @RetryableTopic to avoid confusion for users.

Currently, the builder’s default is still set to MULTIPLE_TOPICS, which is inconsistent with @RetryableTopic. The behavior of these two components should be unified.

3. Direction for Defaults

Referring to PR 2497, using SINGLE_TOPIC is more practical for exponential backoff scenarios. However, selecting SINGLE_TOPIC will also affect Fixed Backoff, as mentioned earlier.

If SINGLE_TOPIC is chosen as the default, the documentation regarding the default behavior should be revised.

Additionally, it’s crucial to highlight that this behavior differs significantly from previous versions.

While I’ve already updated the RetryTopicConfigurationBuilder.sameIntervalTopicReuseStrategy value to SINGLE_TOPIC and documented the changes, I want to ensure that this aligns with the intended direction. I’d appreciate your feedback on this matter.

To Reproduce

  • @RetryableTopic reuses the retry topic.
@Component
public class SimpleRetryableTopicListener {

    private static final Logger log = LoggerFactory.getLogger(SimpleRetryableTopicListener.class);

    @RetryableTopic
    @KafkaListener(
            id = "my-id",
            topics = "retryable-annotation-topic"
    )
    public void listen(String input) {
        log.info("--- Received input: {}", input);
        throw new RuntimeException("failed");
    }
}
  • RetryTopicConfigurationBuilder seperate the retry topics.
@Configuration
public class KafkaRetryConfig {

    @Bean
    public NewTopic newTopic() {
        return new NewTopic("retryable-annotation-topic", 1, (short) 1);
    }

    @Bean
    public RetryTopicConfiguration myRetryTopic() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");

        return RetryTopicConfigurationBuilder
                .newInstance()
                .includeTopic("retryable-annotation-topic")
                .create(new KafkaTemplate<>(new DefaultKafkaProducerFactory<>(props)));
    }}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions