# How to Configure TTL with Jitter Using the ttlAmplitude Parameter in @CoCache

> Configure TTL with jitter in @CoCache using ttlAmplitude. Prevent cache stampedes by distributing expirations across a dynamic interval.

- Repository: [Ahoo Wang/cocache](https://github.com/ahoo-wang/cocache)
- Tags: how-to-guide
- Published: 2026-02-23

---

**Use the `ttlAmplitude` parameter in the `@CoCache` annotation to add a random jitter range to your base `ttl`, preventing cache stampedes by distributing expiration times across the interval `[ttl - ttlAmplitude, ttl + ttlAmplitude]`.**

The `ahoo-wang/cocache` library provides a powerful annotation-driven caching mechanism for JVM applications. When you **configure TTL with jitter using the `ttlAmplitude` parameter in `@CoCache`**, you introduce controlled randomness to cache entry lifetimes, which prevents simultaneous expiration events from overwhelming your backend database.

## Understanding TTL and ttlAmplitude

`@CoCache` provides two related time-to-live (TTL) settings that control entry expiration:

- **`ttl`**: The base TTL for a cache entry in seconds. Defaults to `Long.MAX_VALUE` (no expiration).
- **`ttlAmplitude`**: The jitter range applied to the base TTL. Defaults to `10` seconds.

For each cached entry, CoCache calculates the actual expiration time by adding a random offset within `[-ttlAmplitude, +ttlAmplitude]` to the base `ttl`. This calculation occurs in `ComputedTtlAt.at(ttl, amplitude)`, which selects a random offset inside the amplitude range and returns the final TTL timestamp.

## How Jitter Prevents Cache Stampede

Without jitter, thousands of cache entries created simultaneously expire at the exact same moment. This triggers a **cache stampede**, where numerous concurrent requests miss the cache simultaneously and hammer the underlying database. By configuring `ttlAmplitude`, you spread expiration times across a window, ensuring gradual cache refresh and stable system performance.

## Implementation Details in CoCache Source

### Annotation Definition

The `@CoCache` interface declares both parameters in [`cocache-api/src/main/kotlin/me/ahoo/cache/api/annotation/CoCache.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-api/src/main/kotlin/me/ahoo/cache/api/annotation/CoCache.kt). The `ttl` parameter defines the base duration, while `ttlAmplitude` specifies the maximum deviation in seconds.

### Configuration Extraction

The `CoCacheMetadataParser` class in [`cocache-core/src/main/kotlin/me/ahoo/cache/annotation/CoCacheMetadataParser.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/annotation/CoCacheMetadataParser.kt) reads these annotation values into a `CoCacheMetadata` object. This metadata propagates through the system via `CacheClientSideFactory` implementations, which pass the values to `DefaultTtlConfiguration` as defined in [`cocache-core/src/main/kotlin/me/ahoo/cache/TtlConfiguration.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/TtlConfiguration.kt).

### Cache Value Creation

When writing values, CoCache applies the jitter in [`cocache-core/src/main/kotlin/me/ahoo/cache/DefaultCacheValue.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/DefaultCacheValue.kt). The `missingGuard(ttl, ttlAmplitude)` method (and the standard `ttlAt` companion) invokes `ComputedTtlAt.at(ttl, amplitude)` to generate the randomized expiration timestamp. All client-side implementations—including `MapClientSideCache`, `GuavaClientSideCache`, and `CaffeineClientSideCache`—consume this `TtlConfiguration` to respect the jitter settings.

## Practical Configuration Examples

### Basic Annotation Usage

Apply jitter directly in your cache interface definition:

```kotlin
@CoCache(
    keyPrefix = "user:",
    ttl = 120,          // 120 seconds base TTL
    ttlAmplitude = 30   // ±30 seconds jitter → actual TTL in [90, 150] seconds
)
interface UserCache : Cache<String, User>

```

Every cached `User` entry expires at a random time between 90 and 150 seconds after creation.

### Spring Bean Customization

Override TTL settings programmatically by providing a custom client-side cache bean:

```kotlin
@Configuration
class UserCacheConfig {
    @Bean
    fun userClientSideCache(): ClientSideCache<User> {
        // MapClientSideCache reads ttl and ttlAmplitude from metadata automatically
        return MapClientSideCache()
    }
}

```

The `DefaultClientSideCacheFactory` injects the metadata values (`cacheMetadata.ttl` and `cacheMetadata.ttlAmplitude`) into the cache implementation.

### Manual Cache Creation

For advanced use cases without annotations, instantiate caches directly with jitter parameters:

```kotlin
val ttl: Long = 300               // 5 minutes
val jitter: Long = 20             // ±20 seconds
val cache = MapClientSideCache<String, String>(ttl, jitter)

// Internally, set operations invoke:
// ComputedTtlAt.at(300, 20)  // Returns random value between 280 and 320

```

### Verifying TTL Ranges in Unit Tests

Validate that jitter stays within expected bounds:

```kotlin
@Test
fun `ttl with jitter should be within expected range`() {
    val ttl = 60L
    val jitter = 10L
    val cache = MapClientSideCache<String, String>(ttl, jitter)

    cache.set("k", "v")
    val cached = cache.getCache("k") as DefaultCacheValue<String>
    val actualTtl = cached.ttlAt - System.currentTimeMillis() / 1000
    
    assertTrue(actualTtl in (ttl - jitter)..(ttl + jitter))
}

```

## Summary

- **`ttlAmplitude` adds random jitter** to cache entry lifetimes, with a default value of `10` seconds.
- The actual TTL is calculated by `ComputedTtlAt.at(ttl, amplitude)`, producing values in the range `[ttl - amplitude, ttl + amplitude]`.
- Configuration flows from the `@CoCache` annotation through `CoCacheMetadataParser` to `TtlConfiguration` and finally to `DefaultCacheValue` at write time.
- All client-side cache implementations (`MapClientSideCache`, `GuavaClientSideCache`, `CaffeineClientSideCache`) respect these jitter settings to prevent cache stampedes.

## Frequently Asked Questions

### What is the default value of ttlAmplitude in @CoCache?

The default value is `10` seconds. If you do not specify `ttlAmplitude`, CoCache applies a ±10 second jitter to your base `ttl` value, or uses `Long.MAX_VALUE` if `ttl` is also unspecified.

### How does ttlAmplitude prevent cache stampedes?

`ttlAmplitude` prevents cache stampedes by randomizing expiration times. Without jitter, entries created in the same batch expire simultaneously, causing a thundering herd of database requests. The random offset distributes expiration events across a time window, smoothing the load on backend systems.

### Can I disable TTL jitter in CoCache?

Yes. Set `ttlAmplitude = 0` in your `@CoCache` annotation to disable jitter. When amplitude is zero, `ComputedTtlAt.at()` returns exactly the base `ttl` value with no randomization, causing all entries to expire at precise intervals.

### How do I access the effective TTL of a cached entry?

Cast the returned cache value to `DefaultCacheValue` and inspect the `ttlAt` property. This property contains the calculated expiration timestamp (milliseconds since epoch) after jitter has been applied, as implemented in [`cocache-core/src/main/kotlin/me/ahoo/cache/DefaultCacheValue.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/DefaultCacheValue.kt).