How to Configure TTL with Jitter Using the ttlAmplitude Parameter in @CoCache
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 toLong.MAX_VALUE(no expiration).ttlAmplitude: The jitter range applied to the base TTL. Defaults to10seconds.
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. 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 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.
Cache Value Creation
When writing values, CoCache applies the jitter in 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:
@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:
@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:
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:
@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
ttlAmplitudeadds random jitter to cache entry lifetimes, with a default value of10seconds.- The actual TTL is calculated by
ComputedTtlAt.at(ttl, amplitude), producing values in the range[ttl - amplitude, ttl + amplitude]. - Configuration flows from the
@CoCacheannotation throughCoCacheMetadataParsertoTtlConfigurationand finally toDefaultCacheValueat 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →