or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-keys-scoping.mdcontext-propagation.mdcore-context.mdexecutor-integration.mdfunction-wrapping.mdimplicit-context-values.mdindex.mdstorage-customization.md

executor-integration.mddocs/

0

# Executor Integration

1

2

Context executor integration provides automatic context propagation across asynchronous operations by wrapping Java executor services. This ensures that context values remain available in background threads and scheduled tasks.

3

4

## Static Executor Wrapping

5

6

Static methods wrap executors to automatically propagate the current context at the time of task submission.

7

8

### Executor Wrapping

9

10

Wraps an Executor to propagate the current context.

11

12

```java { .api }

13

static Executor taskWrapping(Executor executor);

14

```

15

16

**Parameters:**

17

- `executor` - The executor to wrap

18

19

**Returns:** An Executor that propagates current context to submitted tasks

20

21

**Usage Example:**

22

```java

23

// Create context-aware executor

24

Executor executor = Executors.newFixedThreadPool(4);

25

Executor contextExecutor = Context.taskWrapping(executor);

26

27

// Context at submission time is propagated

28

Context.current().with(USER_ID_KEY, "user123").makeCurrent();

29

30

contextExecutor.execute(() -> {

31

// This runs with the context from submission time

32

String userId = Context.current().get(USER_ID_KEY); // "user123"

33

processUser(userId);

34

});

35

```

36

37

### ExecutorService Wrapping

38

39

Wraps an ExecutorService to propagate current context to all submitted tasks.

40

41

```java { .api }

42

static ExecutorService taskWrapping(ExecutorService executorService);

43

```

44

45

**Parameters:**

46

- `executorService` - The executor service to wrap

47

48

**Returns:** An ExecutorService that propagates current context

49

50

**Usage Example:**

51

```java

52

ExecutorService executorService = Executors.newCachedThreadPool();

53

ExecutorService contextExecutorService = Context.taskWrapping(executorService);

54

55

Context.current().with(REQUEST_ID_KEY, "req-456").makeCurrent();

56

57

// Submit callable with context propagation

58

Future<String> future = contextExecutorService.submit(() -> {

59

String requestId = Context.current().get(REQUEST_ID_KEY); // "req-456"

60

return processRequest(requestId);

61

});

62

63

String result = future.get();

64

```

65

66

### ScheduledExecutorService Wrapping

67

68

Wraps a ScheduledExecutorService to propagate context to scheduled tasks.

69

70

```java { .api }

71

static ScheduledExecutorService taskWrapping(ScheduledExecutorService executorService);

72

```

73

74

**Parameters:**

75

- `executorService` - The scheduled executor service to wrap

76

77

**Returns:** A ScheduledExecutorService that propagates current context

78

79

**Usage Example:**

80

```java

81

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

82

ScheduledExecutorService contextScheduler = Context.taskWrapping(scheduler);

83

84

Context.current().with(JOB_ID_KEY, "job-789").makeCurrent();

85

86

// Schedule task with context propagation

87

ScheduledFuture<?> future = contextScheduler.schedule(() -> {

88

String jobId = Context.current().get(JOB_ID_KEY); // "job-789"

89

executeScheduledJob(jobId);

90

}, 5, TimeUnit.SECONDS);

91

```

92

93

**Note:** Context is NOT propagated for `scheduleAtFixedRate()` and `scheduleWithFixedDelay()` calls due to their repeated execution nature.

94

95

## Instance Executor Wrapping

96

97

Instance methods wrap executors to propagate a specific context rather than the current context.

98

99

### Instance Executor Wrapping

100

101

Wraps an Executor to propagate this specific context.

102

103

```java { .api }

104

Executor wrap(Executor executor);

105

```

106

107

**Usage Example:**

108

```java

109

// Create specific context

110

Context userContext = Context.current().with(USER_ID_KEY, "admin");

111

112

// Wrap executor with specific context

113

Executor executor = Executors.newSingleThreadExecutor();

114

Executor contextExecutor = userContext.wrap(executor);

115

116

// Tasks always run with the admin context, regardless of current context

117

Context.current().with(USER_ID_KEY, "guest").makeCurrent();

118

119

contextExecutor.execute(() -> {

120

String userId = Context.current().get(USER_ID_KEY); // "admin" (not "guest")

121

performAdminTask(userId);

122

});

123

```

124

125

### Instance ExecutorService Wrapping

126

127

Wraps an ExecutorService to propagate this specific context.

128

129

```java { .api }

130

ExecutorService wrap(ExecutorService executorService);

131

```

132

133

**Usage Example:**

134

```java

135

Context backgroundContext = Context.current()

136

.with(USER_ID_KEY, "system")

137

.with(OPERATION_KEY, "background");

138

139

ExecutorService executorService = Executors.newFixedThreadPool(4);

140

ExecutorService contextExecutorService = backgroundContext.wrap(executorService);

141

142

// All submissions use background context

143

List<Future<String>> futures = new ArrayList<>();

144

for (String task : tasks) {

145

Future<String> future = contextExecutorService.submit(() -> {

146

// Always runs with system user context

147

String userId = Context.current().get(USER_ID_KEY); // "system"

148

return processBackgroundTask(task);

149

});

150

futures.add(future);

151

}

152

```

153

154

### Instance ScheduledExecutorService Wrapping

155

156

Wraps a ScheduledExecutorService to propagate this specific context.

157

158

```java { .api }

159

ScheduledExecutorService wrap(ScheduledExecutorService executorService);

160

```

161

162

**Usage Example:**

163

```java

164

Context maintenanceContext = Context.current()

165

.with(USER_ID_KEY, "maintenance")

166

.with(OPERATION_KEY, "cleanup");

167

168

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

169

ScheduledExecutorService contextScheduler = maintenanceContext.wrap(scheduler);

170

171

// Schedule maintenance tasks with specific context

172

contextScheduler.scheduleAtFixedRate(() -> {

173

String userId = Context.current().get(USER_ID_KEY); // "maintenance"

174

performMaintenance();

175

}, 0, 1, TimeUnit.HOURS);

176

```

177

178

## Executor Patterns

179

180

### Mixed Context Usage

181

182

```java

183

public class TaskProcessor {

184

private final ExecutorService backgroundExecutor;

185

private final ExecutorService userExecutor;

186

187

public TaskProcessor() {

188

// Different executors for different contexts

189

ExecutorService baseExecutor = Executors.newFixedThreadPool(8);

190

191

// Background tasks run with system context

192

Context systemContext = Context.current().with(USER_ID_KEY, "system");

193

this.backgroundExecutor = systemContext.wrap(baseExecutor);

194

195

// User tasks propagate current context

196

this.userExecutor = Context.taskWrapping(baseExecutor);

197

}

198

199

public void processUserTask(Runnable task) {

200

// Uses current context

201

userExecutor.execute(task);

202

}

203

204

public void processBackgroundTask(Runnable task) {

205

// Always uses system context

206

backgroundExecutor.execute(task);

207

}

208

}

209

```

210

211

### Context-Aware Thread Pool

212

213

```java

214

public class ContextAwareService {

215

private final ExecutorService executor;

216

217

public ContextAwareService() {

218

ExecutorService baseExecutor = Executors.newWorkStealingPool();

219

this.executor = Context.taskWrapping(baseExecutor);

220

}

221

222

public CompletableFuture<String> processAsync(String input) {

223

// Context from calling thread is automatically propagated

224

return CompletableFuture.supplyAsync(() -> {

225

// Process with propagated context

226

String userId = Context.current().get(USER_ID_KEY);

227

return processWithUser(input, userId);

228

}, executor);

229

}

230

231

public void shutdown() {

232

executor.shutdown();

233

}

234

}

235

```

236

237

### Error Handling with Context Executors

238

239

```java

240

public void handleAsyncErrors() {

241

ExecutorService executor = Context.taskWrapping(Executors.newSingleThreadExecutor());

242

243

Context errorContext = Context.current().with(OPERATION_KEY, "error-prone");

244

245

try (Scope scope = errorContext.makeCurrent()) {

246

Future<String> future = executor.submit(() -> {

247

try {

248

// Context available in error handling

249

String operation = Context.current().get(OPERATION_KEY);

250

return performRiskyOperation();

251

} catch (Exception e) {

252

// Log with context information

253

String operation = Context.current().get(OPERATION_KEY);

254

logger.error("Operation {} failed", operation, e);

255

throw e;

256

}

257

});

258

259

try {

260

String result = future.get();

261

} catch (ExecutionException e) {

262

// Handle wrapped exception

263

handleError(e.getCause());

264

}

265

}

266

}

267

```

268

269

## Performance Considerations

270

271

- Wrapped executors have minimal overhead - they simply wrap submitted tasks

272

- Context propagation happens at task submission time, not execution time

273

- Avoid creating multiple wrappers around the same executor

274

- Reuse wrapped executors when possible

275

276

```java

277

// Good: Create wrapper once, reuse

278

private static final ExecutorService CONTEXT_EXECUTOR =

279

Context.taskWrapping(Executors.newFixedThreadPool(10));

280

281

// Avoid: Creating wrapper for each use

282

public void badPattern() {

283

ExecutorService wrapped = Context.taskWrapping(someExecutor); // Don't do this repeatedly

284

wrapped.execute(task);

285

}

286

```