CoCache ClientSideCache Implementations: When to Use Map, Guava, or Caffeine

CoCache provides three concrete ClientSideCache implementations—MapClientSideCache for simple in-memory storage without eviction, GuavaClientSideCache for configurable size-based eviction policies, and CaffeineClientSideCache for high-throughput, low-latency workloads—all implementing the ComputedClientSideCache interface.

The ahoo-wang/cocache library offers a flexible client-side caching abstraction that supports multiple storage backends for JVM applications. Choosing the right ClientSideCache implementation depends on your specific requirements for concurrency, memory management, and eviction policies. Each implementation stores entries as CacheValue<V> objects that embed expiration metadata and applies CoCache's TTL and TTL amplitude settings uniformly.

Understanding the ClientSideCache Hierarchy

All concrete implementations implement ComputedClientSideCache<V>, which extends the core ClientSideCache<V> interface defined in cocache-core/src/main/kotlin/me/ahoo/cache/client/ComputedClientSideCache.kt. This design allows you to swap implementations without changing your cache consumption logic. The interface hierarchy ensures that TTL handling, amplitude calculations, and value wrapping remain consistent whether you use a simple Map or a sophisticated Caffeine cache.

MapClientSideCache

Characteristics and Use Cases

MapClientSideCache, located in cocache-core/src/main/kotlin/me/ahoo/cache/client/MapClientSideCache.kt, uses a plain java.util.concurrent.ConcurrentHashMap as its backing store. This implementation provides no built-in size-based eviction—entries persist until explicitly removed or their TTL expires. It supports only basic TTL and TTL amplitude configuration.

Use this implementation for unit tests, prototyping, or lightweight scenarios where your application logic naturally bounds the cache size. It offers minimal configuration overhead and eliminates external dependencies beyond the JDK.

Implementation Example

import me.ahoo.cache.client.MapClientSideCache
import me.ahoo.cache.api.Cache
import me.ahoo.cache.api.CoCache

// Create with explicit TTL (5 minutes)
val clientCache = MapClientSideCache<String>(ttl = 5 * 60 * 1000L)

val coCache: Cache<String, User> = CoCache.builder<String, User>()
    .clientSideCache(clientCache)
    .build()

GuavaClientSideCache

Characteristics and Use Cases

The GuavaClientSideCache class in cocache-core/src/main/kotlin/me/ahoo/cache/client/GuavaClientSideCache.kt wraps Guava's CacheBuilder to provide sophisticated eviction policies. It supports maximum size limits, concurrency level tuning, initial capacity settings, and time-based expiration (write or access based).

Choose this implementation when you need size-based eviction or fine-grained expiration controls but do not require Caffeine's ultra-high performance. It suits moderate-traffic production services where Guava's mature eviction policies provide sufficient flexibility.

Implementation Example

import me.ahoo.cache.client.GuavaClientSideCache
import me.ahoo.cache.api.annotation.GuavaCache

// Configure via the @GuavaCache annotation parameters
val guavaSpec = GuavaCache(
    maximumSize = 10_000,
    expireAfterWrite = 5L,
    expireUnit = java.util.concurrent.TimeUnit.MINUTES
)

// Convert to ClientSideCache with CoCache TTL settings
val clientCache = guavaSpec.toClientSideCache<String>(
    ttl = 10 * 60 * 1000L,
    ttlAmplitude = 0L
)

val coCache = CoCache.builder<String, User>()
    .clientSideCache(clientCache)
    .build()

CaffeineClientSideCache

Characteristics and Use Cases

Found in cocache-core/src/main/kotlin/me/ahoo/cache/client/CaffeineClientSideCache.kt, this implementation leverages the Caffeine caching library for very high throughput and low latency. It offers advanced features including refresh-after-write, asynchronous loading, eviction listeners, and maximum weight eviction policies.

Use CaffeineClientSideCache for high-traffic, latency-sensitive workloads where cache performance is critical. It is the optimal choice when you need features like automatic refreshing or custom weight-based eviction that exceed Guava's capabilities.

Implementation Example

import me.ahoo.cache.client.CaffeineClientSideCache
import com.github.benmanes.caffeine.cache.Caffeine

// Build Caffeine cache with advanced settings
val caffeineCache = Caffeine.newBuilder()
    .maximumSize(50_000)
    .expireAfterWrite(10, java.util.concurrent.TimeUnit.MINUTES)
    .build<String, me.ahoo.cache.api.CacheValue<User>>()

val clientCache = CaffeineClientSideCache(caffeineCache)

val coCache = CoCache.builder<String, User>()
    .clientSideCache(clientCache)
    .build()

Spring Boot Configuration

You can inject any ClientSideCache implementation through Spring configuration. As demonstrated in cocache-example/src/main/kotlin/me/ahoo/cache/example/config/UserCacheConfiguration.kt, CoCache's SpringClientSideCacheFactory automatically binds beans named <cacheName>.ClientSideCache to the corresponding cache definition.

@Configuration
class UserCacheConfig {
    @Bean
    fun userCacheClientSideCache(): ClientSideCache<User> {
        // Switch implementations based on environment requirements
        return CaffeineClientSideCache(
            Caffeine.newBuilder().maximumSize(10_000).build()
        )
        // Or: MapClientSideCache(ttl = 300_000L)
        // Or: GuavaClientSideCache(guavaCacheBuilder.build())
    }
}

Summary

  • MapClientSideCache provides a simple ConcurrentHashMap-based store with TTL support but no size eviction, ideal for testing and bounded datasets.
  • GuavaClientSideCache offers configurable size-based eviction and time-based expiration through Guava's CacheBuilder, suitable for moderate-traffic production services.
  • CaffeineClientSideCache delivers the highest performance with advanced features like refresh-after-write and weighted eviction, optimized for high-throughput, latency-sensitive applications.

Frequently Asked Questions

What is the performance difference between Guava and Caffeine ClientSideCache?

Caffeine provides significantly higher throughput and lower latency compared to Guava, especially under high concurrency. According to the CoCache source code in CaffeineClientSideCache.kt, Caffeine uses more modern concurrency techniques and offers features like asynchronous loading that Guava lacks. For most new projects, Caffeine is the recommended choice unless you have specific dependencies on Guava's API.

Can I switch between ClientSideCache implementations without changing my cache logic?

Yes. All three implementations implement the ComputedClientSideCache interface, so you can swap them by changing the bean definition or builder configuration without modifying your cache usage code. The TTL (CoCache.DEFAULT_TTL) and amplitude settings apply uniformly across all implementations.

When should I use MapClientSideCache instead of Caffeine?

Use MapClientSideCache for unit tests, prototyping, or when your dataset is naturally bounded and you want to minimize external dependencies. It stores entries in a simple ConcurrentHashMap without the overhead of sophisticated eviction algorithms, making it lightweight but unsuitable for unbounded caching scenarios.

How do I configure maximum size limits for client-side caches?

Maximum size configuration is only available in GuavaClientSideCache (via GuavaCache.maximumSize or CacheBuilder.maximumSize) and CaffeineClientSideCache (via Caffeine.newBuilder().maximumSize()). MapClientSideCache does not support size-based eviction, so you must ensure your application controls the entry count manually when using the Map implementation.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →