Skip to content

Implement Optimistic Decisions

⏱ 25 minutes advanced

Standard SDK initialization waits for the datafile to download before serving decisions. On cold starts or in high-latency environments, this delay blocks the critical path. Optimistic decisions flip the model: serve immediately from a cached datafile, then sync the latest configuration in the background. Users get instant responses, and flag changes propagate within one polling cycle.

  1. Cache the datafile locally for instant startup
  2. Configure the SDK for optimistic initialization
  3. Handle the transition from cached to fresh data
  4. Monitor for stale-data edge cases

Store the datafile on disk, in a database, or in an in-memory cache so the SDK can read it before making any network request.

Save and load a cached datafile
javascript
import fs from 'fs';
import { createInstance } from '@optimizely/optimizely-sdk';

const DATAFILE_PATH = '/tmp/optimizely-datafile.json';

// Save after each successful fetch
function cacheDatafile(datafile) {
  fs.writeFileSync(DATAFILE_PATH, JSON.stringify(datafile));
}

// Load on startup
function loadCachedDatafile() {
  try {
    const raw = fs.readFileSync(DATAFILE_PATH, 'utf-8');
    return JSON.parse(raw);
  } catch {
    return null; // No cache available
  }
}
python
import json
import os

DATAFILE_PATH = '/tmp/optimizely-datafile.json'

def cache_datafile(datafile):
    with open(DATAFILE_PATH, 'w') as f:
        json.dump(datafile, f)

def load_cached_datafile():
    try:
        with open(DATAFILE_PATH, 'r') as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return None
csharp
using System.IO;
using System.Text.Json;

public class DatafileCache
{
    private const string Path = "/tmp/optimizely-datafile.json";

    public static void Save(string datafile)
    {
        File.WriteAllText(Path, datafile);
    }

    public static string? Load()
    {
        try { return File.ReadAllText(Path); }
        catch { return null; }
    }
}

Step 2: Initialize with cached data, poll in background

Section titled “Step 2: Initialize with cached data, poll in background”

Create the SDK instance with the cached datafile so decisions are available immediately. Enable polling so the SDK fetches updates without blocking.

Optimistic SDK initialization
javascript
const cachedDatafile = loadCachedDatafile();

const optimizely = createInstance({
  sdkKey: process.env.OPTIMIZELY_SDK_KEY,
  datafile: cachedDatafile,          // Serve from cache immediately
  datafileOptions: {
    autoUpdate: true,
    updateInterval: 30_000,          // Poll every 30 seconds
    urlTemplate: 'https://cdn.optimizely.com/datafiles/%s.json',
  },
});

// Decisions are available NOW if cache exists
// The SDK polls for updates in the background
if (cachedDatafile) {
  console.log('SDK ready from cache -- serving optimistic decisions');
} else {
  // No cache -- must wait for first fetch
  await optimizely.onReady();
  console.log('SDK ready from network');
}
python
from optimizely import optimizely as opti
from optimizely.config_manager import PollingConfigManager

cached = load_cached_datafile()

config_manager = PollingConfigManager(
    sdk_key=os.environ['OPTIMIZELY_SDK_KEY'],
    update_interval=30,
    datafile=json.dumps(cached) if cached else None,
)

client = opti.Optimizely(config_manager=config_manager)

if cached:
    print('SDK ready from cache')
else:
    # Block until first datafile fetch completes
    import time
    while not client.config_manager.get_config():
        time.sleep(0.1)
    print('SDK ready from network')
csharp
var cached = DatafileCache.Load();

var optimizely = cached != null
    ? OptimizelyFactory.NewDefaultInstance("YOUR_SDK_KEY", cached)
    : OptimizelyFactory.NewDefaultInstance("YOUR_SDK_KEY");

if (cached != null)
{
    Console.WriteLine("SDK ready from cache");
}
else
{
    // Block until datafile is available
    await Task.Delay(2000); // Allow time for initial fetch
    Console.WriteLine("SDK ready from network");
}

Register a notification listener to save the datafile each time the SDK receives a fresh copy. This keeps the cache current for the next cold start.

Keep the cache updated
javascript
optimizely.notificationCenter.addNotificationListener(
  'OPTIMIZELY_CONFIG_UPDATE',
  () => {
    const config = optimizely.getOptimizelyConfig();
    if (config) {
      cacheDatafile(config);
      console.log('Datafile cache updated:', config.revision);
    }
  }
);
python
from optimizely.notification_center import NotificationCenter

def on_config_update():
    config = client.get_optimizely_config()
    if config:
        cache_datafile(config.get_datafile())
        print(f'Datafile cache updated: revision {config.revision}')

client.notification_center.add_notification_listener(
    'OPTIMIZELY_CONFIG_UPDATE',
    on_config_update
)
csharp
optimizely.NotificationCenter.AddNotification(
    NotificationType.OptimizelyConfigUpdate,
    () =>
    {
        var config = optimizely.GetOptimizelyConfig();
        if (config != null)
        {
            DatafileCache.Save(config.GetDatafile());
            Console.WriteLine($"Cache updated: revision {config.Revision}");
        }
    }
);

A cached datafile can be outdated. Consider these edge cases:

ScenarioImpactMitigation
New flag addeddecide returns enabled: false for unknown flagsDefault-off is safe; the flag activates after the next poll
Flag archivedUser gets a decision for a removed flagHarmless; clean up references in code during maintenance
Variation removedUser gets an outdated variation keyValidate the variation key before acting on it
Cache file corruptedSDK fails to parseWrap load in try/catch, fall back to network fetch

Set a cache TTL if your environment demands freshness guarantees. Reject cached datafiles older than a threshold (e.g., 24 hours) and force a network fetch instead.

  • Cold start with cache serves decisions within 5ms
  • Cold start without cache waits for network, then serves decisions
  • Cache updates after each successful poll
  • Corrupted cache falls back to network fetch without errors
  • New flags added in the dashboard appear after one poll cycle