# ThreadContext API

**URL:** https://discuss.elastic.co/t/threadcontext-api/173694
**Category:** Elasticsearch
**Created:** [March 25, 2019, 9:00am UTC](https://discuss.elastic.co/t/threadcontext-api/173694 "2019-03-25T09:00:41Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Simone\_Scarduzio](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/simone_scarduzio/32/17128_2.png) [@Simone\_Scarduzio](https://discuss.elastic.co/u/Simone_Scarduzio)
#### Post date: [March 25, 2019, 9:00am UTC](https://discuss.elastic.co/t/threadcontext-api/173694/1 "2019-03-25T09:00:42Z")

</div>

Hi, this is a follow up question from [https://github.com/elastic/elasticsearch/issues/40391](https://github.com/elastic/elasticsearch/issues/40391).

I'm struggling to understand how to properly use the org.elasticsearch.common.util.concurrent.ThreadContext API.

The javadoc says:

```java
/**
 * A ThreadContext is a map of string headers and a transient map of keyed objects that are associated with
 * a thread. It allows to store and retrieve header information across method calls, network calls as well as threads spawned from a
 * thread that has a {@link ThreadContext} associated with. Threads spawned from a {@link org.elasticsearch.threadpool.ThreadPool}
 * have out of the box support for {@link ThreadContext} and all threads spawned will inherit the {@link ThreadContext} from the thread
 * that it is forking from.". Network calls will also preserve the senders headers automatically.
 * <p>
 * Consumers of ThreadContext usually don't need to interact with adding or stashing contexts. Every elasticsearch thread is managed by
 * a thread pool or executor being responsible for stashing and restoring the threads context. For instance if a network request is
 * received, all headers are deserialized from the network and directly added as the headers of the threads {@link ThreadContext}
 * (see {@link #readHeaders(StreamInput)}. In order to not modify the context that is currently active on this thread the network code
 * uses a try/with pattern to stash it's current context, read headers into a fresh one and once the request is handled or a handler thread
 * is forked (which in turn inherits the context) it restores the previous context. For instance:
 * </p>
 * <pre>
 * // current context is stashed and replaced with a default context
 * try (StoredContext context = threadContext.stashContext()) {
 * threadContext.readHeaders(in); // read headers into current context
 * if (fork) {
 * threadPool.execute(() -&gt; request.handle()); // inherits context
 * } else {
 * request.handle();
 * }
 * }
 * // previous context is restored on StoredContext#close()
 * </pre>
 *
 */

```

In the example given, stashing the current context and auto-restoring it in the automatic `close()` called at the end of the try might be useful to avoid the `IllegalArgumentException: value for key [test-transient] already present` described in [https://github.com/elastic/elasticsearch/issues/40391](https://github.com/elastic/elasticsearch/issues/40391)

However, it won't cover the main use case of when I want to propagate a value across different threads while they are handling the same request. I.e.

- Setting a header in my ActionFilter
- Recovering the header value from my IndexSearchWrapper

What's the best/simplest way to achieve this?

---

<div class="post-metadata">

### Author: ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)
#### Post date: [March 25, 2019, 11:53am UTC](https://discuss.elastic.co/t/threadcontext-api/173694/2 "2019-03-25T11:53:29Z")

</div>

> [@Simone\_Scarduzio](#):
>
> I want to propagate a value across different threads while they are handling the same request. I.e.
> 
> - Setting a header in my ActionFilter
> - Recovering the header value from my IndexSearchWrapper
> 
> What's the best/simplest way to achieve this?

Could you explain a bit more what you've tried and in what way it's not working? Looking at, for instance, `SecurityActionFilter`, it looks like values do propagate across different threads (and, indeed, across the network) when handling the same request.

---

<div class="post-metadata">

### Author: ![Simone\_Scarduzio](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/simone_scarduzio/32/17128_2.png) [@Simone\_Scarduzio](https://discuss.elastic.co/u/Simone_Scarduzio)
#### Post date: [March 25, 2019, 4:02pm UTC](https://discuss.elastic.co/t/threadcontext-api/173694/3 "2019-03-25T16:02:25Z")

</div>

Hi David, thanks for replying. As I said, I need to write a header (or a transient) in my implementation of ActionFilter, and read it in my implementation IndexSearcherWrapper.

I tried wrapping in two try statements similar as the one seen in the documentation both the code that writes the header (in the ActionFilter) and reads it (in the IndexSearchWrapper) without luck.

Here is how:

In my ActionFilter:

```java
 @Override
  public <Request extends ActionRequest, Response extends ActionResponse> void apply(
      Task task,
      String action,
      Request request,
      ActionListener<Response> listener,
      ActionFilterChain<Request, Response> chain) {

    try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) {
          threadPool.getThreadContext().putTransient("key","Value");
    }
    ...
    chain.proceed(task, action, request, listener);
  
}

```

In my IndexSearchWrapper:

```java
  @Override
  protected DirectoryReader wrap(DirectoryReader reader) throws IOException {
    try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) {
      System.out.println("TRANSIENT PARAM: >>>>>>>>>>>> " + threadPool.getThreadContext().getTransient("key"));
    }
  }

```

The result of this is

```auto
TRANSIENT PARAM: >>>>>>>>>>>> null

```

There must be something obvious I'm missing ☹

---

<div class="post-metadata">

### Author: ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)
#### Post date: [March 25, 2019, 4:13pm UTC](https://discuss.elastic.co/t/threadcontext-api/173694/4 "2019-03-25T16:13:52Z")

</div>

> [@Simone\_Scarduzio](#):
>
> ```auto
> try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) {
> threadPool.getThreadContext().putTransient("key","Value");
> }
> ...
> chain.proceed(task, action, request, listener);
> 
> ```

This seems wrong. You're creating a context, putting a value into it, then discarding the context before proceeding. I think you should be calling `chain.proceed` within the `try` block.

> [@Simone\_Scarduzio](#):
>
> ```auto
> @Override
> protected DirectoryReader wrap(DirectoryReader reader) throws IOException {
> try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) {
> System.out.println("TRANSIENT PARAM: >>>>>>>>>>>> " + threadPool.getThreadContext().getTransient("key"));
> }
> }
> 
> ```

This also seems wrong. You are calling `stashContext()` which temporarily hides the current context and starts again with a brand new, empty, one. I think you should not be calling `.stashContext()` here.

Also are you sure you want to use `.putTransient` and not `.putHeader`? The former is node-local whereas the latter preserves the context across nodes.

---

<div class="post-metadata">

### Author: ![Simone\_Scarduzio](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/simone_scarduzio/32/17128_2.png) [@Simone\_Scarduzio](https://discuss.elastic.co/u/Simone_Scarduzio)
#### Post date: [March 25, 2019, 5:15pm UTC](https://discuss.elastic.co/t/threadcontext-api/173694/5 "2019-03-25T17:15:15Z")

</div>

Hi @David,

Thanks for the feedback! I switched to wrapping only the part that sets the header, and now it seems to work ok in my dummy plugin.

However, the problem arises in my actual plugin code where it throws the same "value for key already present" exception. I think it's because I'm doing network calls using CompletableFuture (which defaults to ForkJoinPool, so it's another threadpool, and it may be a problem 🤦🏻‍♂️

I will try supplying the threadPool.executor to it and report back.

---

<div class="post-metadata">

### Author: ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)
#### Post date: [March 25, 2019, 7:11pm UTC](https://discuss.elastic.co/t/threadcontext-api/173694/6 "2019-03-25T19:11:12Z")

</div>

If Elasticsearch is complaining that you're trying to set a key twice then I suspect you are not properly processing each request in its own context. Each one needs a separate one of these:

```auto
    try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) {
        ...
    }

```

It's not easy to give much more help without seeing more code, but hopefully now you've got something working you can start to bisect your way to what you want.

> [@Simone\_Scarduzio](#):
>
> I'm doing network calls using CompletableFuture

Yes, perhaps you want to use Elasticsearch's thread pools. You should also be wary of `CompletableFuture` because it's easy to swallow fatal exceptions like `AssertionError`, `StackOverflowError` or `OutOfMemoryError` when using them. Elasticsearch provides safer alternatives like `PlainActionFuture` and `PlainListenableActionFuture` that are usually sufficient.

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)
#### Post date: [April 22, 2019, 7:11pm UTC](https://discuss.elastic.co/t/threadcontext-api/173694/7 "2019-04-22T19:11:17Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
