or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asynchronous-caching.mdcache-construction.mdcache-policies.mdfunctional-interfaces.mdindex.mdstatistics.mdsynchronous-caching.md

asynchronous-caching.mddocs/

0

# Asynchronous Caching

1

2

Caffeine provides asynchronous cache interfaces that return `CompletableFuture` instances for non-blocking cache operations. The async interfaces enable high-throughput applications to perform cache operations without blocking threads.

3

4

## AsyncCache Interface

5

6

The `AsyncCache` interface provides asynchronous cache operations returning futures.

7

8

```java { .api }

9

public interface AsyncCache<K, V> {

10

// Retrieval operations

11

CompletableFuture<V> getIfPresent(K key);

12

CompletableFuture<V> get(K key, Function<? super K, ? extends V> mappingFunction);

13

CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor, CompletableFuture<V>> mappingFunction);

14

CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys, Function<Set<? extends K>, Map<K, V>> mappingFunction);

15

16

// Storage operations

17

void put(K key, CompletableFuture<V> valueFuture);

18

19

// Synchronous view

20

Cache<K, V> synchronous();

21

}

22

```

23

24

### Basic Async Operations

25

26

#### Asynchronous Retrieval

27

28

```java

29

AsyncCache<String, String> asyncCache = Caffeine.newBuilder()

30

.maximumSize(1000)

31

.buildAsync();

32

33

// Simple async retrieval - returns future of value or null

34

CompletableFuture<String> future1 = asyncCache.getIfPresent("key1");

35

String value = future1.join(); // null if not present

36

37

// Async get with compute function

38

CompletableFuture<String> future2 = asyncCache.get("key2", k -> {

39

// This computation runs asynchronously

40

return "computed_" + k;

41

});

42

43

// Async get with executor-aware compute function

44

CompletableFuture<String> future3 = asyncCache.get("key3", (key, executor) -> {

45

return CompletableFuture.supplyAsync(() -> {

46

// Long-running computation on provided executor

47

try {

48

Thread.sleep(1000);

49

} catch (InterruptedException e) {

50

Thread.currentThread().interrupt();

51

}

52

return "async_computed_" + key;

53

}, executor);

54

});

55

```

56

57

#### Asynchronous Storage

58

59

```java

60

// Store a completed future

61

asyncCache.put("immediate", CompletableFuture.completedFuture("immediate_value"));

62

63

// Store a future that completes later

64

CompletableFuture<String> laterFuture = CompletableFuture.supplyAsync(() -> {

65

// Simulate async computation

66

try {

67

Thread.sleep(500);

68

} catch (InterruptedException e) {

69

Thread.currentThread().interrupt();

70

}

71

return "delayed_value";

72

});

73

asyncCache.put("delayed", laterFuture);

74

```

75

76

#### Bulk Async Operations

77

78

```java

79

Set<String> keys = Set.of("key1", "key2", "key3");

80

81

// Bulk async retrieval with computation for missing values

82

CompletableFuture<Map<String, String>> bulkFuture = asyncCache.getAll(keys, missingKeys -> {

83

Map<String, String> result = new HashMap<>();

84

for (String key : missingKeys) {

85

result.put(key, "bulk_" + key);

86

}

87

return result;

88

});

89

90

Map<String, String> bulkResult = bulkFuture.join();

91

```

92

93

### Working with Futures

94

95

#### Chaining Operations

96

97

```java

98

AsyncCache<String, UserData> userCache = Caffeine.newBuilder()

99

.maximumSize(1000)

100

.buildAsync();

101

102

// Chain async operations

103

CompletableFuture<String> userNameFuture = userCache

104

.get("user123", userId -> fetchUserFromDatabase(userId))

105

.thenApply(userData -> userData.getName())

106

.thenApply(String::toUpperCase);

107

108

// Handle results asynchronously

109

userNameFuture.thenAccept(userName -> {

110

System.out.println("User name: " + userName);

111

}).exceptionally(throwable -> {

112

System.err.println("Failed to get user name: " + throwable.getMessage());

113

return null;

114

});

115

```

116

117

#### Combining Multiple Cache Operations

118

119

```java

120

CompletableFuture<String> user1Future = asyncCache.get("user1", this::loadUser);

121

CompletableFuture<String> user2Future = asyncCache.get("user2", this::loadUser);

122

123

// Combine results from multiple async cache operations

124

CompletableFuture<String> combinedFuture = user1Future.thenCombine(user2Future, (user1, user2) -> {

125

return "Combined: " + user1 + " + " + user2;

126

});

127

128

String combined = combinedFuture.join();

129

```

130

131

## AsyncLoadingCache Interface

132

133

The `AsyncLoadingCache` interface extends `AsyncCache` and provides automatic async loading.

134

135

```java { .api }

136

public interface AsyncLoadingCache<K, V> extends AsyncCache<K, V> {

137

// Automatic async loading

138

CompletableFuture<V> get(K key);

139

CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys);

140

141

// Synchronous view

142

LoadingCache<K, V> synchronous();

143

}

144

```

145

146

### Async Loading Operations

147

148

#### Creating Async Loading Cache with CacheLoader

149

150

```java

151

AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()

152

.maximumSize(1000)

153

.buildAsync(key -> {

154

// This CacheLoader runs on the default executor

155

Thread.sleep(100); // Simulate work

156

return "loaded_" + key;

157

});

158

159

// Get value - loads asynchronously if not present

160

CompletableFuture<String> loadedFuture = asyncLoadingCache.get("key1");

161

String loadedValue = loadedFuture.join();

162

```

163

164

#### Creating Async Loading Cache with AsyncCacheLoader

165

166

```java

167

AsyncLoadingCache<String, UserData> userAsyncCache = Caffeine.newBuilder()

168

.maximumSize(1000)

169

.buildAsync(new AsyncCacheLoader<String, UserData>() {

170

@Override

171

public CompletableFuture<UserData> asyncLoad(String userId, Executor executor) {

172

return CompletableFuture.supplyAsync(() -> {

173

// Async loading logic

174

return databaseService.fetchUser(userId);

175

}, executor);

176

}

177

178

@Override

179

public CompletableFuture<Map<String, UserData>> asyncLoadAll(

180

Set<? extends String> userIds, Executor executor) {

181

return CompletableFuture.supplyAsync(() -> {

182

// Efficient bulk async loading

183

return databaseService.fetchUsers(userIds);

184

}, executor);

185

}

186

});

187

188

// Efficient bulk async loading

189

CompletableFuture<Map<String, UserData>> usersFuture =

190

userAsyncCache.getAll(Set.of("user1", "user2", "user3"));

191

```

192

193

### Async Refresh Operations

194

195

```java

196

AsyncLoadingCache<String, String> refreshingAsyncCache = Caffeine.newBuilder()

197

.maximumSize(1000)

198

.refreshAfterWrite(Duration.ofMinutes(5))

199

.buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> {

200

// Async refresh computation

201

return fetchLatestValue(key);

202

}, executor));

203

204

// The cache will automatically refresh values in the background

205

CompletableFuture<String> valueFuture = refreshingAsyncCache.get("key1");

206

207

// Manual async refresh

208

CompletableFuture<String> refreshFuture = refreshingAsyncCache.synchronous().refresh("key1");

209

```

210

211

## Synchronous Views

212

213

Both async cache interfaces provide synchronous views for mixed usage patterns.

214

215

### AsyncCache Synchronous View

216

217

```java

218

AsyncCache<String, String> asyncCache = Caffeine.newBuilder()

219

.maximumSize(1000)

220

.buildAsync();

221

222

// Get synchronous view

223

Cache<String, String> syncView = asyncCache.synchronous();

224

225

// Use synchronous operations on the same underlying cache

226

asyncCache.put("async_key", CompletableFuture.completedFuture("async_value"));

227

String value = syncView.getIfPresent("async_key"); // "async_value"

228

229

syncView.put("sync_key", "sync_value");

230

CompletableFuture<String> asyncValue = asyncCache.getIfPresent("sync_key");

231

// Future completed with "sync_value"

232

```

233

234

### AsyncLoadingCache Synchronous View

235

236

```java

237

AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()

238

.maximumSize(1000)

239

.buildAsync(key -> "loaded_" + key);

240

241

// Get synchronous loading view

242

LoadingCache<String, String> syncLoadingView = asyncLoadingCache.synchronous();

243

244

// Mixed usage

245

CompletableFuture<String> asyncLoaded = asyncLoadingCache.get("async_key");

246

String syncLoaded = syncLoadingView.get("sync_key");

247

```

248

249

## Error Handling in Async Caches

250

251

### Handling Computation Failures

252

253

```java

254

AsyncCache<String, String> asyncCache = Caffeine.newBuilder()

255

.maximumSize(1000)

256

.buildAsync();

257

258

CompletableFuture<String> failingFuture = asyncCache.get("error_key", key -> {

259

if (key.equals("error_key")) {

260

throw new RuntimeException("Computation failed");

261

}

262

return "success_" + key;

263

});

264

265

// Handle failures

266

failingFuture

267

.thenApply(value -> "Processed: " + value)

268

.exceptionally(throwable -> {

269

System.err.println("Computation failed: " + throwable.getMessage());

270

return "fallback_value";

271

})

272

.thenAccept(result -> System.out.println("Final result: " + result));

273

```

274

275

### Async Loader Error Handling

276

277

```java

278

AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()

279

.maximumSize(1000)

280

.buildAsync((key, executor) -> {

281

return CompletableFuture.supplyAsync(() -> {

282

if (key.startsWith("fail_")) {

283

throw new RuntimeException("Loading failed for " + key);

284

}

285

return "loaded_" + key;

286

}, executor);

287

});

288

289

// Failed loads result in exceptional completion

290

CompletableFuture<String> result = asyncLoadingCache.get("fail_test");

291

result.whenComplete((value, throwable) -> {

292

if (throwable != null) {

293

System.err.println("Load failed: " + throwable.getMessage());

294

// Entry is not cached when loading fails

295

} else {

296

System.out.println("Loaded: " + value);

297

}

298

});

299

```

300

301

## Performance Considerations

302

303

### Executor Configuration

304

305

```java

306

// Configure custom executor for async operations

307

ForkJoinPool customPool = new ForkJoinPool(20);

308

309

AsyncCache<String, String> customExecutorCache = Caffeine.newBuilder()

310

.maximumSize(1000)

311

.executor(customPool)

312

.buildAsync();

313

314

// All async computations will use the custom executor

315

CompletableFuture<String> future = customExecutorCache.get("key", k -> expensiveComputation(k));

316

```

317

318

### Memory Usage

319

320

Async caches store `CompletableFuture` instances rather than direct values:

321

322

```java

323

// Memory overhead: each entry stores a CompletableFuture wrapper

324

AsyncCache<String, String> asyncCache = Caffeine.newBuilder()

325

.maximumSize(1000)

326

.buildAsync();

327

328

// For completed values, consider using synchronous cache if memory is constrained

329

Cache<String, String> syncCache = Caffeine.newBuilder()

330

.maximumSize(1000)

331

.build();

332

```

333

334

### Thread Safety

335

336

Async caches maintain the same thread-safety guarantees as synchronous caches:

337

338

```java

339

AsyncCache<String, String> threadSafeAsyncCache = Caffeine.newBuilder()

340

.maximumSize(1000)

341

.buildAsync();

342

343

// Multiple threads can safely access async cache concurrently

344

ExecutorService executor = Executors.newFixedThreadPool(10);

345

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

346

347

for (int i = 0; i < 100; i++) {

348

final int threadId = i;

349

CompletableFuture<String> future = threadSafeAsyncCache.get("key_" + threadId,

350

k -> "computed_" + k);

351

futures.add(future);

352

}

353

354

// Wait for all computations

355

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

356

```