Skip to content

Change sync implementation on async in retrying#333

Merged
ArtDu merged 1 commit intomasterfrom
retrying
Jan 12, 2023
Merged

Change sync implementation on async in retrying#333
ArtDu merged 1 commit intomasterfrom
retrying

Conversation

@ArtDu
Copy link
Copy Markdown
Contributor

@ArtDu ArtDu commented Dec 26, 2022

We had synchronous get and sleep, now we use timeoutScheduler for these purposes. The retry is invoked by recursive method calls in the asynchronous calls section.

I haven't forgotten about:

  • Tests
  • Changelog
  • Documentation
  • Commit messages comply with the guideline
  • Cleanup the code for review. See checklist

Related issues:
Closes #299

@ArtDu ArtDu force-pushed the retrying branch 2 times, most recently from d534cf5 to 3b9d4ee Compare December 27, 2022 19:33
@ArtDu ArtDu force-pushed the retrying branch 3 times, most recently from a89b1c5 to 759c9d2 Compare December 28, 2022 13:52
@ArtDu ArtDu changed the title WIP: Add async call in retrying client Change sync realisation on async in retrying Dec 28, 2022
@ArtDu ArtDu force-pushed the retrying branch 4 times, most recently from 321d835 to 774cd55 Compare December 28, 2022 14:07
@ArtDu ArtDu marked this pull request as ready for review December 28, 2022 14:07
@ArtDu ArtDu force-pushed the retrying branch 2 times, most recently from f01b530 to aa5e6bf Compare December 28, 2022 14:35
bitgorbovsky
bitgorbovsky previously approved these changes Dec 28, 2022
Copy link
Copy Markdown

@bitgorbovsky bitgorbovsky left a comment

Choose a reason for hiding this comment

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

Generally it looks good to me

@ArtDu ArtDu requested a review from bitgorbovsky December 29, 2022 10:44
@akudiyar akudiyar changed the title Change sync realisation on async in retrying Change sync implementation on async in retrying Dec 29, 2022
*/
public interface RequestRetryPolicy {

ScheduledExecutorService timeoutScheduler =
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This field will be static and public, I believe we are not willing to do that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is a problem that we need this ScheduledExecutorService timeoutScheduler in the interface to use solutions like this one:

RequestRetryPolicy policy = throwable -> true;

@ArtDu ArtDu force-pushed the retrying branch 2 times, most recently from 5d450ea to c7bd439 Compare December 29, 2022 15:25
Copy link
Copy Markdown
Collaborator

@akudiyar akudiyar left a comment

Choose a reason for hiding this comment

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

Not a full review (we need to fix at least one place first because the second is almost the same). I feel that the new code will be much easier to understand if it will have any comments regarding the logic. Currently, it is not clear what potential failure cases we are covering by what statement, which adds an additional future handler. Also, I have some doubts that this code will work as intended because we are not waiting anywhere on the modified futures after adding a handler to it.

/**
* @author Artyom Dubinin
*/
final class DefaultTimeoutScheduler {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe we don't need a separate class-container for that. We already have TarantoolRequestRetryPolicies where we can put and access this private static variable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved to TarantoolRequestRetryPolicies

} while (this.canRetryRequest(ex));
throw new CompletionException(ex);
}, executor);
default <T> CompletableFuture<T> failAfterRequestTimeout(CompletableFuture<T> resultFuture) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same for this method - it may be an internal method of the new Runnable class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Created issue #341 and PR #340 with concept of it

return null;
}

while (ex instanceof ExecutionException || ex instanceof CompletionException) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: This loop may be a part of some separate error handler. Not neccesasry now btw

CompletableFuture<T> operationFuture = operation.get();
CompletableFuture<T> requestTimeoutFuture = failAfterRequestTimeout(resultFuture);

operationFuture.acceptEither(requestTimeoutFuture, resultFuture::complete).exceptionally(ex -> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What will happen when operationFuture completes normally?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ALso I don't see who is waiting on the result future produced from acceptEither. Without that the desired effect will not be achieved

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What will happen when operationFuture completes normally?

resultFuture::complete

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ALso I don't see who is waiting on the result future produced from acceptEither. Without that the desired effect will not be achieved

We don't need to wait this future, with waiting it will be the same synchronous stuff

resultFuture.completeExceptionally(ex);
}
return null;
}).exceptionally(ex -> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

From where comes the exception that is handled here? What is the case for it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If previous exceptionally completed with exception like problem with incorrect user canRetryRequest, we'd go there

final TimeoutException ex = new TimeoutException("Request timeout after " + requestTimeout);
return future.completeExceptionally(ex);
}, requestTimeout, TimeUnit.MILLISECONDS);
resultFuture.whenComplete((res, ex) -> scheduledFuture.cancel(false));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe it will not work this way. whenComplete returns a new future on which we need to call join or get to invoke the effect. It doesn't change resultFuture itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe it will not work this way. whenComplete returns a new future on which we need to call join or get to invoke the effect. It doesn't change resultFuture itself.

We don't give a care what whenComplete return us, we should only know that resultFuture is completed. And with this knowledge we just cancel scheduledFuture cause it isn't needed already

ScheduledFuture<?> delayFuture = getTimeoutScheduler().schedule(() -> {
runAsyncOperation(operation, resultFuture, lastExceptionWrapper);
}, getDelay(), TimeUnit.MILLISECONDS);
resultFuture.whenComplete((r, e) -> delayFuture.cancel(false));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe it will not work this way. whenComplete returns a new future on which we need to call join or get to invoke the effect. It doesn't change resultFuture itself.

Copy link
Copy Markdown
Contributor Author

@ArtDu ArtDu Jan 1, 2023

Choose a reason for hiding this comment

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

We don't want to change resultFuture here. It's just cancel already started delayFuture, it's like optimization

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@akudiyar

I've been thinking about it and here's what:

Returns a new CompletionStage with the same result or exception as this stage, that executes the given action when this stage completes.

WhenComplete returns the same result as resultFuture. We don't need to wait this whenComplete future.

}

@Test
void testAsyncPerformance() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We need to make a sync + async parallel test with the use of different threads

Copy link
Copy Markdown
Contributor Author

@ArtDu ArtDu Jan 10, 2023

Choose a reason for hiding this comment

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

Added 2155f78

@ArtDu ArtDu force-pushed the retrying branch 2 times, most recently from c13c011 to d736235 Compare January 10, 2023 09:47
@ArtDu ArtDu requested a review from Elishtar January 10, 2023 10:20
Elishtar
Elishtar previously approved these changes Jan 10, 2023
bitgorbovsky
bitgorbovsky previously approved these changes Jan 10, 2023
akudiyar
akudiyar previously approved these changes Jan 11, 2023
Copy link
Copy Markdown
Collaborator

@akudiyar akudiyar left a comment

Choose a reason for hiding this comment

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

I approve this as we discussed all the details verbally, with two conditions:

  • CHANGELOG needs to be fixed
  • #341 needs to be implemented right after that PR, because these two new methods in API are a bad change, the method contents are too complex for a new reader and the unit tests for this logic are lacking.

CHANGELOG.md Outdated
### Features
- Add `fields` option to ProxySpace for controlling the result tuple fields ([#236](https://github.com/tarantool/cartridge-java/pull/236))
- Parse metadata from crud response ([#272](https://github.com/tarantool/cartridge-java/pull/272))
- Do not use sync calls instead of async in retrying ([#299](https://github.com/tarantool/cartridge-java/pull/299))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This needs to be moved to unreleased

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@ArtDu ArtDu force-pushed the retrying branch 2 times, most recently from 6d80a5d to e6c3c57 Compare January 12, 2023 08:03
@ArtDu ArtDu merged commit 83841c4 into master Jan 12, 2023
@ArtDu ArtDu deleted the retrying branch January 12, 2023 09:17
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.

100% cpu problem with retrying

4 participants