Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

Dev guideRecipesAPI ReferenceChangelog
Dev guideAPI ReferenceRecipesChangelogUser GuideGitHubDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Configure the CMAB cache for the Python SDK

Configure caching behavior for Contextual Multi-Armed Bandit (CMAB) decisions in the Feature Experimentation Python SDK.

👍

Beta

CMAB for Feature Experimentation is in beta. Contact your Customer Success Manager for more information.

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

Prerequisites

  • Python SDK version 5.3.0 or higher.
  • A CMAB-enabled experiment in your Optimizely Feature Experimentation project.

Minimum SDK version

5.3.0

Description

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

  • 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 using the OptimizelyFactory class methods before creating your Optimizely client instance.

Default configuration

The SDK uses these default values when no custom configuration is provided:

Setting

Default Value

Description

Cache Implementation

LRUCache

Thread-safe in-memory LRU cache.

Cache Size

10000

Maximum number of cached decisions.

Cache TTL

1800 seconds (30 minutes)

Time-to-live for cache entries.

cmab_prediction_endpoint

https://prediction.cmab.optimizely.com/predict/{}

URL template for CMAB prediction API. The {} placeholder is replaced with the rule ID. Useful for proxying.

Configuration methods

Set cache size

OptimizelyFactory.set_cmab_cache_size(cache_size, logger=None)

Parameters

  • cache_size (int) – Maximum number of decisions to cache. Must be a positive integer.
  • (Optional) logger – Logger for logging validation errors.

Example

from optimizely.optimizely_factory import OptimizelyFactory
from optimizely import logger as optimizely_logger

logger = optimizely_logger.SimpleLogger()

# Set cache size to 500 entries
OptimizelyFactory.set_cmab_cache_size(500, logger)

Set cache TTL

OptimizelyFactory.set_cmab_cache_ttl(cache_ttl, logger=None)

Parameters:

  • cache_ttl (int): Time in seconds for cache entries to live. Must be a positive number.
  • (Optional) logger – Logger for logging validation errors.

Example

from optimizely.optimizely_factory import OptimizelyFactory
from optimizely import logger as optimizely_logger

logger = optimizely_logger.SimpleLogger()

# Set cache TTL to 10 minutes (600 seconds)
OptimizelyFactory.set_cmab_cache_ttl(600, logger)

Set custom cache

OptimizelyFactory.set_cmab_custom_cache(custom_cache)

Parameters

  • custom_cache – Custom cache implementation that responds to lookup, save, remove, and reset methods.

Example

# Provide your own cache implementation
OptimizelyFactory.set_cmab_custom_cache(my_custom_cache)

Examples

Basic configuration

Adjust cache size and TTL for your application's needs, like in the following example:

from optimizely.optimizely_factory import OptimizelyFactory
from optimizely import logger as optimizely_logger

logger = optimizely_logger.SimpleLogger()

# Configure CMAB cache settings.
OptimizelyFactory.set_cmab_cache_size(500, logger)      # Cache up to 500 decisions
OptimizelyFactory.set_cmab_cache_ttl(600, logger)       # Refresh cache every 10 minutes

# Create Optimizely client
optimizely_client = OptimizelyFactory.custom_instance(
    'YOUR_SDK_KEY',
    None,                   # datafile (optional)
    None,                   # event_dispatcher (optional)
    logger                  # logger
)

# Use the client normally
user = optimizely_client.create_user_context('user123', {
    'age': 25,
    'location': 'US'
})

decision = user.decide('my-cmab-flag')
# Cache storeS this decision for 10 minutes.

Advanced: Custom cache implementation

For multi-instance deployments or distributed systems, you can provide your own cache implementation, like in the following example:

from optimizely.optimizely_factory import OptimizelyFactory
from optimizely import logger as optimizely_logger

# Example: Custom cache implementation for distributed systems.
# You can use Redis, Memcached, or any other caching system.
class CustomCmabCache:
    def __init__(self):
        # Initialize your cache backend here.
        # For example: self.redis = Redis(), self.memcached = Client(), and so on.
        pass

    def lookup(self, key):
        # Retrieve value from your cache backend.
        # Return the cached value if found, None otherwise.
        # Example: return self.redis.get(key)
        pass

    def save(self, key, value):
        # Store the key-value pair in your cache backend.
        # Consider setting an expiration/TTL if your backend supports it.
        # Example: self.redis.setex(key, ttl, value)
        pass

    def remove(self, key):
        # Remove the key from your cache backend.
        # Example: self.redis.delete(key)
        pass

    def reset(self):
        # Clear all CMAB cache entries from your cache backend.
        # Example: self.redis.flushdb() or clear specific key patterns
        pass

# Create and configure your custom cache.
custom_cache = CustomCmabCache()

# Configure Optimizely with custom cache.
OptimizelyFactory.set_cmab_custom_cache(custom_cache)

# Create Optimizely client
optimizely_client = OptimizelyFactory.custom_instance(
    'YOUR_SDK_KEY',
    None,
    None,
    optimizely_logger.SimpleLogger()
)

# CMAB decisions now use your custom cache
user = optimizely_client.create_user_context('user123', {
    'device_type': 'mobile'
})

decision = user.decide('my-cmab-flag')

Custom cache interface

To implement a custom cache, your cache must implement the following four methods:

class CustomCmabCache:
    # Retrieve a value from the cache.
    # @param key [str] The cache key
    # @return [dict, None] The cached value if found, None otherwise
    def lookup(self, key):
        # Your implementation here
        pass

    # Store a key-value pair in the cache.
    # @param key [str] The cache key
    # @param value [dict] The value to cache
    def save(self, key, value):
        # Your implementation here
        pass

    # Remove a key from the cache.
    # @param key [str] The cache key to remove
    def remove(self, key):
        # Your implementation here
        pass

    # Clear all entries from the cache.
    def reset(self):
        # Your implementation here
        pass

Cache behavior

Automatic cache invalidation

The cache is automatically invalidated when

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

CMAB-specific decide options

Control cache behavior on a per-decision basis using decide options, like in the following example:

from optimizely.decision.optimizely_decide_option import OptimizelyDecideOption

# Ignore cache for this decision (always fetch fresh).
decision = user.decide('my-flag', [
    OptimizelyDecideOption.IGNORE_CMAB_CACHE
])

# Invalidate cached decision for this user and experiment.
decision = user.decide('my-flag', [
    OptimizelyDecideOption.INVALIDATE_USER_CMAB_CACHE
])

# Clear entire CMAB cache.
decision = user.decide('my-flag', [
    OptimizelyDecideOption.RESET_CMAB_CACHE
])

# Combine multiple options.
decision = user.decide('my-flag', [
    OptimizelyDecideOption.INCLUDE_REASONS,
    OptimizelyDecideOption.IGNORE_CMAB_CACHE
])

Available CMAB decide options

The following are the available decide options with a short description of what they do:

  • 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 in the following scenarios:

  • 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 example, 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.