question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pass Threadlocal across "@Bulkhead" annotation

See original GitHub issue

Hi team,

We are trying to migrate from Hystrix to Resiliency4j. We have context value set in ThreadLocal. Whenever we go across @Bulkhead annotation the ThreadLocal is empty

    @Bulkhead(name = "backendA", type = Type.THREADPOOL)
    public CompletableFuture<String> futureSuccessReqCtx() { 
        //empty thread local here
    }

In Hystrix we were copying thread local using HystrixConcurrencyStrategy . Do we have anything equivalent in Resiliency4j. If not, please suggest any other approach.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:29 (20 by maintainers)

github_iconTop GitHub Comments

3reactions
adrilocommented, Sep 19, 2019

Hi @RobWin,

I actually found a way to pass context to the threads without touching the lib:

// somewhere we define a CorrelationId class, which contains a ThreadLocal value, and some getters/setters

public int test() {
    ThreadPoolBulkhead bulkhead = threadPoolBulkheadRegistry.bulkhead("threadlocal");
    return bulkhead
        .executeSupplier(propagateCorrelationId(() -> {
            assertTrue(CorrelationId.isInitialized());
            return 1;
        }))
        .toCompletableFuture()
        .join();
}

private <T> Supplier<T> propagateCorrelationId(Supplier<T> supplier) {
        UUID correlationId = CorrelationId.get();

        return () -> {
            try {
                CorrelationId.initWith(correlationId);
                return supplier.get();
            } finally {
                // cleanup
                CorrelationId.clear();
            }
        };
    }

This can obviously be extended to pass more context to the executing thread.

Given that we can achieve this result without messing with Resilience4j, I’m now wondering how useful it would be to add such a feature directly in the lib. But if you think it’s still a good idea, it could work this way:

Developers can register ContextPropagators via the BulkheadConfig. A ContextPropagator provides 3 things:

  • a supplier to fetch a value
  • a callback to call before the execution in the “new” thread
  • a callback to call after the execution in the “new” thread

Or translated in code:

public class ContextPropagator<T> {
    private Supplier<Optional<T>> currentThreadSupplier;
    private Consumer<T> before;
    private Consumer<T> after;

Then in the implementation of FixedThreadPoolBulkhead::submit, I would change it to something like that:

	public <T> CompletableFuture<T> submit(Callable<T> callable) {
		final CompletableFuture<T> promise = new CompletableFuture<>();
                // ADDED:
               var threadContexts = config.getContextPropagators()
                                           .stream()
                                           .map(ContextPropagator::captureThreadContext)
                                           .collect(Collectors.toList());

		try {
			CompletableFuture.supplyAsync(() -> {
				try {
					// ADDED:
					threadContexts.forEach(ContextPropagator.CapturedThreadContext::beforeRun);
					publishBulkheadEvent(() -> new BulkheadOnCallPermittedEvent(name));
					return callable.call();
				} catch (Exception e) {
					throw new CompletionException(e);
				} finally {
                                    // ADDED:
                                   threadContexts.forEach(ContextPropagator.CapturedThreadContext::afterRun);
                                }
			}, executorService).whenComplete((result, throwable) -> {
				...
			});
		} catch (RejectedExecutionException rejected) {
			...
		}
		return promise;
	}

This is just a rough idea on how this would work. If that’s interesting, the API must be worked on carefully, as the config now provides hooks and is no longer a value store.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implementing Bulkhead with Resilience4j - Reflectoring
Sometimes we store data in a ThreadLocal variable and read it in a different area of the code. We do this to avoid...
Read more >
java - ThreadLocal value access across different threads
currentThread() , but you can pass in other threads if you wish. However, this likely isn't a great idea. For one, holding a...
Read more >
Home of Quarkus Cheat-Sheet - GitHub Pages
When a request cannot be processed BulkheadException is thrown. It can be used together with any other fault tolerance annotation. @Bulkhead(5) @Retry ...
Read more >
Rest Client for MicroProfile
This simple API exposes one API call, located at /greet from the base URL of the client. Invoking this endpoint returns a javax.ws.rs.core....
Read more >
An Introduction to ThreadLocal in Java - Baeldung
In this tutorial, we'll be looking at the ThreadLocal construct from the java.lang package. This gives us the ability to store data ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found