Skip to content

Conversation

@ozangunalp
Copy link
Contributor

@ozangunalp ozangunalp commented Feb 10, 2025

Take 2 after #45827 has been reverted for flaky tests.

  • Changes to WorkerPoolRegistry implementation to make sure messages are dispatched on the correct context (duplicated context created as message context), this makes sure the regular emitters, when used correctly work with context propagation.
  • quarkus.messaging.connector-context-propagation runtime property to configure propagated context to connector channels, defaults to NONE.
  • quarkus.messaging.request-scoped.enabled build time property to enable request scoped context for consumed incoming messages, destroys state/deactivates the context once the message is (n)acked, defaults to false
  • ContextualEmitter, a Quarkus-specific emitter type for better handling the request-scoped context propagation.
  • Added messaging doc section on context propagation and channel decorators

Note on previous PR flaky tests and ContextualEmitter:

When context propagation is configured to clear (not propagate) the request-scoped (CDI) context, the currently active contexts are still available to the contextualized call, with their state erased. https://github.com/microprofile/microprofile-context-propagation/blob/aed4d96d81675a0502185e340ec42d88b8bf2339/api/src/main/java/org/eclipse/microprofile/context/ThreadContext.java#L326
Once the contextualized call returns, the intermediate state is destroyed.

When using internal channels with emitters to dispatch messages locally, the message is emitted asynchronously and dispatched on the duplicated message context. This duplicated context references a fresh CDI state.
The race condition is,

  • most of the times the contextualized call returns before the message is dispatched to the processor method, so the state is destroyed, and the request-scope is not active when the message is processed.
  • sometimes (rarely) the message is dispatched before the state is destroyed, so the message consumer method has an active albeit fresh request-scope context.

Tricks like @ActivateRequestScope create more confusion because sometimes there already is an active context, other times it creates a new one.

The ContextualEmitter makes sure the duplicated context is captured on the contextualized call, and it returns (destroying the state) before the message emit happens.
It ensures that, if the caller method doesn't propagate CDI context (ex. is annotated with @CurrentThreadContext(propagated = {})) the processing of the messaged dispatched by the emitter won't have an active CDI context.

WARNING: All this doesn't resolve issues like calls that propagate a request-scoped context to an emitter, that doesn't wait until the end of processing, destroying the context state before or during the locally dispatched message processing.


Fix #46256

@quarkus-bot

This comment has been minimized.

@github-actions
Copy link

github-actions bot commented Feb 10, 2025

🙈 The PR is closed and the preview is expired.

@quarkus-bot

This comment has been minimized.

@ozangunalp
Copy link
Contributor Author

@holly-cummins fyi the fix for #46047

@ozangunalp ozangunalp force-pushed the messaging_context_propagation_take2 branch from 688aebe to b062fd1 Compare February 20, 2025 10:52
@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

Copy link
Member

@cescoffier cescoffier left a comment

Choose a reason for hiding this comment

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

LGTM - a few minor comments

@ozangunalp ozangunalp force-pushed the messaging_context_propagation_take2 branch from b062fd1 to 4452e6f Compare February 24, 2025 11:38
@ozangunalp
Copy link
Contributor Author

Rebased and made changes for comments

@quarkus-bot
Copy link

quarkus-bot bot commented Feb 24, 2025

Status for workflow Quarkus Documentation CI

This is the status report for running Quarkus Documentation CI on commit 4452e6f.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

Warning

There are other workflow runs running, you probably need to wait for their status before merging.

@quarkus-bot
Copy link

quarkus-bot bot commented Feb 24, 2025

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit 4452e6f.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.


Flaky tests - Develocity

⚙️ JVM Tests - JDK 21

📦 extensions/micrometer/deployment

io.quarkus.micrometer.deployment.binder.VertxHttpClientMetricsTest.testWebClientMetrics - History

  • event executor terminated - java.util.concurrent.RejectedExecutionException
java.util.concurrent.RejectedExecutionException: event executor terminated
	at io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:934)
	at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:353)
	at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:346)
	at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:836)
	at io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java:827)
	at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
	at io.vertx.core.impl.EventLoopExecutor.execute(EventLoopExecutor.java:35)

@ozangunalp
Copy link
Contributor Author

@FroMage replying to #46256 (comment) here as it is more relevant :

@ozangunalp OK thanks for the explanation. I did notice recently that the CDI cleared context would start a new request context. I'm not sure how that's really helping any scenario, but well… it is what it is.

So here, it will start an empty context, and what happens is that it will also destroy it before your consumers have a chance to use it? This is not very useful. I wonder if we've really thought this use-case through in CP, @manovotn. This is not a scenario we've tested a lot, I think. I'm not sure how it composes, but it's possible that we're creating and destroying lots of empty contexts and perhaps we have some bits that capture these empty contexts but can't use them before they're destroyed.

As I linked in the description, creating an empty request context state for "cleared" is written in the spec: https://github.com/microprofile/microprofile-context-propagation/blob/aed4d96d81675a0502185e340ec42d88b8bf2339/api/src/main/java/org/eclipse/microprofile/context/ThreadContext.java#L326 and Arc request scopes are implemented that way.
This only happens with explicitly cleared/not propagated CDI context though.

it will start an empty context, and what happens is that it will also destroy it before your consumers have a chance to use it? This is not very useful.

It is helpful in the current state of things, and provides consistent behavior when using the ContextualEmitter with internal channels. When explicitly cleared, having a non-active request context is not that surprising.
Another option would be to clear manually the Vert.x context of any request context state. But that would not play well with context propagation APIs.
Note that using reactive messaging internal channels is a limited use case, mainly used to schedule tasks on different threads in the same application.

@FroMage
Copy link
Member

FroMage commented Feb 25, 2025

Well the spec doesn't say anything about creating a new context or when it's terminated. That's the bit that is problematic here.

@manovotn
Copy link
Contributor

As I linked in the description, creating an empty request context state for "cleared" is written in the spec: https://github.com/microprofile/microprofile-context-propagation/blob/aed4d96d81675a0502185e340ec42d88b8bf2339/api/src/main/java/org/eclipse/microprofile/context/ThreadContext.java#L326 and Arc request scopes are implemented that way.
This only happens with explicitly cleared/not propagated CDI context though.

You beat me to it.
There are also TCKs for it - note that while it may not seem so useful for req. contexts due to existence of @ActivateRequestContext, the spec was designed for CDI Full which also does this for session and conversation contexts that have no such annotation.

@ozangunalp
Copy link
Contributor Author

@ozangunalp ozangunalp requested a review from FroMage February 26, 2025 08:13
@ozangunalp
Copy link
Contributor Author

@FroMage do you think it is ok to merge?

@ozangunalp ozangunalp merged commit 4f815b9 into quarkusio:main Mar 18, 2025
57 checks passed
@quarkus-bot quarkus-bot bot added this to the 3.22 - main milestone Mar 18, 2025
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.

Incidental Context Propagation With In-Memory Reactive Messaging Channels

4 participants