In Optimizely, you create and manage feature flags in a web interface hosted on app.optimizely.com. Then, you implement an SDK in your application. The link between these two systems is called the datafile.
The datafile is a JSON representation of all the feature flags, audiences, environments, and other elements that you’ve configured in your project.
When you make a change in Optimizely's interface, like toggling a feature on or changing its traffic allocation, Optimizely builds a new version of the datafile and serves it on cdn.optimizely.com. To incorporate this change, your application downloads the new datafile and caches it locally to make decisions quickly. We call this process datafile synchronization.
In SDK versions 3.2 and later, the datafile is fetched automatically. If you would like to learn about options to customize the way you manage the datafile continue reading.
There are several approaches for synchronizing datafiles, depending on your application’s needs. These approaches involve a trade-off between latency and freshness. Finding the right balance ensures that your datafile stays up-to-date without slowing down your application. This guide walks through the best practices and alternatives for striking this balance.
There is no right answer for managing the datafile because every application has different implementation and performance constraints. We recommend evaluating these options and standardizing your approach to datafile management by building a wrapper around our SDKs. This wrapper can also capture other context-specific options like event dispatching and logging.
This topic focuses on managing datafiles in server-side contexts. For our mobile iOS and Android SDKs, datafile management is implemented out-of-the-box. For more information about using datafiles on mobile platforms, see:
To understand the tradeoffs in datafile management, it helps to consider the most naive approach.
Imagine that every time you roll out a feature, you fetch the latest datafile, then use it to initialize an Optimizely client. This approach guarantees you the latest datafile, but it comes at a major performance cost. Every decision requires a round-trip network request. In asynchronous contexts like SMS or chatbots, this can work.
However, for synchronous use cases like a web server or API, you can cache a local copy of the datafile within your application, then synchronize it periodically instead. This lets you make test decisions immediately, without waiting for a network request, while keeping the configuration up-to-date.
For example, you can set up a timer to re-download the datafile every 5 minutes and store it in memory, then read from there every time you make a request. Or, you could use a webhook to keep a centralized service in sync and make internal HTTP requests for the datafile. As these examples illustrate, there are several choices to consider when implementing datafile synchronization:
- Where to store the datafile: Locally in memory, on the filesystem, or on a separate service.
- When to update it: Via a “pull” model that polls for updates on a regular interval, or by listening for a “push” from a webhook.
The sections below walk through the best practices and trade-offs for each approach.
The most common storage approach is to store the JSON string of the datafile itself. This approach is sufficient in many cases, but parsing JSON can take up to 100 ms depending on the language, load, and datafile size. Even when parsing is much faster, be careful with implementations that require repeated JSON parsing. For example, if you re-instantiate the SDK within a loop, the performance cost can quickly add up.
If you do need to instantiate repeatedly, pass in the already-parsed object or previously-instantiated Optimizely client rather than the raw datafile.
Either option gives you the flexibility to choose a format for caching the datafile.
We recommend storing the datafile directly in memory. You'll be able to look up the datafile with near-zero latency, so your web service stays performant. In simple applications, you accomplish this by instantiating the Optimizely object directly and passing it around as needed. In more complex applications, you can use tools like Memcache.
We recommend keeping the datafile in local storage. For example, you can keep the JSON file directly on your local file system, which is generally slower than a memory lookup but faster than a network request. Alternatively, you can store the datafile in a distributed store like Redis. In general, we recommend systems that allow fast reads and relatively fast writes.
If you have many colocated services that all need to operate off the same datafile, consider hosting the Optimizely SDK as an independent service. This service can expose SDK methods like isFeatureEnabled() as HTTP endpoints that other services can hit. Then, you can implement datafile synchronization within the service using any of the methods above. This approach adds a small latency hit from the internal network request, but it makes implementing in a microservice architecture substantially easier, especially if you have many different types of services operating in different languages. The centralized endpoint allows you to implement the logic just once, rather than separately in each service.
The other key consideration is when to update the datafile. In general, you have two choices: pull or push. We recommend using both approaches together, if possible. Use a webhook (push) as the primary means for keeping your datafile up to date, but keep polling (pull) at regular intervals in case the webhook fails.
The “pull” approach consists of polling the Optimizely CDN on a regular interval and updating the stored datafile whenever a new revision is available. Polling is generally easy to implement through a timer or CRON job.
Pulling works best if you don’t need instant updates. For example, if you’re comfortable with toggling a feature flag off and waiting for the change to percolate to your users, polling on a 5- or 10-minute interval is fine.
If you need faster updates, such as every time a feature flag is toggled, we recommend “pushing” the changes as soon as you make them. You can configure a webhook to ping your server as soon as a change is made so you can pull the update down immediately.
This is the preferred approach for server-side contexts with a reliable network connection, but it doesn’t usually apply for web and mobile clients.
You can fetch the datafile via REST API. Accessing the datafile via the REST API ensures that you get the most up to date changes, but the download itself may take slightly longer and you’ll need to authenticate with an API token.
Updated 9 days ago