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

> Explore CoCache ClientSideCache implementations: Map, Guava, and Caffeine. Learn when to use each for efficient in-memory caching strategies and optimized performance.

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

---

**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`](https://github.com/ahoo-wang/cocache/blob/main/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`](https://github.com/ahoo-wang/cocache/blob/main/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

```kotlin
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`](https://github.com/ahoo-wang/cocache/blob/main/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

```kotlin
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`](https://github.com/ahoo-wang/cocache/blob/main/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

```kotlin
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`](https://github.com/ahoo-wang/cocache/blob/main/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.

```kotlin
@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`](https://github.com/ahoo-wang/cocache/blob/main/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.