From b7f9b510a35ed9fe6a7e9df06988788a8c836f37 Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Thu, 12 Mar 2026 23:29:29 +0530 Subject: [PATCH 01/11] Fix security plugin issue Signed-off-by: Deepti24 --- .../common/util/concurrent/ThreadContext.java | 19 +++++++- ...hreadContextBasedTracerContextStorage.java | 2 +- .../util/concurrent/ThreadContextTests.java | 43 +++++++++++++++++++ ...ContextBasedTracerContextStorageTests.java | 10 +++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 1d4406eac4458..333877b5381fc 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -293,10 +293,25 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio final ThreadContextStruct newContext = threadLocal.get(); return () -> { + ThreadContextStruct current = threadLocal.get(); + ThreadContextStruct enrichedOriginalContext = originalContext; + final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); + if (!propagated.isEmpty()) { + Map merged = new HashMap<>(originalContext.transientHeaders); + propagated.forEach(merged::putIfAbsent); + enrichedOriginalContext = new ThreadContextStruct( + enrichedOriginalContext.requestHeaders, + enrichedOriginalContext.responseHeaders, + merged, + enrichedOriginalContext.persistentHeaders, + enrichedOriginalContext.isSystemContext, + enrichedOriginalContext.warningHeadersSize + ); + } if (preserveResponseHeaders && threadLocal.get() != newContext) { - threadLocal.set(originalContext.putResponseHeaders(threadLocal.get().responseHeaders)); + threadLocal.set(enrichedOriginalContext.putResponseHeaders(threadLocal.get().responseHeaders)); } else { - threadLocal.set(originalContext); + threadLocal.set(enrichedOriginalContext); } }; } diff --git a/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java b/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java index 908164d1935a7..9f22d5c035a84 100644 --- a/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java +++ b/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java @@ -56,7 +56,7 @@ public Map transients(Map source) { final Map transients = new HashMap<>(); if (source.containsKey(CURRENT_SPAN)) { final SpanReference current = (SpanReference) source.get(CURRENT_SPAN); - if (current != null) { + if (current != null && current.getSpan() != null) { transients.put(CURRENT_SPAN, new SpanReference(current.getSpan())); } } diff --git a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java index 5992ffa1465b4..4d7e149faa043 100644 --- a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java +++ b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java @@ -851,4 +851,47 @@ protected void doRun() throws Exception { } }; } + + //We are simulating behavior that happens in Netty4HttpRequestHeaderVerifier + //It take a snapshot of state and stores in CONTEXT_TO_RESTORE and + // later tries to restore the same in SecurityFilter. Any transients added in between are lost + public void testPropagatedTransientsAreRestored() { + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + final String PROPAGATED_KEY = "test_propagated_transient"; + final Object PROPAGATED_VALUE = new Object(); + + // Register a propagator that declares PROPAGATED_KEY as a transient to carry across stashes. + threadContext.registerThreadContextStatePropagator(new ThreadContextStatePropagator() { + @Override + @SuppressWarnings("removal") + public Map transients(Map source) { + if (source.containsKey(PROPAGATED_KEY)) { + return Collections.singletonMap(PROPAGATED_KEY, source.get(PROPAGATED_KEY)); + } + return Collections.emptyMap(); + } + + @Override + @SuppressWarnings("removal") + public Map headers(Map source) { + return Collections.emptyMap(); + } + }); + + ThreadContext.StoredContext storedContext = null; + try(ThreadContext.StoredContext sc = threadContext.newStoredContext(false, Collections.emptyList())) { + //now we add something to original thread + // Simulate the tracing infrastructure writing CURRENT_SPAN into the stashed context. + storedContext = sc; + threadContext.putTransient(PROPAGATED_KEY, PROPAGATED_VALUE); + } + catch(Exception e) { + //unlikey to get exception, if we got one, test should fail + throw e; + } + //storedContext would have closed. Now we restore and after that, our original thread should have it + storedContext.restore(); + //we should be able to find the key now + assertEquals(threadContext.getTransient(PROPAGATED_KEY), PROPAGATED_VALUE); + } } diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java index 98dfc367c20f5..b4f602cf48d0f 100644 --- a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java +++ b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java @@ -22,7 +22,9 @@ import org.junit.After; import org.junit.Before; +import java.util.HashMap; import java.util.Optional; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -269,4 +271,12 @@ public void testSpanNotPropagatedToChildSystemThreadContext() { assertThat(threadContext.getTransient(ThreadContextBasedTracerContextStorage.CURRENT_SPAN), is(not(nullValue()))); assertThat(threadContextStorage.get(ThreadContextBasedTracerContextStorage.CURRENT_SPAN), is(nullValue())); } + + public void testNullSpanWithinSpanReference() { + SpanReference spanReference = new SpanReference(null); + Map source = new HashMap<>(); + source.put(ThreadContextBasedTracerContextStorage.CURRENT_SPAN, spanReference); + ThreadContextBasedTracerContextStorage context = (ThreadContextBasedTracerContextStorage) threadContextStorage; + assertTrue(context.transients(source).isEmpty()); + } } From bc5e24353acc6a4bf308aeec68ea9101183f8794 Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Fri, 13 Mar 2026 01:21:04 +0530 Subject: [PATCH 02/11] Fix checkstyle Signed-off-by: Deepti24 --- .../common/util/concurrent/ThreadContext.java | 4 ++-- .../util/concurrent/ThreadContextTests.java | 17 ++++++++--------- ...adContextBasedTracerContextStorageTests.java | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 333877b5381fc..0aa9f98ba2295 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -302,11 +302,11 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio enrichedOriginalContext = new ThreadContextStruct( enrichedOriginalContext.requestHeaders, enrichedOriginalContext.responseHeaders, - merged, + merged, enrichedOriginalContext.persistentHeaders, enrichedOriginalContext.isSystemContext, enrichedOriginalContext.warningHeadersSize - ); + ); } if (preserveResponseHeaders && threadLocal.get() != newContext) { threadLocal.set(enrichedOriginalContext.putResponseHeaders(threadLocal.get().responseHeaders)); diff --git a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java index 4d7e149faa043..454b8f52bdacb 100644 --- a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java +++ b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java @@ -852,8 +852,8 @@ protected void doRun() throws Exception { }; } - //We are simulating behavior that happens in Netty4HttpRequestHeaderVerifier - //It take a snapshot of state and stores in CONTEXT_TO_RESTORE and + // We are simulating behavior that happens in Netty4HttpRequestHeaderVerifier + // It take a snapshot of state and stores in CONTEXT_TO_RESTORE and // later tries to restore the same in SecurityFilter. Any transients added in between are lost public void testPropagatedTransientsAreRestored() { ThreadContext threadContext = new ThreadContext(Settings.EMPTY); @@ -879,19 +879,18 @@ public Map headers(Map source) { }); ThreadContext.StoredContext storedContext = null; - try(ThreadContext.StoredContext sc = threadContext.newStoredContext(false, Collections.emptyList())) { - //now we add something to original thread + try (ThreadContext.StoredContext sc = threadContext.newStoredContext(false, Collections.emptyList())) { + // now we add something to original thread // Simulate the tracing infrastructure writing CURRENT_SPAN into the stashed context. storedContext = sc; threadContext.putTransient(PROPAGATED_KEY, PROPAGATED_VALUE); - } - catch(Exception e) { - //unlikey to get exception, if we got one, test should fail + } catch (Exception e) { + // unlikey to get exception, if we got one, test should fail throw e; } - //storedContext would have closed. Now we restore and after that, our original thread should have it + // storedContext would have closed. Now we restore and after that, our original thread should have it storedContext.restore(); - //we should be able to find the key now + // we should be able to find the key now assertEquals(threadContext.getTransient(PROPAGATED_KEY), PROPAGATED_VALUE); } } diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java index b4f602cf48d0f..bc2975b97ec91 100644 --- a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java +++ b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java @@ -23,8 +23,8 @@ import org.junit.Before; import java.util.HashMap; -import java.util.Optional; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; From edc807da1d7cde8fdf76aeb2767f0dcaa502af57 Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Fri, 13 Mar 2026 01:31:43 +0530 Subject: [PATCH 03/11] Address review comments Signed-off-by: Deepti24 --- .../common/util/concurrent/ThreadContext.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 0aa9f98ba2295..2c868c4f6e45c 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -293,25 +293,31 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio final ThreadContextStruct newContext = threadLocal.get(); return () -> { + // Re-apply propagator-declared transients from the current context back into the + // snapshot being restored. This ensures that transients written after the snapshot + // was taken using newStoredContext (e.g. CURRENT_SPAN set by the tracing infrastructure) are not silently + // dropped when the security plugin (or any other caller) calls storedContext.restore(). + // Without this, restore() would blindly overwrite the threadLocal with the original + // snapshot, losing any propagated transients that were set after newStoredContext() was called. ThreadContextStruct current = threadLocal.get(); - ThreadContextStruct enrichedOriginalContext = originalContext; + ThreadContextStruct restoredContext = originalContext; final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); if (!propagated.isEmpty()) { Map merged = new HashMap<>(originalContext.transientHeaders); propagated.forEach(merged::putIfAbsent); - enrichedOriginalContext = new ThreadContextStruct( - enrichedOriginalContext.requestHeaders, - enrichedOriginalContext.responseHeaders, + restoredContext = new ThreadContextStruct( + restoredContext.requestHeaders, + restoredContext.responseHeaders, merged, - enrichedOriginalContext.persistentHeaders, - enrichedOriginalContext.isSystemContext, - enrichedOriginalContext.warningHeadersSize + restoredContext.persistentHeaders, + restoredContext.isSystemContext, + restoredContext.warningHeadersSize ); } if (preserveResponseHeaders && threadLocal.get() != newContext) { - threadLocal.set(enrichedOriginalContext.putResponseHeaders(threadLocal.get().responseHeaders)); + threadLocal.set(restoredContext.putResponseHeaders(threadLocal.get().responseHeaders)); } else { - threadLocal.set(enrichedOriginalContext); + threadLocal.set(restoredContext); } }; } From f47cebe0735ce2dbe9da20521e0397e12cead3a7 Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Fri, 13 Mar 2026 01:44:07 +0530 Subject: [PATCH 04/11] Update change log Signed-off-by: Deepti24 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e0190c725670..c4b7df8b6bd33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Delegate getMin/getMax methods for ExitableTerms ([#20775](https://github.com/opensearch-project/OpenSearch/pull/20775)) - Fix terms lookup subquery fetch limit reading from non-existent index setting instead of cluster `max_clause_count` ([#20823](https://github.com/opensearch-project/OpenSearch/pull/20823)) - Fix array_index_out_of_bounds_exception with wildcard and aggregations ([#20842](https://github.com/opensearch-project/OpenSearch/pull/20842)) +- Ensure that transient ThreadContext headers with propagators survive restore ([#169373](https://github.com/opensearch-project/OpenSearch/pull/20854)) ### Dependencies - Bump shadow-gradle-plugin from 8.3.9 to 9.3.1 ([#20569](https://github.com/opensearch-project/OpenSearch/pull/20569)) From b58616fb3342ec76df620ee828238ece5d5791ac Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Fri, 13 Mar 2026 18:36:42 +0530 Subject: [PATCH 05/11] Create method for adding transients Signed-off-by: Deepti24 --- .../common/util/concurrent/ThreadContext.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 2c868c4f6e45c..7c42936d371a5 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -303,16 +303,7 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio ThreadContextStruct restoredContext = originalContext; final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); if (!propagated.isEmpty()) { - Map merged = new HashMap<>(originalContext.transientHeaders); - propagated.forEach(merged::putIfAbsent); - restoredContext = new ThreadContextStruct( - restoredContext.requestHeaders, - restoredContext.responseHeaders, - merged, - restoredContext.persistentHeaders, - restoredContext.isSystemContext, - restoredContext.warningHeadersSize - ); + restoredContext = originalContext.putTransientIfAbsent(propagated); } if (preserveResponseHeaders && threadLocal.get() != newContext) { threadLocal.set(restoredContext.putResponseHeaders(threadLocal.get().responseHeaders)); @@ -885,6 +876,14 @@ private ThreadContextStruct putTransient(Map values) { return new ThreadContextStruct(requestHeaders, responseHeaders, newTransient, persistentHeaders, isSystemContext); } + private ThreadContextStruct putTransientIfAbsent(Map values) { + Map newTransient = new HashMap<>(this.transientHeaders); + for (Map.Entry entry : values.entrySet()) { + newTransient.putIfAbsent(entry.getKey(), entry.getValue()); + } + return new ThreadContextStruct(requestHeaders, responseHeaders, newTransient, persistentHeaders, isSystemContext); + } + private ThreadContextStruct putTransient(String key, Object value) { Map newTransient = new HashMap<>(this.transientHeaders); putSingleHeader(key, value, newTransient); From b1aa4db9579010253b6481d9c6b59830a0f06c5d Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Sat, 14 Mar 2026 18:35:24 +0530 Subject: [PATCH 06/11] Fix test cases Signed-off-by: Deepti24 --- .../common/util/concurrent/ThreadContext.java | 15 ++++++++++++--- .../util/concurrent/ThreadContextTests.java | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 7c42936d371a5..e9f2ab57cbb5a 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -252,6 +252,13 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders) { return newStoredContext(preserveResponseHeaders, Collections.emptyList()); } + public StoredContext newStoredContext(boolean preserveResponseHeaders, boolean preserveTransients) { + return newStoredContext(preserveResponseHeaders, Collections.emptyList(), preserveTransients); + } + + public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear) { + return newStoredContext(preserveResponseHeaders, transientHeadersToClear, false); + } /** * Just like {@link #stashContext()} but no default context is set. Instead, the {@code transientHeadersToClear} argument can be used * to clear specific transient headers in the new context. All headers (with the possible exception of {@code responseHeaders}) are @@ -259,7 +266,7 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders) { * * @param preserveResponseHeaders if set to true the response headers of the restore thread will be preserved. */ - public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear) { + public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear, boolean preserveTransients) { final ThreadContextStruct originalContext = threadLocal.get(); final Map newTransientHeaders = new HashMap<>(originalContext.transientHeaders); @@ -302,8 +309,10 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio ThreadContextStruct current = threadLocal.get(); ThreadContextStruct restoredContext = originalContext; final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); - if (!propagated.isEmpty()) { - restoredContext = originalContext.putTransientIfAbsent(propagated); + if(preserveTransients) { + if (!propagated.isEmpty()) { + restoredContext = originalContext.putTransientIfAbsent(propagated); + } } if (preserveResponseHeaders && threadLocal.get() != newContext) { threadLocal.set(restoredContext.putResponseHeaders(threadLocal.get().responseHeaders)); diff --git a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java index 454b8f52bdacb..f995e4f94a965 100644 --- a/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java +++ b/server/src/test/java/org/opensearch/common/util/concurrent/ThreadContextTests.java @@ -879,7 +879,7 @@ public Map headers(Map source) { }); ThreadContext.StoredContext storedContext = null; - try (ThreadContext.StoredContext sc = threadContext.newStoredContext(false, Collections.emptyList())) { + try (ThreadContext.StoredContext sc = threadContext.newStoredContext(false, true)) { // now we add something to original thread // Simulate the tracing infrastructure writing CURRENT_SPAN into the stashed context. storedContext = sc; From 765bfafae945fabd593b57795779c6375e16a64a Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Sat, 14 Mar 2026 18:36:51 +0530 Subject: [PATCH 07/11] checkstyle Signed-off-by: Deepti24 --- .../opensearch/common/util/concurrent/ThreadContext.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index e9f2ab57cbb5a..b83ec1eccd0ce 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -259,6 +259,7 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, boolean p public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear) { return newStoredContext(preserveResponseHeaders, transientHeadersToClear, false); } + /** * Just like {@link #stashContext()} but no default context is set. Instead, the {@code transientHeadersToClear} argument can be used * to clear specific transient headers in the new context. All headers (with the possible exception of {@code responseHeaders}) are @@ -266,7 +267,11 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio * * @param preserveResponseHeaders if set to true the response headers of the restore thread will be preserved. */ - public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear, boolean preserveTransients) { + public StoredContext newStoredContext( + boolean preserveResponseHeaders, + Collection transientHeadersToClear, + boolean preserveTransients + ) { final ThreadContextStruct originalContext = threadLocal.get(); final Map newTransientHeaders = new HashMap<>(originalContext.transientHeaders); @@ -309,7 +314,7 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio ThreadContextStruct current = threadLocal.get(); ThreadContextStruct restoredContext = originalContext; final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); - if(preserveTransients) { + if (preserveTransients) { if (!propagated.isEmpty()) { restoredContext = originalContext.putTransientIfAbsent(propagated); } From 17a4c730d2cb8cae0cc8e6699351ca523b7f6ebc Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Sat, 14 Mar 2026 18:49:53 +0530 Subject: [PATCH 08/11] minor refactor Signed-off-by: Deepti24 --- .../org/opensearch/common/util/concurrent/ThreadContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index b83ec1eccd0ce..70d7a0fafb1ea 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -313,8 +313,8 @@ public StoredContext newStoredContext( // snapshot, losing any propagated transients that were set after newStoredContext() was called. ThreadContextStruct current = threadLocal.get(); ThreadContextStruct restoredContext = originalContext; - final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); if (preserveTransients) { + final Map propagated = propagateTransients(current.transientHeaders, current.isSystemContext); if (!propagated.isEmpty()) { restoredContext = originalContext.putTransientIfAbsent(propagated); } From d1174844588e309f2a3baf048d142b042977ac40 Mon Sep 17 00:00:00 2001 From: Deepti24 Date: Mon, 16 Mar 2026 23:48:09 +0530 Subject: [PATCH 09/11] change order of boolean Signed-off-by: Deepti24 --- .../opensearch/common/util/concurrent/ThreadContext.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java index 70d7a0fafb1ea..ce1807320d0fa 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java @@ -253,11 +253,11 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders) { } public StoredContext newStoredContext(boolean preserveResponseHeaders, boolean preserveTransients) { - return newStoredContext(preserveResponseHeaders, Collections.emptyList(), preserveTransients); + return newStoredContext(preserveResponseHeaders, preserveTransients, Collections.emptyList()); } public StoredContext newStoredContext(boolean preserveResponseHeaders, Collection transientHeadersToClear) { - return newStoredContext(preserveResponseHeaders, transientHeadersToClear, false); + return newStoredContext(preserveResponseHeaders, false, transientHeadersToClear); } /** @@ -269,8 +269,8 @@ public StoredContext newStoredContext(boolean preserveResponseHeaders, Collectio */ public StoredContext newStoredContext( boolean preserveResponseHeaders, - Collection transientHeadersToClear, - boolean preserveTransients + boolean preserveTransients, + Collection transientHeadersToClear ) { final ThreadContextStruct originalContext = threadLocal.get(); final Map newTransientHeaders = new HashMap<>(originalContext.transientHeaders); From d86ab970c4c099960f090c03e3c7b3056d2c583c Mon Sep 17 00:00:00 2001 From: Deepti Chauhan Date: Thu, 19 Mar 2026 17:50:55 +0530 Subject: [PATCH 10/11] Adding test case Signed-off-by: Deepti Chauhan --- .../ThreadContextBasedTracerContextStorageTests.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java index bc2975b97ec91..ca066aaf330f1 100644 --- a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java +++ b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java @@ -273,10 +273,22 @@ public void testSpanNotPropagatedToChildSystemThreadContext() { } public void testNullSpanWithinSpanReference() { + // invalid span, should not be present in final transients SpanReference spanReference = new SpanReference(null); Map source = new HashMap<>(); source.put(ThreadContextBasedTracerContextStorage.CURRENT_SPAN, spanReference); ThreadContextBasedTracerContextStorage context = (ThreadContextBasedTracerContextStorage) threadContextStorage; assertTrue(context.transients(source).isEmpty()); + + // valid span, present in final transients + final Span span = tracer.startSpan(SpanCreationContext.internal().name("test")); + spanReference = new SpanReference(span); + source = new HashMap<>(); + source.put(ThreadContextBasedTracerContextStorage.CURRENT_SPAN, spanReference); + assertFalse(context.transients(source).isEmpty()); + assertEquals( + span, + ((SpanReference) context.transients(source).get(ThreadContextBasedTracerContextStorage.CURRENT_SPAN)).getSpan() + ); } } From b2d5b5e7ea2f088bcb774c95d0334687c71bcb58 Mon Sep 17 00:00:00 2001 From: Deepti Chauhan Date: Thu, 19 Mar 2026 18:59:38 +0530 Subject: [PATCH 11/11] Fix checklstyle Signed-off-by: Deepti Chauhan --- .../tracing/ThreadContextBasedTracerContextStorageTests.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java index ca066aaf330f1..6a5f25cc90139 100644 --- a/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java +++ b/server/src/test/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorageTests.java @@ -286,9 +286,6 @@ public void testNullSpanWithinSpanReference() { source = new HashMap<>(); source.put(ThreadContextBasedTracerContextStorage.CURRENT_SPAN, spanReference); assertFalse(context.transients(source).isEmpty()); - assertEquals( - span, - ((SpanReference) context.transients(source).get(ThreadContextBasedTracerContextStorage.CURRENT_SPAN)).getSpan() - ); + assertEquals(span, ((SpanReference) context.transients(source).get(ThreadContextBasedTracerContextStorage.CURRENT_SPAN)).getSpan()); } }