Skip to content

Conversation

@Flamme1004K
Copy link
Contributor

I'm submitting a PR for issue #4230. Although the KafkaBackoffAwareMessageListenerAdapter class delegates to RecordMessagingMessageListenerAdapter, it was observed that metrics were being recorded incorrectly because it wasn't adding metrics despite RecordMessagingMessageListenerAdapter.

To resolve this, I modified the code to set isListenerAdapterObservationAware to true even when it's a KafkaBackoffAwareMessageListenerAdapter. Additionally, when adding observations, I ensured that if the KafkaBackoffAwareMessageListenerAdapter class delegates to RecordMessagingMessageListenerAdapter, it also adds the metrics.

Thank you for your attention.

@Flamme1004K Flamme1004K changed the title Enhance listener observation handling with additional adapter support GH-4230: Enhance listener observation handling with additional adapter support Jan 10, 2026
@victorpasqualino
Copy link

I did something very similar, with a few differences: #4240

@Flamme1004K
Copy link
Contributor Author

Flamme1004K commented Jan 11, 2026

@victorpasqualino good Idea!

I got inspired by your code and tried refactoring it. Can you take a look?

@victorpasqualino
Copy link

@victorpasqualino good Idea!

I got inspired by your code and tried refactoring it. Can you take a look?

* [211c631](https://github.com/spring-projects/spring-kafka/pull/4238/commits/211c6313eee43311ec5cf21e3f1287b915f1fc48)

Looks good to me. Thank you

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, sure you run /gradlew check locally before pushing.

Thanks

* @author Soby Chacko
* @since 3.2.7
* @author Hyoungjune Kim
* @since 4.0.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct.
The class was created really in that version, so you cannot modify it.
Plus, the version you target is still not correct since I fill like we need to back-port the fix to 3.3.x as well.
Therefore, if we do @since, we should aim for the lowest version we are going to back-port to.
Luckily, we don't need to use @since anywhere in your change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I rollbacked @since

}

private MessageListener<?, ?> unwrapDelegateIfAny(MessageListener<?, ?> listener) {
if (listener instanceof KafkaBackoffAwareMessageListenerAdapter<?, ?> backoffAware) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The KafkaBackoffAwareMessageListenerAdapter is an AbstractDelegatingMessageListenerAdapter, so I think any of those should be covered here.
If we go this method, it has to be static.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good! I added static to this method

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. No need to add a comment with a confirmation.
I'll see an agreement in the next commit you push.

How about an AbstractDelegatingMessageListenerAdapter support?
You didn't comment anything to contradict my suggestion and didn't implement it.
With the 👍 it give a false impression that all the request have been addressed.
Any comments, please?

And another our code style suggestion: the static methods should go in the end of class.
Not a big deal: just an info if you'd like to know more about our developing process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was such a good suggestion I ended up hitting the emoji button without thinking. Next time I'll make sure to use them appropriately.

I've been thinking about it, and instead of using AbstractDelegatingMessageListenerAdapter, how about checking if it's an instance of DelegatingMessageListener and extracting it if it is? What do you think?

@Flamme1004K
Copy link
Contributor Author

@artembilan Thank you, Code Review! I run /gradlew check. result I modifed source code.

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the update!

I won't mind any objections for my review.

}

static class RetryBackOffObservationListener {
final CountDownLatch latch = new CountDownLatch(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every member in the class (including before the last } of the last inner class) has to be surrounded with blank lines.
That makes the code much easier to read.
And we do here an Open Source, so our code should be as clean as possible and as a good example for community.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm learning about open source. Thank you.


private void setupObservationRegistry(ObservationRegistry observationRegistry) {
if (this.listener == null) {
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for better void digesting it would be better to make it opposite: use if (this.listener != null) { instead around the rest of the code in this method.

}

private MessageListener<?, ?> unwrapDelegateIfAny(MessageListener<?, ?> listener) {
if (listener instanceof KafkaBackoffAwareMessageListenerAdapter<?, ?> backoffAware) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. No need to add a comment with a confirmation.
I'll see an agreement in the next commit you push.

How about an AbstractDelegatingMessageListenerAdapter support?
You didn't comment anything to contradict my suggestion and didn't implement it.
With the 👍 it give a false impression that all the request have been addressed.
Any comments, please?

And another our code style suggestion: the static methods should go in the end of class.
Not a big deal: just an info if you'd like to know more about our developing process.

// NOTHING
}
finally {
latch.countDown();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need any logic here since in the end you still fall back to the await().untilAsserted().
Therefore, such a latch is just redundant barrier which really does not contribute to the async flow.

Copy link
Contributor Author

@Flamme1004K Flamme1004K Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During local testing, intermittent failures in the test code were observed, so the source code was modified to call latch.countDown().

The reason is that the count for spring.kafka.listener.id can only be obtained after messages are consumed by the listener. If verification occurs before consumption, a null pointer exception occurs regardless of untilAsserted.

@Flamme1004K
Copy link
Contributor Author

@artembilan Thank you for your prompt feedback on the code review.

I've addressed the feedback you provided. Please review it.

private void setupObservationRegistry(ObservationRegistry observationRegistry) {
if (this.listener != null) {
MessageListener<?, ?> target = unwrapDelegateIfAny(this.listener);
if (target instanceof RecordMessagingMessageListenerAdapter<?, ?> adapter) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to think about this change twice.
You see, our existing logic is RecordMessagingMessageListenerAdapter.class.equals(this.listener.getClass()).
I believe even if we extract the delegate, we still have to be sure that we talk about exactly the same object.
So, if we do this instanceof, then any RecordMessagingMessageListenerAdapter extension is a valid candidate for isListenerAdapterObservationAware.
But that is really not what we want here.
We want to check exactly only for the RecordMessagingMessageListenerAdapter and nothing more.
In some other places we do have some custom RecordMessagingMessageListenerAdapter , which must not be treated as an observation aware.
Makes sense?

Copy link
Contributor Author

@Flamme1004K Flamme1004K Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artembilan I agree. It's correct to change the code to verify it's specifically RecordMessagingMessageListenerAdapter.class.

I will modify the code to RecordMessagingMessageListenerAdapter.class.equals(this.listener.getClass()).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the code!

878dc39

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!
Will merge when build is green.

Thank you!

@artembilan artembilan merged commit 90d8522 into spring-projects:main Jan 13, 2026
3 checks passed
@artembilan
Copy link
Member

@Flamme1004K ,

thank you for contribution; looking forward for more!

@Flamme1004K
Copy link
Contributor Author

@artembilan
Thank you! I will strive to contribute more in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants