Skip to content
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

Fix termination of request context for non-blocking scheduled methods #27108

Merged
merged 1 commit into from Aug 4, 2022

Conversation

mkouba
Copy link
Contributor

@mkouba mkouba commented Aug 3, 2022

@mkouba mkouba requested a review from DavideD August 3, 2022 12:35
Copy link
Contributor

@DavideD DavideD left a comment

Choose a reason for hiding this comment

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

Nice, LGTM

@DavideD
Copy link
Contributor

DavideD commented Aug 3, 2022

A test would be nice though. It's easy to mess up this stuff with a refactoring.

@mkouba
Copy link
Contributor Author

mkouba commented Aug 3, 2022

A test would be nice though. It's easy to mess up this stuff with a refactoring.

For sure. We'll need an integration test for this...

@DavideD
Copy link
Contributor

DavideD commented Aug 3, 2022

By the way, the fix looks correct but I haven't run a test to check that it fixes the issue. I trust you on this.

@mkouba
Copy link
Contributor Author

mkouba commented Aug 3, 2022

By the way, the fix looks correct but I haven't run a test to check that it fixes the issues. I trust you on this.

I did. But since CP is involved one can be never sure ;-).

@mkouba
Copy link
Contributor Author

mkouba commented Aug 3, 2022

I will add a test shortly...

@mkouba
Copy link
Contributor Author

mkouba commented Aug 3, 2022

In fact, the fix is probably wrong. Or at least does not cover all possible cases. I'm on it.

@mkouba mkouba requested a review from machi1990 August 3, 2022 14:26
@mkouba mkouba force-pushed the sched-uni-req-context-fix branch 2 times, most recently from 49124e5 to 9cd048a Compare August 3, 2022 14:28
@mkouba
Copy link
Contributor Author

mkouba commented Aug 3, 2022

The PR should be ready for review now. I've modified the existing NonBlockingScheduledMethodTest to verify the request context termination. Note that we don't need to terminate the context when an invokeBean() invocation results in an exception because the failure is wrapped in the generated invoker class with CompletableFuture.failedStage().

} finally {
requestContext.terminate();
// Always deactivate the context
requestContext.deactivate();
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure this is right?

The context could be deactivated before being used by invokeBean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, deactivation means that we remove a threadlocal but do not destroy the context/beans, i.e. the thread is "clear". If Mutiny is used inside the invokeBean() then we rely on context propagation to make a snapshot of the current context and propagate it accordingly.

It wouldn't work for CompletionStage with a custom executor though. In this case, a user needs to handle the context propagation manually (as it was before).

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks correct, CP takes snapshot based on the ContextState of currently active context when it is captured. If I get this scenario right, then this will happen when you perform invokeBean().

@DavideD
Copy link
Contributor

DavideD commented Aug 3, 2022

Note that we don't need to terminate the context when an invokeBean() invocation results in an exception because the failure is wrapped in the generated invoker class with CompletableFuture.failedStage().

There's nothing in the javadoc of invokeBean that states that it should never throw an exception though. It actually declares throws Exception in the signature.

@DavideD
Copy link
Contributor

DavideD commented Aug 3, 2022

if the implementation of invokeBean looks something like this:

protected CompletionStage<Void> invokeBean(ScheduledExecution execution) throws Exception {
    if (validationFails()) {
        throw new RuntimeException("Validation failed!");
    }
    return  doSomething()
}

Will everything still work when the validation fails?

Maybe I'm missing something though. My review is only based on the code in the PR.

@DavideD
Copy link
Contributor

DavideD commented Aug 3, 2022

Maybe I'm missing something though. My review is only based on the code in the PR.

In particular, I don't know when we have to deactivate or terminate the context and in which order these operations can run.

@mkouba
Copy link
Contributor Author

mkouba commented Aug 4, 2022

if the implementation of invokeBean looks something like this:

protected CompletionStage<Void> invokeBean(ScheduledExecution execution) throws Exception {
    if (validationFails()) {
        throw new RuntimeException("Validation failed!");
    }
    return  doSomething()
}

Will everything still work when the validation fails?

Maybe I'm missing something though. My review is only based on the code in the PR.

The actual (generated) implementation of the ScheduledInvoker for Jobs.everySecondUni() looks like:

public class NonBlockingScheduledMethodTest$_Jobs_ScheduledInvoker_everySecondUni_bc4773d4b48cf55d38fb70c74dd8ac9a99dcf2af
		extends
			DefaultInvoker {
	public CompletionStage invokeBean(final ScheduledExecution scheduledExecution) throws Exception {
		try {
			final ArcContainer container = Arc.container();
			return ((NonBlockingScheduledMethodTest$Jobs) container
					.instance(container.bean("43b9ee810358bfaef695a734f2ffc1709e5a4cfb")).get()).everySecondUni()
					.subscribeAsCompletionStage();
		} catch (Throwable ex) {
			return CompletableFuture.failedStage(ex);
		}
	}

	public boolean isBlocking() {
		return false;
	}
}

@mkouba
Copy link
Contributor Author

mkouba commented Aug 4, 2022

@manovotn pls could you review this one. Your knowledge of CDI and CP could be beneficial here.

@mkouba mkouba requested a review from manovotn August 4, 2022 07:11
@mkouba
Copy link
Contributor Author

mkouba commented Aug 4, 2022

Note that we don't need to terminate the context when an invokeBean() invocation results in an exception because the failure is wrapped in the generated invoker class with CompletableFuture.failedStage().

There's nothing in the javadoc of invokeBean that states that it should never throw an exception though. It actually declares throws Exception in the signature.

I've removed the Exception from the signature (probably a leftover). Also note that the DefaultInvoker is not part of the public API.

Copy link
Contributor

@manovotn manovotn left a comment

Choose a reason for hiding this comment

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

LGTM, this way the context should get destroyed correctly.

@mkouba mkouba added the triage/waiting-for-ci Ready to merge when CI successfully finishes label Aug 4, 2022
@mkouba mkouba added this to the 2.12 - main milestone Aug 4, 2022
@mkouba mkouba merged commit ac10fb9 into quarkusio:main Aug 4, 2022
@quarkus-bot quarkus-bot bot removed the triage/waiting-for-ci Ready to merge when CI successfully finishes label Aug 4, 2022
@DavideD
Copy link
Contributor

DavideD commented Aug 4, 2022

Nice, thanks!

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

Successfully merging this pull request may close these issues.

None yet

4 participants