Skip to content

CDI context propagation #167

@FroMage

Description

@FroMage

I didn't see in the spec anywhere where we define how CDI should propagate its context with CP. In particular, the current implementation of CDI context propagation turns out to have confusing semantics, where it does not capture the request context entirely, but only the values currently added to the context.

My opinion was that it was obvious that when we want the request context to be captured, it has to be the entire request context (which is a mutable Map), which means being able to see all modifications to it that happened before. Not just the current values defined in the request context.

So it was implemented as capturing values currently active at this point, which is wrong IMO and pretty useless, unfortunately. So perhaps we need to clarify this in the spec?

To explain why we want to propagate the entire mutable context, here's an imperative example:

// nothing in the current request context
a(); // causes lazy beans to be added to the request context
b(); // can use the added request-context beans

It's pretty obvious that the request context (like the application context) is a mutable Map that gets mutated, and "happens-before" modifications are visible to later instructions. It's a context that follows the current request.

Now here's the same application in a reactive fashion:

// nothing in the current request context
return threadContext.withContextPropagation(CompletableFuture.completedFuture(null))
 .thenAccept(v-> a()) // causes lazy beans to be added to the request context
 .thenAccept(v -> b()); // can use the added request-context beans

To any reactive user, it's pretty obvious that this should work exactly the same as the imperative case. Except with the current implementation of CDI context propagation, the request context is captured before a() adds things to it, and restored empty so that b() has request-scoped beans vanish from its request scope.

In practice, this causes the request scope to not really be propagated in a reactive pipeline (since "happens-before" mutations are not always visible). It differs from the imperative code, and does not follow the principle of least surprise (beans vanishing from the request context and potentially later coming back to it are very hard to debug).

It also makes lazy request-scoped beans unusable with reactive applications, since to make them properly follow the request lifecycle we would require to eagerly load them all at the start of the request, which is very bad for performance.

Finally, note that this is not regular at all with the application scope, which is propagated entirely, with the right "happens-before" mutations visible.

I didn't think this would be something the spec should define, since it's CDI-specific, and I thought pretty obvious, but perhaps we can make this generic and specify that while context-propagation is ThreadContextProvider-specific, we encourage behaviour to follow the "happens-before" semantics for mutable contexts, so that reactive code behaves in the same way to imperative code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions