-
Notifications
You must be signed in to change notification settings - Fork 3
persistent datastore
By default, the SDK keeps bucketing decisions in memory only. This means each new session (JavaScript) or HTTP request (PHP) recalculates which variation a visitor sees. While the MurmurHash algorithm ensures deterministic bucketing for the same visitor ID and configuration, changes to experience settings (adding/removing variations, adjusting traffic allocation) can shift a returning visitor to a different variation.
A persistent DataStore solves this by saving bucketing decisions to durable storage. Once a visitor is bucketed, subsequent sessions or requests read the stored assignment instead of recalculating it, keeping the visitor's experience consistent even when the project configuration changes.
Persistence also enables cross-request conversion attribution in PHP. For example, a visitor bucketed on page 1 can have a purchase tracked on page 3 and the SDK will correctly link the conversion to the original variation.
Android: the Android SDK persists sticky bucketing decisions and visitor state automatically in app-private storage (
SharedPreferencesplus on-disk config/event files) — there is no DataStore interface to implement. The rest of this guide applies to the JavaScript and PHP SDKs.
The JavaScript and PHP SDKs expect a DataStore with two methods:
| Method | Signature | Description |
|---|---|---|
get |
get(key): value |
Retrieve a value by key. When called without a key (JS only), return all stored data. |
set |
set(key, value): void |
Store a value under the given key. |
In the JavaScript SDK, you pass any object implementing these two methods via the dataStore configuration option.
In the PHP SDK, the DataStore defaults to the PSR-16 CacheInterface provided via the cache option. This cache serves a dual purpose: it stores both the fetched project configuration (with a TTL controlled by dataRefreshInterval) and visitor bucketing data. You can optionally pass a separate dataStore to decouple visitor data from config caching.
import type {ConvertInterface, ConvertConfig} from '@convertcom/js-sdk';
import ConvertSDK from '@convertcom/js-sdk';
class CustomDataStore {
#data = {};
get(key) {
if (!key) return this.#data;
return this.#data[key.toString()];
}
set(key, value) {
if (!key) throw new Error('Invalid CustomDataStore key!');
this.#data[key.toString()] = value;
}
}
const dataStore = new CustomDataStore();
const convertSDK: ConvertInterface = new ConvertSDK({
sdkKey: 'xxx',
dataStore
} as ConvertConfig);The JavaScript example above uses an in-memory object for illustration. In production, replace the backing store with Redis, a database, or any persistent layer appropriate for your environment. For Cloudflare Workers, use the KVDataStore from @convertcom/js-sdk-cloudflare.
The PHP example uses Symfony's Redis adapter wrapped in a PSR-16 interface. Any PSR-16 implementation works -- Memcached, filesystem, database, or a custom adapter.
If you need a different storage backend for visitor data than for config caching, pass the dataStore option separately:
When dataStore is provided, it takes precedence over cache for visitor data.
Pass the DataStore when creating the SDK instance:
import ConvertSDK from '@convertcom/js-sdk';
const convertSDK = new ConvertSDK({
sdkKey: 'xxx',
dataStore: myDataStoreInstance
});
convertSDK.onReady().then(() => {
const context = convertSDK.createContext('user-123');
const variation = context.runExperience('experience-key');
// Bucketing decision is persisted via the DataStore
});The SDK identifies visitors by the $visitorId you pass to createContext(). You are responsible for providing the same ID across requests. Common approaches:
-
Session ID --
session_id()(works for web apps with PHP sessions) - Cookie -- a persistent cookie with a unique visitor token
- Authenticated user ID -- for logged-in users
Without a DataStore, bucketing decisions live only in memory. With a DataStore, the SDK reads from and writes to it via a DataStoreManager wrapper. On each bucketing call the SDK checks the DataStore first; if a stored decision exists for that visitor and experience, it is returned directly. Otherwise, the SDK calculates a new bucketing decision and writes it to the DataStore for future use.
In PHP, the default in-memory ArrayCache is replaced on each request, so bucketing is recalculated every time. Swapping in a persistent PSR-16 cache (Redis, Memcached, etc.) makes the first request calculate and store the decision, while subsequent requests for the same visitor read it from the cache and skip the computation.
See the SDK configuration options (JS | PHP) for the full list of initialization parameters, and the visitor context guide for details on creating and managing visitor contexts.
Copyrights © 2025 All Rights Reserved by Convert Insights, Inc.
Getting Started
JavaScript SDK
Core Concepts
- Experiences & Variations
- Feature Flags
- Bucketing Algorithm
- Rule Evaluation
- Segments
- Data Management
- Event System
- API Communication
How-To Guides
- Running Experiences
- Running Features
- Tracking Conversions
- Visitor Context
- Persistent DataStore
- Client-Side Experimentation
- Server-Side Experimentation
- Tracking Script → SDK
- Troubleshooting
Edge & Integrations
Contributing