Skip to content

GH-3032: RecoveryCallback on RetryingDeserializer #3035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 15, 2024

Conversation

sobychacko
Copy link
Contributor

Fixes: #3032

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.

Also missed an entry in the whats-new.adoc.

Thanks

* @param recoveryCallback {@link RecoveryCallback} to execute
* @since 3.1.2
*/
public void setRecoveryCallback(@Nullable RecoveryCallback<T> recoveryCallback) {
Copy link
Member

Choose a reason for hiding this comment

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

Setters must follow just after constructors.

@Test
void retryDeserializerWithRecoveryCallback() throws InterruptedException {
NonRecoverableDeser delegate = new NonRecoverableDeser();
RetryingDeserializer<String> rdes = new RetryingDeserializer<>(delegate, new RetryTemplate());
Copy link
Member

Choose a reason for hiding this comment

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

You also can just inject a lambda which would throw that exception.
I don't think it matters how many time we retry.
Only what is important for this test that RecoveryCallback is called.

NonRecoverableDeser delegate = new NonRecoverableDeser();
RetryingDeserializer<String> rdes = new RetryingDeserializer<>(delegate, new RetryTemplate());
rdes.setRecoveryCallback(context -> {
delegate.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.

You can just sent here a result of mock() and then verify in the end that it was called.
There is no any race conditions: everything in the test is called sequentially.

}

@Override
public void close() {
Copy link
Member

Choose a reason for hiding this comment

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

The Deserializer is functional interface, so no need to override those default methods.
See my comment above about its simple lambda for the RetryingDeserializer ctor.

@sobychacko
Copy link
Contributor Author

Also missed an entry in the whats-new.adoc.

Thanks

Aren't we going to backport the change to 3.1.x though?

RetryingDeserializer<String> rdes = new RetryingDeserializer<>((s, b) -> {
throw new RuntimeException();
}, new RetryTemplate());
RecoveryCallback<String> recoveryCallback = mock(RecoveryCallback.class);
Copy link
Member

Choose a reason for hiding this comment

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

I know that is going to be nit-picking, but I'll shoot it anyway.

The Mockito starting with some version does not need the type argument.
Just RecoveryCallback<String> recoveryCallback = mock(); is enough to make it working.
And the code looks nicer.

RecoveryCallback<String> recoveryCallback = mock(RecoveryCallback.class);
rdes.setRecoveryCallback(recoveryCallback);
rdes.deserialize("my-topic", "my-data".getBytes());
verify(recoveryCallback, times(1)).recover(any(RetryContext.class));
Copy link
Member

Choose a reason for hiding this comment

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

The times(1) is there by default:

    public static <T> T verify(T mock) {
        return MOCKITO_CORE.verify(mock, times(1));
    }

Why do you use this explicitly?

rdes.setRecoveryCallback(recoveryCallback);
rdes.deserialize("my-topic", "my-data".getBytes());
verify(recoveryCallback, times(1)).recover(any(RetryContext.class));
rdes.close();
Copy link
Member

Choose a reason for hiding this comment

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

There is nothing to close. See our delegate lambda.
IMHO the test should be as short as possible and be focused on the specific task.
If tests are too complicated and they are flaky, it is hard to find the reason and solution.
I know: this one is not, but better to have a good habit for less code - better support!

Copy link
Contributor Author

@sobychacko sobychacko Feb 15, 2024

Choose a reason for hiding this comment

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

ok, will cleanup, i was just following the original test patterns.

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.

LGTM.
Will merger when PR build is green.

@artembilan artembilan merged commit 284a188 into spring-projects:main Feb 15, 2024
@vspiliop
Copy link

@artembilan How can I find when spring-kafka v3.1.2 will be included in a

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>...</version>
  </parent>

Should I assume in the next spring boot release? Is there a link with planned future spring boot releases?

Thanks again!

@artembilan
Copy link
Member

@vspiliop ,

yes, this one is included in released yesterday Spring Boot 3.2.3.
This calendar have all our scheduled releases: https://calendar.spring.io/

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.

Provide a recovery callback for the execute method of RetryingDeserializer
3 participants