Propagate Sleuth baggage on parallel streams
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Spring Cloud Sleuth keeps trace context and baggage in thread-local state. That works well for normal request handling, but it breaks down once work jumps to threads that Sleuth is not instrumenting for you.
Why parallelStream() Loses Baggage
Sleuth documentation is direct about this: parallelStream() is not supported out of the box for context propagation. The reason is simple. Java parallel streams run work on the common fork-join pool, and Sleuth does not automatically wrap those tasks with the current trace context.
So if you start with baggage such as tenant-id or request-id on the request thread, code inside the parallel stream may see no baggage at all. Logging correlation, downstream headers, and custom tracing tags can all disappear.
Configure Baggage First
Before propagation matters, the baggage fields themselves must be registered:
That makes Sleuth treat tenant-id as propagated baggage and also copy it into logging correlation where supported.
Prefer an Instrumented Executor
Instead of parallelStream(), use CompletableFuture with a Sleuth-aware executor. Spring Cloud Sleuth provides wrappers such as TraceableExecutorService and LazyTraceExecutor for exactly this problem.
Example:
In this model, each asynchronous task is submitted through an executor that captures and restores the tracing context. That includes baggage.
Reading the Baggage
Inside the worker thread, you can access baggage through the tracer:
As long as the work was scheduled through a traced executor, the value is available where you need it.
Why Manual Copying Is Usually the Wrong Fix
You can manually capture a value before the stream starts and pass it into lambdas. That works for a single field:
But it does not solve the full tracing problem. The active span, MDC correlation, and any future baggage fields are still disconnected unless you copy everything yourself. That creates brittle code.
The better design is to treat context propagation as infrastructure, not business logic.
Migrating Forward
For modern Spring Boot applications, Micrometer Tracing and the Micrometer context propagation library are the long-term path. The underlying lesson stays the same: thread switches need explicit propagation support.
If you are still on Sleuth, the practical answer is:
- avoid
parallelStream()for traced work - use traced executors or
CompletableFuture - keep baggage registration explicit
Common Pitfalls
The most common mistake is assuming baggage works everywhere because it works in controller code. Request-thread success says nothing about later fork-join threads.
Another problem is configuring correlation fields but not remote fields, or the reverse. If baggage is not registered properly, even a correct executor setup will look broken.
Teams also forget executor boundaries hidden inside libraries. If a downstream component spins up work on its own unmanaged pool, context can still disappear.
Finally, do not treat parallelStream() as harmless syntactic sugar in tracing-heavy code. It is a concurrency decision with observability consequences.
Summary
- Sleuth does not propagate baggage through
parallelStream()automatically. - Baggage relies on thread-local trace context, so unmanaged thread hops lose it.
- Register baggage fields explicitly in configuration.
- Use
TraceableExecutorService,LazyTraceExecutor, or another traced executor for async work. - Prefer executor-based concurrency over
parallelStream()when trace context must survive.
Related reading
- Proper way to cache results TaskT with IMemoryCache
- Pros and cons of multi-leader vs leaderless replication in databases?
- proxy for distributed file share system in window
- pub/sub middleware with persistent storage
- Proper way of handling exception in task continuewith
- Properly catch boto3 Errors
- Pushing avro file to Kafka
- pymongo replication secondary readreference not work

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.