or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-utilities.mdcaching.mdcollections.mdconcurrency.mdgraph-api.mdhash-math.mdimmutable-collections.mdindex.mdio-utilities.mdother-utilities.md

caching.mddocs/

0

# Caching

1

2

Flexible in-memory caching with automatic loading, expiration, eviction, and comprehensive statistics. Guava's caching utilities provide a powerful and efficient way to store frequently accessed data.

3

4

## Package: com.google.common.cache

5

6

### Cache Basics

7

8

Simple cache for storing key-value pairs with manual population.

9

10

```java { .api }

11

import com.google.common.cache.Cache;

12

import com.google.common.cache.CacheBuilder;

13

import java.util.concurrent.TimeUnit;

14

15

// Basic cache creation

16

Cache<String, String> cache = CacheBuilder.newBuilder()

17

.maximumSize(1000)

18

.expireAfterWrite(10, TimeUnit.MINUTES)

19

.build();

20

21

// Manual cache operations

22

cache.put("key", "value");

23

String value = cache.getIfPresent("key"); // Returns null if not present

24

cache.invalidate("key"); // Remove specific entry

25

cache.invalidateAll(); // Remove all entries

26

27

// Bulk operations

28

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

29

entries.put("key1", "value1");

30

entries.put("key2", "value2");

31

cache.putAll(entries);

32

33

// Get all present values

34

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

35

Map<String, String> present = cache.getAllPresent(keys); // Only returns existing entries

36

37

// Cache statistics

38

CacheStats stats = cache.stats();

39

long hits = stats.hitCount();

40

long misses = stats.missCount();

41

double hitRate = stats.hitRate();

42

43

// Convert to ConcurrentMap view

44

ConcurrentMap<String, String> mapView = cache.asMap();

45

```

46

47

### LoadingCache

48

49

Cache that automatically loads values when they're not present using a CacheLoader.

50

51

```java { .api }

52

import com.google.common.cache.LoadingCache;

53

import com.google.common.cache.CacheLoader;

54

55

// Cache with automatic loading

56

LoadingCache<String, String> cache = CacheBuilder.newBuilder()

57

.maximumSize(1000)

58

.expireAfterAccess(30, TimeUnit.MINUTES)

59

.build(new CacheLoader<String, String>() {

60

@Override

61

public String load(String key) throws Exception {

62

return loadFromDatabase(key); // Your loading logic

63

}

64

65

@Override

66

public Map<String, String> loadAll(Iterable<? extends String> keys) throws Exception {

67

return loadMultipleFromDatabase(keys); // Optional bulk loading

68

}

69

});

70

71

// Automatic loading - loads if not present

72

String value = cache.get("key"); // Loads automatically, throws ExecutionException on load failure

73

String unchecked = cache.getUnchecked("key"); // Throws unchecked exception on load failure

74

75

// Bulk loading

76

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

77

ImmutableMap<String, String> values = cache.getAll(keys); // Uses loadAll if available

78

79

// Refresh (asynchronous reload)

80

cache.refresh("key"); // Triggers background reload, returns stale value immediately

81

82

// Manual override

83

cache.put("key", "manual-value"); // Override loaded value

84

```

85

86

### CacheBuilder Configuration

87

88

Comprehensive configuration options for cache behavior.

89

90

```java { .api }

91

import com.google.common.cache.CacheBuilder;

92

import com.google.common.cache.RemovalListener;

93

import com.google.common.cache.RemovalNotification;

94

import com.google.common.cache.Weigher;

95

96

// Size-based eviction

97

Cache<String, String> sizeCache = CacheBuilder.newBuilder()

98

.maximumSize(1000) // Maximum number of entries

99

.build();

100

101

// Weight-based eviction

102

Cache<String, List<String>> weightCache = CacheBuilder.newBuilder()

103

.maximumWeight(100000)

104

.weigher(new Weigher<String, List<String>>() {

105

@Override

106

public int weigh(String key, List<String> value) {

107

return key.length() + value.size() * 10; // Custom weight calculation

108

}

109

})

110

.build();

111

112

// Time-based expiration

113

Cache<String, String> timeCache = CacheBuilder.newBuilder()

114

.expireAfterWrite(10, TimeUnit.MINUTES) // Expire after write

115

.expireAfterAccess(5, TimeUnit.MINUTES) // Expire after last access

116

.refreshAfterWrite(1, TimeUnit.MINUTES) // Refresh after write (for LoadingCache)

117

.build();

118

119

// Reference-based eviction (memory-sensitive)

120

Cache<String, String> refCache = CacheBuilder.newBuilder()

121

.weakKeys() // Weak references for keys

122

.weakValues() // Weak references for values

123

.softValues() // Soft references for values (alternative to weak)

124

.build();

125

126

// Statistics collection

127

Cache<String, String> statsCache = CacheBuilder.newBuilder()

128

.recordStats() // Enable statistics collection

129

.build();

130

131

// Removal listener

132

Cache<String, String> listenedCache = CacheBuilder.newBuilder()

133

.removalListener(new RemovalListener<String, String>() {

134

@Override

135

public void onRemoval(RemovalNotification<String, String> notification) {

136

String key = notification.getKey();

137

String value = notification.getValue();

138

RemovalCause cause = notification.getCause();

139

System.out.println("Removed: " + key + " -> " + value + " (" + cause + ")");

140

}

141

})

142

.build();

143

144

// Combined configuration

145

Cache<String, ExpensiveObject> productionCache = CacheBuilder.newBuilder()

146

.maximumSize(10000)

147

.expireAfterWrite(30, TimeUnit.MINUTES)

148

.expireAfterAccess(10, TimeUnit.MINUTES)

149

.refreshAfterWrite(5, TimeUnit.MINUTES)

150

.recordStats()

151

.removalListener(loggingRemovalListener)

152

.build();

153

```

154

155

### Advanced CacheLoader Patterns

156

157

Sophisticated loading strategies for different use cases.

158

159

```java { .api }

160

import com.google.common.cache.CacheLoader;

161

import com.google.common.util.concurrent.Futures;

162

import com.google.common.util.concurrent.ListenableFuture;

163

import com.google.common.util.concurrent.ListeningExecutorService;

164

165

// Asynchronous loading

166

CacheLoader<String, String> asyncLoader = new CacheLoader<String, String>() {

167

private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(

168

Executors.newFixedThreadPool(10));

169

170

@Override

171

public String load(String key) throws Exception {

172

return loadSynchronously(key);

173

}

174

175

@Override

176

public ListenableFuture<String> reload(String key, String oldValue) {

177

return executor.submit(() -> {

178

try {

179

return loadSynchronously(key);

180

} catch (Exception e) {

181

return oldValue; // Return stale value on error

182

}

183

});

184

}

185

186

@Override

187

public Map<String, String> loadAll(Iterable<? extends String> keys) throws Exception {

188

// Efficient bulk loading

189

return batchLoadFromDatabase(keys);

190

}

191

};

192

193

// Fallback loading with error handling

194

CacheLoader<String, String> fallbackLoader = new CacheLoader<String, String>() {

195

@Override

196

public String load(String key) throws Exception {

197

try {

198

return primaryDataSource.load(key);

199

} catch (Exception e) {

200

// Fallback to secondary source

201

String fallback = secondaryDataSource.load(key);

202

if (fallback != null) {

203

return fallback;

204

}

205

throw e; // Re-throw if all sources fail

206

}

207

}

208

};

209

210

// Conditional loading based on key

211

CacheLoader<String, Object> conditionalLoader = new CacheLoader<String, Object>() {

212

@Override

213

public Object load(String key) throws Exception {

214

if (key.startsWith("user:")) {

215

return userService.loadUser(key.substring(5));

216

} else if (key.startsWith("config:")) {

217

return configService.loadConfig(key.substring(7));

218

} else {

219

throw new IllegalArgumentException("Unknown key type: " + key);

220

}

221

}

222

};

223

```

224

225

### Cache Statistics and Monitoring

226

227

Comprehensive statistics for cache performance monitoring.

228

229

```java { .api }

230

import com.google.common.cache.CacheStats;

231

232

// Enable statistics collection

233

LoadingCache<String, String> cache = CacheBuilder.newBuilder()

234

.recordStats()

235

.maximumSize(1000)

236

.build(cacheLoader);

237

238

// Access statistics

239

CacheStats stats = cache.stats();

240

241

// Hit/Miss statistics

242

long requestCount = stats.requestCount(); // Total requests

243

long hitCount = stats.hitCount(); // Cache hits

244

long missCount = stats.missCount(); // Cache misses

245

double hitRate = stats.hitRate(); // Hit rate (0.0 to 1.0)

246

double missRate = stats.missRate(); // Miss rate (0.0 to 1.0)

247

248

// Load statistics

249

long loadCount = stats.loadCount(); // Number of loads

250

double averageLoadPenalty = stats.averageLoadPenalty(); // Average load time in nanoseconds

251

long totalLoadTime = stats.totalLoadTime(); // Total time spent loading

252

253

// Eviction statistics

254

long evictionCount = stats.evictionCount(); // Number of evictions

255

256

// Error statistics (for LoadingCache)

257

long loadExceptionCount = stats.loadExceptionCount(); // Failed loads

258

double loadExceptionRate = stats.loadExceptionRate(); // Load failure rate

259

260

// Monitoring cache performance

261

public void monitorCache(LoadingCache<?, ?> cache) {

262

CacheStats stats = cache.stats();

263

264

if (stats.hitRate() < 0.8) {

265

System.out.println("Warning: Low hit rate - " + stats.hitRate());

266

}

267

268

if (stats.averageLoadPenalty() > TimeUnit.SECONDS.toNanos(1)) {

269

System.out.println("Warning: Slow loading - " + stats.averageLoadPenalty() + "ns");

270

}

271

272

if (stats.evictionCount() > stats.requestCount() * 0.1) {

273

System.out.println("Warning: High eviction rate");

274

}

275

}

276

```

277

278

### Removal Listeners and Cleanup

279

280

Handling cache entry removal for resource cleanup and monitoring.

281

282

```java { .api }

283

import com.google.common.cache.RemovalListener;

284

import com.google.common.cache.RemovalNotification;

285

import com.google.common.cache.RemovalCause;

286

287

// Cleanup removal listener

288

RemovalListener<String, DatabaseConnection> cleanupListener =

289

new RemovalListener<String, DatabaseConnection>() {

290

@Override

291

public void onRemoval(RemovalNotification<String, DatabaseConnection> notification) {

292

DatabaseConnection connection = notification.getValue();

293

if (connection != null) {

294

try {

295

connection.close(); // Cleanup resources

296

} catch (Exception e) {

297

logger.warn("Failed to close connection for key: " + notification.getKey(), e);

298

}

299

}

300

}

301

};

302

303

// Monitoring removal listener

304

RemovalListener<String, String> monitoringListener =

305

new RemovalListener<String, String>() {

306

@Override

307

public void onRemoval(RemovalNotification<String, String> notification) {

308

RemovalCause cause = notification.getCause();

309

310

switch (cause) {

311

case EXPLICIT:

312

// Manual removal via invalidate()

313

break;

314

case REPLACED:

315

// Value replaced with put()

316

break;

317

case COLLECTED:

318

// Garbage collected (weak/soft references)

319

break;

320

case EXPIRED:

321

// Expired based on time

322

break;

323

case SIZE:

324

// Evicted due to size constraints

325

metrics.incrementEvictionCounter();

326

break;

327

}

328

329

logger.debug("Cache entry removed: {} -> {} ({})",

330

notification.getKey(), notification.getValue(), cause);

331

}

332

};

333

334

// Asynchronous removal listener (for expensive cleanup)

335

RemovalListener<String, String> asyncListener = RemovalListeners.asynchronous(

336

expensiveCleanupListener,

337

Executors.newSingleThreadExecutor()

338

);

339

```

340

341

### Cache Patterns and Best Practices

342

343

Common patterns for effective cache usage.

344

345

```java { .api }

346

// Null value handling

347

LoadingCache<String, Optional<String>> nullSafeCache = CacheBuilder.newBuilder()

348

.build(new CacheLoader<String, Optional<String>>() {

349

@Override

350

public Optional<String> load(String key) throws Exception {

351

String value = database.get(key);

352

return Optional.fromNullable(value); // Wrap nulls in Optional

353

}

354

});

355

356

// Usage with null handling

357

Optional<String> result = nullSafeCache.get("key");

358

if (result.isPresent()) {

359

String value = result.get();

360

// Use value

361

}

362

363

// Refresh-ahead pattern

364

LoadingCache<String, String> refreshCache = CacheBuilder.newBuilder()

365

.refreshAfterWrite(5, TimeUnit.MINUTES) // Refresh after 5 minutes

366

.expireAfterWrite(10, TimeUnit.MINUTES) // Expire after 10 minutes

367

.build(new CacheLoader<String, String>() {

368

@Override

369

public String load(String key) throws Exception {

370

return expensiveComputation(key);

371

}

372

373

@Override

374

public ListenableFuture<String> reload(String key, String oldValue) {

375

// Asynchronous reload - returns old value immediately while loading new

376

return backgroundExecutor.submit(() -> expensiveComputation(key));

377

}

378

});

379

380

// Write-through cache pattern

381

public class WritethroughCache {

382

private final LoadingCache<String, String> cache;

383

private final Database database;

384

385

public Writethrough Cache(Database database) {

386

this.database = database;

387

this.cache = CacheBuilder.newBuilder()

388

.maximumSize(1000)

389

.build(new CacheLoader<String, String>() {

390

@Override

391

public String load(String key) throws Exception {

392

return database.get(key);

393

}

394

});

395

}

396

397

public String get(String key) throws ExecutionException {

398

return cache.get(key);

399

}

400

401

public void put(String key, String value) {

402

database.put(key, value); // Write to database first

403

cache.put(key, value); // Then update cache

404

}

405

406

public void remove(String key) {

407

database.remove(key); // Remove from database first

408

cache.invalidate(key); // Then remove from cache

409

}

410

}

411

412

// Multi-level cache

413

public class MultiLevelCache {

414

private final Cache<String, String> l1Cache; // Small, fast cache

415

private final LoadingCache<String, String> l2Cache; // Larger cache

416

417

public MultiLevelCache() {

418

this.l1Cache = CacheBuilder.newBuilder()

419

.maximumSize(100)

420

.expireAfterAccess(1, TimeUnit.MINUTES)

421

.build();

422

423

this.l2Cache = CacheBuilder.newBuilder()

424

.maximumSize(10000)

425

.expireAfterWrite(30, TimeUnit.MINUTES)

426

.build(new CacheLoader<String, String>() {

427

@Override

428

public String load(String key) throws Exception {

429

return database.get(key);

430

}

431

});

432

}

433

434

public String get(String key) throws ExecutionException {

435

// Try L1 cache first

436

String value = l1Cache.getIfPresent(key);

437

if (value != null) {

438

return value;

439

}

440

441

// Fall back to L2 cache

442

value = l2Cache.get(key);

443

l1Cache.put(key, value); // Promote to L1

444

return value;

445

}

446

}

447

```

448

449

### Cache Specification String

450

451

Configure caches using string specifications for external configuration.

452

453

```java { .api }

454

import com.google.common.cache.CacheBuilderSpec;

455

456

// Parse cache specification from string (useful for configuration files)

457

String spec = "maximumSize=1000,expireAfterWrite=30m,recordStats";

458

CacheBuilderSpec builderSpec = CacheBuilderSpec.parse(spec);

459

LoadingCache<String, String> cache = CacheBuilder.from(builderSpec)

460

.build(cacheLoader);

461

462

// Alternative: direct parsing

463

LoadingCache<String, String> cache2 = CacheBuilder.from("maximumSize=500,expireAfterAccess=10m")

464

.recordStats()

465

.build(cacheLoader);

466

467

// Specification format examples:

468

// "maximumSize=1000" - size limit

469

// "maximumWeight=50000" - weight limit

470

// "expireAfterWrite=30m" - expire 30 minutes after write

471

// "expireAfterAccess=1h" - expire 1 hour after access

472

// "refreshAfterWrite=5m" - refresh 5 minutes after write

473

// "weakKeys" - use weak references for keys

474

// "weakValues" - use weak references for values

475

// "softValues" - use soft references for values

476

// "recordStats" - enable statistics

477

```

478

479

### Testing Cache Behavior

480

481

Utilities and patterns for testing cache implementations.

482

483

```java { .api }

484

import com.google.common.testing.FakeTicker;

485

import java.util.concurrent.TimeUnit;

486

487

// Testing time-based expiration

488

public void testCacheExpiration() {

489

FakeTicker ticker = new FakeTicker();

490

491

LoadingCache<String, String> cache = CacheBuilder.newBuilder()

492

.expireAfterWrite(10, TimeUnit.MINUTES)

493

.ticker(ticker) // Use fake ticker for testing

494

.build(cacheLoader);

495

496

// Add entry

497

cache.put("key", "value");

498

assertEquals("value", cache.getIfPresent("key"));

499

500

// Advance time by 5 minutes

501

ticker.advance(5, TimeUnit.MINUTES);

502

assertEquals("value", cache.getIfPresent("key")); // Still present

503

504

// Advance time by another 6 minutes (total 11 minutes)

505

ticker.advance(6, TimeUnit.MINUTES);

506

assertNull(cache.getIfPresent("key")); // Should be expired

507

}

508

509

// Testing cache statistics

510

public void testCacheStats() {

511

LoadingCache<String, String> cache = CacheBuilder.newBuilder()

512

.recordStats()

513

.build(cacheLoader);

514

515

// Trigger cache operations

516

cache.get("key1"); // Miss + Load

517

cache.get("key1"); // Hit

518

cache.get("key2"); // Miss + Load

519

520

CacheStats stats = cache.stats();

521

assertEquals(3, stats.requestCount());

522

assertEquals(1, stats.hitCount());

523

assertEquals(2, stats.missCount());

524

assertEquals(2, stats.loadCount());

525

}

526

```

527

528

Guava's caching framework provides a robust, flexible solution for improving application performance through intelligent data caching with automatic loading, configurable expiration strategies, and comprehensive monitoring capabilities.