Skip to content

Split retrying into more detailed modules#340

Merged
akudiyar merged 1 commit intomasterfrom
retrying_runnable
Mar 13, 2023
Merged

Split retrying into more detailed modules#340
akudiyar merged 1 commit intomasterfrom
retrying_runnable

Conversation

@ArtDu
Copy link
Copy Markdown
Contributor

@ArtDu ArtDu commented Jan 10, 2023

I haven't forgotten about:

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

Related issues:
Closes #341

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.

Overall looks better than the current state, let's comb it out a bit

new RetryingAsyncOperation<>(this, operation, resultFuture, lastExceptionWrapper);
CompletableFuture.runAsync(() -> {
runAsyncOperation(operation, resultFuture, lastExceptionWrapper);
retryingAsyncOperation.run();
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.

Wouldn't it be better here to organize the code as
.runAsync(retryingAsyncOperation, executor).acceptEither(... global timeout future..., ...dummy callback...) ?

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.

The current variant does not schedule the global timeout future correctly, because the "run()" method is executed synchronously in the current thread

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.

And we need to start operation asynchronously because we need to start timer after starting of the task

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.

But you are first finishing the task here and then scheduling a timer. You need to run the main task asynchronously.

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.

But you are first finishing the task here and then scheduling a timer. You need to run the main task asynchronously.

I don't finish the task here, I just start it. Using acceptEither could give us the probability of completing the timeout task before the operation was started

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.

It should never happen really. Both tasks are scheduled with acceptEither.

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.

Yeah, it will wait for them until they both finish. But retryingAsyncOperation.run(); is not running of the operation, it's just starting of operation. Then the scheduler can start timeout before operation

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.

runAsync is not running the operation too, it just schedules it in the executor. We schedule both tasks and wait on both, which one finishes first:
https://www.concretepage.com/java/java-8/java-completablefuture-accepteither
Note, we may need to use supplyAsync instead of runAsync, depending on the callback.

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.

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 code will be cleaner, although I admit run()" is not "synchronous" in the way that we get the operation result first. But we may call the callback in whenComplete() there even before we start the timeout.

}
return null;
}).exceptionally(ex -> { // if error has been happened in previous exceptionally section
public <R> CompletableFuture<R> wrapOperation(Supplier<CompletableFuture<R>> operation, Executor executor) {
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.

It seems that this function can now be combined with the one in the base class and does not need a separate implementation

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.

It was combined if I remember it correcty

@ArtDu ArtDu force-pushed the retrying_runnable branch 3 times, most recently from 76958bb to 2721d58 Compare January 31, 2023 15:51
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.

Answered some of the comments

// start async operation running
CompletableFuture<T> operationFuture = operation.get();
// start scheduled request timeout task
// it never completes correctly only exceptionally
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 still need to either delete the comment (it is duplicated by the class Javadoc) or rewrite it to the timeout task can only be completed with an exception or canceled for example.

CompletableFuture<T> requestTimeoutFuture = requestTimeoutOperation.get();

operationFuture.acceptEither(requestTimeoutFuture, resultFuture::complete)
.exceptionally(ex -> { // if requestTimeout has been raised or operation return exception
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.

But it will be the only one, instead of several unnecessary return null statements.


if (policy.canRetryRequest(ex)) {
// retry it after delay
ScheduledFuture<?> delayFuture = TarantoolRequestRetryPolicies.getTimeoutScheduler()
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.

Please add any description to the error. Otherwise, anyone will need to dig down the sources for the source code line.

resultFuture.completeExceptionally(ex);
}
return null;
}).exceptionally(ex -> { // if error has been happened in previous exceptionally section
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.

If try ... except is within a callback, it's a normal practice and even the only way in some cases (like for the checked exceptions).

new RetryingAsyncOperation<>(this, operation, resultFuture, lastExceptionWrapper);
CompletableFuture.runAsync(() -> {
runAsyncOperation(operation, resultFuture, lastExceptionWrapper);
retryingAsyncOperation.run();
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.

But you are first finishing the task here and then scheduling a timer. You need to run the main task asynchronously.

@ArtDu ArtDu force-pushed the retrying_runnable branch 3 times, most recently from d1e379e to 1428b81 Compare March 1, 2023 11:52
@ArtDu ArtDu marked this pull request as ready for review March 1, 2023 11:59
@ArtDu ArtDu changed the title WIP: Retrying runnable Split retrying into more detailed modules Mar 1, 2023
@ArtDu ArtDu requested a review from akudiyar March 1, 2023 12:00
Closes #341

Co-authored-by: Alexey Kuzin <akudiyar@gmail.com>
@ArtDu ArtDu force-pushed the retrying_runnable branch from 1428b81 to 0175919 Compare March 1, 2023 12:05
@dkasimovskiy
Copy link
Copy Markdown
Contributor

@akudiyar please take a look on the PR.

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.

It seems that some changes that I was reviewing disappeared completely. so I think we need to merge this already. Nvm these comments, but let them be

new RetryingAsyncOperation<>(this, operation, resultFuture, lastExceptionWrapper);
CompletableFuture.runAsync(() -> {
runAsyncOperation(operation, resultFuture, lastExceptionWrapper);
retryingAsyncOperation.run();
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.

new RetryingAsyncOperation<>(this, operation, resultFuture, lastExceptionWrapper);
CompletableFuture.runAsync(() -> {
runAsyncOperation(operation, resultFuture, lastExceptionWrapper);
retryingAsyncOperation.run();
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 code will be cleaner, although I admit run()" is not "synchronous" in the way that we get the operation result first. But we may call the callback in whenComplete() there even before we start the timeout.

@akudiyar akudiyar added this pull request to the merge queue Mar 13, 2023
Merged via the queue into master with commit d3a3d6a Mar 13, 2023
@akudiyar akudiyar deleted the retrying_runnable branch March 13, 2023 22:19
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.

Use Runnable classes in AsyncOperation and TimeoutOperation in retrying

3 participants