Search code examples
sap-cloud-sdk

how to use spring webclient properly in the tenant context


I am using the Spring WebClient (org.springframework.web.reactive.function.client.WebClient) in order to implement some non-blocking behaviour.

        Mono<ClientResponse> clientResponse = 
            webClient
            .get(...).exchange();

        clientResponse.subscribe((response) -> {
             //--- unfortunately my tenant context is lost here !!! Someone knows the right solution?
        });

However my tenant-context gets lost once the response arrives (of course, we are in a new thread).

Here is the documentation of how to implement common asynchronous tasks within the tenant-context: https://sap.github.io/cloud-sdk/docs/java/features/multi-tenancy/multi-tenancy-thread-context/

However, I couldn't find the recommended way how to remain the tenant-context using Spring webflux (using the WebClient).


Solution

  • Edit: The SAP Cloud SDK destroys a ThreadContext at the end of an operation. This is done to prevent it from leaking e.g. when the thread is re-used.

    If:

    • the context is passed to an asynchronous operation
    • but the "parent" operation is done before the asynchronous operation starts

    the ThreadContext will not be available in the asynchronous operation. That is what is happening when using the WebClient.

    This is not true for custom properties, the SDK does not clear them. But that also means that you have to clear them yourself to ensure they don't leak when the thread is being re-used.

    I'd recommend not to use the multi tenancy features of the SDK in such cases. Still, I'll leave my below answer in for context.


    You can transfer the SAP Cloud SDK's ThreadContext to any new Thread if and only if you have control:

    • Before the new Thread is created
    • The operation that will be run asynchronously that requires the context
    • Edit: The parent thread (context) still exists

    I am not too familiar with WebFlux, but I'll assume that:

    • clientResponse.subscribe runs in the parent Thread
    • And the lambda is executed in the asynchronous Thread

    Given that, the following should transfer the ThreadContext to be present in the lambda:

    ThreadContextExecutor executor = new ThreadContextExecutor();
    
    clientResponse.subscribe((response) -> 
        executor.execute(() ->
           {
              // your code goes here, the ThreadContext should be available
           }
        )
    );
    
    

    You would have to do this at every point where an operation is running in a new Thread. As Artem pointed out, currently there is no feature in the SDK for this that would achieve the same automagically.


    I think it is also possible to define a custom ExecutorService that Spring will use to create and run new Threads. In a custom executor you could encapsulate this wrapping logic once for all operations.