---
title: Configure the CMAB cache for the Java SDK
slug: feature-experimentation/docs/cmab-cache-config-for-the-java-sdk
docTags: 
createdAt: 2026-08-05T20:39:55.000Z
---

The CMAB cache stores variation assignments to reduce latency and minimize API calls to the CMAB service.

## Prerequisites

- Java SDK version 4.3.0 or higher.
- A CMAB-enabled experiment in your Optimizely project.

## Minimum SDK version

4.3.0

## Description

When a user is bucketed into a CMAB experiment, the Java SDK makes an API call to the CMAB service to determine which variation to show. To improve performance and reduce latency, the Java SDK caches these decisions based on the following:

- User ID
- Experiment ID
- CMAB attribute values

The cache is automatically invalidated when CMAB attributes change for a user, ensuring fresh decisions when context changes.

By default, the SDK uses an in-memory Least Recently Used (LRU) cache with a maximum size of 10000 entries and a TTL of 30 minutes (1800 seconds). You can customize these settings or provide your own cache implementation.

## Configuration options

Configure CMAB caching when building your `CmabService` instance using the `DefaultCmabService.Builder`.

### Default configuration

The Java SDK uses the following default values when no custom configuration is provided:

| Setting              | Default Value               | Description                                            |
| -------------------- | --------------------------- | ------------------------------------------------------ |
| Cache Size           | `10000`                     | Maximum number of cached decisions.                    |
| Cache TTL            | `1800` seconds (30 minutes) | Time-to-live for cache entries.                        |
| Cache Implementation | `DefaultLRUCache`           | Thread-safe in-memory Least Recently Used (LRU) cache. |

### Configuration methods

### Set cache size

```java
DefaultCmabService.builder().withCmabCacheSize(int cacheSize)
```

**Parameters**

- `cacheSize` (int) – Maximum number of decisions to cache. Must be a positive integer.

**Example**

```java
import com.optimizely.ab.cmab.DefaultCmabClient;
import com.optimizely.ab.cmab.service.DefaultCmabService;

// Set cache size to 500 entries
DefaultCmabService cmabService = DefaultCmabService.builder()
    .withClient(new DefaultCmabClient())
    .withCmabCacheSize(500)
    .build();
```

### Set cache TTL

```java
DefaultCmabService.builder().withCmabCacheTimeoutInSecs(int timeoutInSecs)
```

**Parameters**

- `timeoutInSecs` (int) – Time in seconds for cache entries to live. Must be a positive number.

**Example**

```java
import com.optimizely.ab.cmab.DefaultCmabClient;
import com.optimizely.ab.cmab.service.DefaultCmabService;

// Set cache TTL to 10 minutes (600 seconds)
DefaultCmabService cmabService = DefaultCmabService.builder()
    .withClient(new DefaultCmabClient())
    .withCmabCacheTimeoutInSecs(600)
    .build();
```

### Set custom cache

```java
DefaultCmabService.builder().withCustomCache(Cache<CmabCacheValue> cache)
```

**Parameters**

- `cache` (Cache\<CmabCacheValue>) – Custom cache implementation that implements the `Cache` interface.

**Example**

```java
import com.optimizely.ab.cmab.DefaultCmabClient;
import com.optimizely.ab.cmab.service.DefaultCmabService;
import com.optimizely.ab.internal.Cache;

// Provide your own cache implementation
Cache<CmabCacheValue> customCache = new MyCustomCache<>();

DefaultCmabService cmabService = DefaultCmabService.builder()
    .withClient(new DefaultCmabClient())
    .withCustomCache(customCache)
    .build();
```

## Examples

### Basic configuration

Adjust cache size and TTL for your application's needs.

```java
import com.optimizely.ab.Optimizely;
import com.optimizely.ab.OptimizelyFactory;
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.cmab.DefaultCmabClient;
import com.optimizely.ab.cmab.service.DefaultCmabService;
import com.optimizely.ab.config.HttpProjectConfigManager;
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;

// Configure CMAB cache settings
DefaultCmabService cmabService = DefaultCmabService.builder()
    .withClient(new DefaultCmabClient())
    .withCmabCacheSize(500)           // Cache up to 500 decisions
    .withCmabCacheTimeoutInSecs(600)  // Refresh cache every 10 minutes
    .build();

// Create ProjectConfigManager
HttpProjectConfigManager configManager = HttpProjectConfigManager.builder()
    .withSdkKey("your-sdk-key")
    .build();

// Create Optimizely client with custom CMAB service
Optimizely optimizelyClient = OptimizelyFactory.newDefaultInstance(
    configManager,
    null,  // notification center
    null,  // event handler
    null,  // odp api manager
    cmabService
);

// Use the client normally
Map<String, Object> attributes = new HashMap<>();
attributes.put("age", 25);
attributes.put("location", "US");

OptimizelyUserContext user = optimizelyClient.createUserContext("user123", attributes);
OptimizelyDecision decision = user.decide("my-cmab-flag");
// Cache will store this decision for 10 minutes
```

### Advanced: custom cache implementation

For multi-instance deployments or distributed systems, you can provide your own cache implementation.

```java
import com.optimizely.ab.Optimizely;
import com.optimizely.ab.OptimizelyFactory;
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.cmab.DefaultCmabClient;
import com.optimizely.ab.cmab.service.CmabCacheValue;
import com.optimizely.ab.cmab.service.DefaultCmabService;
import com.optimizely.ab.config.HttpProjectConfigManager;
import com.optimizely.ab.internal.Cache;
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;

import java.util.HashMap;
import java.util.Map;

// Example: Custom cache implementation for distributed systems
// You can use Redis, Memcached, or any other caching system
class CustomCmabCache implements Cache<CmabCacheValue> {
    
    public CustomCmabCache() {
        // Initialize your cache backend here
        // For example: this.redis = new JedisPool(), this.memcached = new MemcachedClient(), etc.
    }
    
    @Override
    public CmabCacheValue lookup(String key) {
        // Retrieve value from your cache backend
        // Return the cached value if found, null otherwise
        // Example: String json = redis.get(key); return deserialize(json);
        return null;
    }
    
    @Override
    public void save(String key, CmabCacheValue value) {
        // Store the key-value pair in your cache backend
        // Consider setting an expiration/TTL if your backend supports it
        // Example: redis.setex(key, ttl, serialize(value));
    }
    
    @Override
    public void remove(String key) {
        // Remove the key from your cache backend
        // Example: redis.del(key);
    }
    
    @Override
    public void reset() {
        // Clear all CMAB cache entries from your cache backend
        // Example: redis.flushDB() or delete keys matching a pattern
    }
}

// Create and configure your custom cache
Cache<CmabCacheValue> customCache = new CustomCmabCache();

// Configure CMAB service with custom cache
DefaultCmabService cmabService = DefaultCmabService.builder()
    .withClient(new DefaultCmabClient())
    .withCustomCache(customCache)
    .build();

// Create ProjectConfigManager
HttpProjectConfigManager configManager = HttpProjectConfigManager.builder()
    .withSdkKey("YOUR_SDK_KEY")
    .build();

// Create Optimizely client with custom CMAB service
Optimizely optimizelyClient = OptimizelyFactory.newDefaultInstance(
    configManager,
    null,
    null,
    null,
    cmabService
);

// CMAB decisions now use your custom cache
Map<String, Object> attributes = new HashMap<>();
attributes.put("device_type", "mobile");

OptimizelyUserContext user = optimizelyClient.createUserContext("user123", attributes);
OptimizelyDecision decision = user.decide("my-cmab-flag");
```

## Cache behavior

### Automatic cache invalidation

The cache is automatically invalidated when the following occurs:

- The cached entry's TTL expires.
- CMAB attribute values change for a user (detected through MurmurHash3 hash comparison).
- `IGNORE_CMAB_CACHE` decide option is used.
- `INVALIDATE_USER_CMAB_CACHE` decide option is used.
- `RESET_CMAB_CACHE` decide option is used.

## CMAB-specific decide options

Control cache behavior on a per-decision basis using decide options.

```java
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption;
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;
import java.util.Arrays;

// Ignore cache for this decision (always fetch fresh)
OptimizelyDecision decision = user.decide(
    "my-flag",
    Arrays.asList(OptimizelyDecideOption.IGNORE_CMAB_CACHE)
);

// Invalidate cached decision for this user and experiment
decision = user.decide(
    "my-flag",
    Arrays.asList(OptimizelyDecideOption.INVALIDATE_USER_CMAB_CACHE)
);

// Clear entire CMAB cache
decision = user.decide(
    "my-flag",
    Arrays.asList(OptimizelyDecideOption.RESET_CMAB_CACHE)
);

// Combine multiple options
decision = user.decide(
    "my-flag",
    Arrays.asList(
        OptimizelyDecideOption.INCLUDE_REASONS,
        OptimizelyDecideOption.IGNORE_CMAB_CACHE
    )
);
```

### Available CMAB decide options

| Decide Option                | Description                                                          |
| ---------------------------- | -------------------------------------------------------------------- |
| `IGNORE_CMAB_CACHE`          | Bypass cache and fetch a fresh decision from CMAB service.           |
| `INVALIDATE_USER_CMAB_CACHE` | Remove cached decision for this user and experiment before deciding. |
| `RESET_CMAB_CACHE`           | Clear all entries from the CMAB cache before deciding.               |

## When to use a custom cache

Consider implementing a custom cache when

- **Multi-instance deployments** – Share cache across multiple application instances using Redis, Memcached, or similar.
- **Distributed systems** – Use external caching systems for centralized caching across services.
- **Persistent cache** – Maintain cache across application restarts.
- **Custom eviction policies** – Implement domain-specific cache management (for instance, user-based eviction).
- **Monitoring** – Track cache metrics (hit rate, memory usage, and so on) with your existing monitoring tools.
- **Compliance** – Store cached data in specific locations or with specific encryption requirements.
