ContextAwareCompletableFuture
Why Do We Need ContextAwareCompletableFuture?
Java provides asynchronous execution support via the CompletableFuture
API. However, when you execute code asynchronously using CompletableFuture.supplyAsync(...)
, the JVM spawns a new thread that does not inherit the context of the original (main) thread.
In the Navida Pro ecosystem, every backend request captures and stores:
- TenantContext – Identifies which tenant the request belongs to
- TraceId – A unique identifier to trace the full journey of a request across services
Unfortunately, when using default CompletableFuture
, this critical context information is lost in the new thread. This can:
- Break observability
- Disrupt request-specific logic
- Hinder audit logging and security validations
To resolve this, we introduced a custom wrapper: ContextAwareCompletableFuture
, which ensures that parent thread context is propagated to the async thread.
How to Use It
Instead of using Java’s built-in CompletableFuture
, simply switch to ContextAwareCompletableFuture
.
Without TenantContext and TraceId
public CompletableFuture<UserProfileSchemaDTO> getLastFailedActivity(
UserProfileSchemaDTO userProfileSchemaDTO, String token, String aokId, String accept) {
return CompletableFuture.supplyAsync(
() -> healthGoalService.getLastFailedTraining(token, aokId, accept));
}
In the above code, any downstream logic trying to access TenantContext or TraceId will receive null values.
With TenantContext and TraceId
public CompletableFuture<UserProfileSchemaDTO> getLastFailedActivity(
UserProfileSchemaDTO userProfileSchemaDTO, String token, String aokId, String accept) {
return ContextAwareCompletableFuture.supplyAsync(
() -> healthGoalService.getLastFailedTraining(token, aokId, accept));
}
Here, the context is automatically propagated to the new thread, ensuring traceability and tenant-specific logic work correctly.
Internals (How It Works)
ContextAwareCompletableFuture
wraps the task and:
- Captures the current context (like
TenantContextHolder
andTraceContext
) - Reattaches this context inside the new thread
- Executes your logic safely within the restored context
Best Practices
- Always use
ContextAwareCompletableFuture
for async code in Navida - Avoid mixing regular
CompletableFuture
and context-aware ones - Validate context availability in downstream service calls using logging or assertions
Summary
To maintain consistent traceability, security, and observability across threads, we strongly recommend using ContextAwareCompletableFuture
for all asynchronous processing in Navida backend services.