CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-grpc--grpc-context

Context propagation mechanism for Java applications that enables carrying scoped values across API boundaries and between threads

Pending
Overview
Eval results
Files

execution-utilities.mddocs/

Execution Utilities

Utilities for wrapping Runnables, Callables, and Executors to automatically propagate context across thread boundaries and asynchronous operations.

Capabilities

Direct Execution

Execute code immediately within a specific context scope. These methods handle attach/detach automatically.

/**
 * Immediately run a Runnable with this context as the current context.
 * @param r Runnable to run
 */
public void run(Runnable r);

/**
 * Immediately call a Callable with this context as the current context.
 * @param c Callable to call
 * @return Result of the callable
 * @throws Exception If the callable throws an exception
 */
public <V> V call(Callable<V> c) throws Exception;

Usage Examples:

Context.Key<String> USER_KEY = Context.key("user");
Context withUser = Context.current().withValue(USER_KEY, "alice");

// Run a task with the context
withUser.run(() -> {
    String user = USER_KEY.get(); // "alice"
    processUserRequest(user);
});

// Call a task that returns a value
String result = withUser.call(() -> {
    String user = USER_KEY.get(); // "alice"
    return fetchUserData(user);
});

// Exception handling with call()
try {
    Integer count = withUser.call(() -> {
        return performCalculation();
    });
} catch (Exception e) {
    System.out.println("Calculation failed: " + e.getMessage());
}

Runnable and Callable Wrapping

Wrap Runnables and Callables so they execute with a specific context, useful for passing tasks to other threads or executors.

/**
 * Wrap a Runnable so that it executes with this context as the current context.
 * @param r Runnable to wrap
 * @return Wrapped Runnable that propagates this context
 */
public Runnable wrap(Runnable r);

/**
 * Wrap a Callable so that it executes with this context as the current context.
 * @param c Callable to wrap  
 * @return Wrapped Callable that propagates this context
 */
public <C> Callable<C> wrap(Callable<C> c);

Usage Examples:

Context.Key<String> REQUEST_ID_KEY = Context.key("requestId");
Context withRequestId = Context.current().withValue(REQUEST_ID_KEY, "req-123");

ExecutorService executor = Executors.newFixedThreadPool(4);

// Wrap a Runnable for execution in another thread
Runnable task = withRequestId.wrap(() -> {
    String requestId = REQUEST_ID_KEY.get(); // "req-123" 
    processRequest(requestId);
});

// Submit to executor - context will be propagated
executor.submit(task);

// Wrap a Callable for execution in another thread
Callable<String> computation = withRequestId.wrap(() -> {
    String requestId = REQUEST_ID_KEY.get(); // "req-123"
    return performComputation(requestId);
});

// Submit and get result - context was propagated
Future<String> result = executor.submit(computation);
String computationResult = result.get();

Fixed Context Executor

Wrap an Executor so that all tasks submitted to it execute with a specific context. The context is fixed at creation time.

/**
 * Wrap an Executor so that it always executes with this context as the current context.
 * @param e Executor to wrap
 * @return Wrapped Executor that propagates this context for all tasks
 */
public Executor fixedContextExecutor(Executor e);

Usage Example:

Context.Key<String> TENANT_KEY = Context.key("tenant");
Context withTenant = Context.current().withValue(TENANT_KEY, "tenant-abc");

ExecutorService baseExecutor = Executors.newFixedThreadPool(4);

// Create executor that always uses the tenant context
Executor tenantExecutor = withTenant.fixedContextExecutor(baseExecutor);

// All tasks submitted to this executor will have the tenant context
tenantExecutor.execute(() -> {
    String tenant = TENANT_KEY.get(); // "tenant-abc"
    processTenantRequest(tenant);
});

tenantExecutor.execute(() -> {
    String tenant = TENANT_KEY.get(); // "tenant-abc" 
    performTenantMaintenance(tenant);
});

// Even if current context changes, the executor still uses the fixed context
Context.current().withValue(TENANT_KEY, "different-tenant").run(() -> {
    tenantExecutor.execute(() -> {
        String tenant = TENANT_KEY.get(); // Still "tenant-abc"!
        handleTenantData(tenant);
    });
});

Current Context Executor

Create an Executor that propagates whatever the current context is at the time each task is submitted. This is a static method that captures context dynamically.

/**
 * Create an executor that propagates the current context when execute() is called.
 * The context is captured at submission time, not creation time.
 * @param e Base executor to wrap
 * @return Wrapped Executor that propagates the current context for each task
 */
public static Executor currentContextExecutor(Executor e);

Usage Example:

Context.Key<String> USER_KEY = Context.key("user");
ExecutorService baseExecutor = Executors.newFixedThreadPool(4);

// Create executor that captures current context for each task
Executor contextExecutor = Context.currentContextExecutor(baseExecutor);

// Task submitted with user "alice" context
Context.current().withValue(USER_KEY, "alice").run(() -> {
    contextExecutor.execute(() -> {
        String user = USER_KEY.get(); // "alice"
        processUserTask(user);
    });
});

// Different task submitted with user "bob" context  
Context.current().withValue(USER_KEY, "bob").run(() -> {
    contextExecutor.execute(() -> {
        String user = USER_KEY.get(); // "bob"  
        processUserTask(user);
    });
});

// Tasks submitted without user context
contextExecutor.execute(() -> {
    String user = USER_KEY.get(); // null (no user in context)
    processGuestTask();
});

Context Propagation Patterns

Common patterns for propagating context across asynchronous operations.

CompletableFuture with Context:

Context.Key<String> TRACE_ID_KEY = Context.key("traceId");
Context withTrace = Context.current().withValue(TRACE_ID_KEY, "trace-456");

ExecutorService executor = Executors.newFixedThreadPool(4);
Executor contextExecutor = Context.currentContextExecutor(executor);

CompletableFuture<String> future = withTrace.call(() -> {
    return CompletableFuture
        .supplyAsync(() -> {
            String traceId = TRACE_ID_KEY.get(); // "trace-456"
            return fetchData(traceId);
        }, contextExecutor)
        .thenApplyAsync(data -> {
            String traceId = TRACE_ID_KEY.get(); // "trace-456" 
            return processData(data, traceId);
        }, contextExecutor);
});

Thread Pool with Shared Context:

Context.Key<String> SERVICE_KEY = Context.key("service");
Context serviceContext = Context.current().withValue(SERVICE_KEY, "payment-service");

ExecutorService pool = Executors.newFixedThreadPool(10);
Executor serviceExecutor = serviceContext.fixedContextExecutor(pool);

// All tasks in this pool will have service context
for (int i = 0; i < 100; i++) {
    final int taskId = i;
    serviceExecutor.execute(() -> {
        String service = SERVICE_KEY.get(); // "payment-service"
        processPaymentTask(taskId, service);
    });
}

Mixed Context Propagation:

Context.Key<String> REQUEST_KEY = Context.key("request");
Context.Key<String> SESSION_KEY = Context.key("session");

ExecutorService executor = Executors.newFixedThreadPool(4);
Executor currentContextExecutor = Context.currentContextExecutor(executor);

// Base context with session
Context sessionContext = Context.current().withValue(SESSION_KEY, "session-789");

sessionContext.run(() -> {
    // Each request gets its own context but inherits session
    for (int i = 0; i < 5; i++) {
        final String requestId = "req-" + i;
        
        Context.current().withValue(REQUEST_KEY, requestId).run(() -> {
            // This task will have both session and request context
            currentContextExecutor.execute(() -> {
                String session = SESSION_KEY.get(); // "session-789"
                String request = REQUEST_KEY.get();  // "req-0", "req-1", etc.
                handleRequest(request, session);
            });
        });
    }  
});

Install with Tessl CLI

npx tessl i tessl/maven-io-grpc--grpc-context

docs

cancellation-system.md

context-management.md

deadline-management.md

execution-utilities.md

index.md

tile.json