Use a **User Profile Service** to persist information about your users and ensure variation assignments are sticky. For example, if you are working on a backend website, you can create an implementation that reads and saves user profiles from a Redis or memcached store.
### Non-mobile SDKs
In the non-mobile SDKs , there is no default implementation. Implementing a User Profile Service is optional and is only necessary if you want to keep variation assignments sticky even when experiment conditions are changed while it is running (for example, audiences, attributes, variation pausing, and traffic distribution). Otherwise, the non-mobile SDKs are stateless and rely on deterministic bucketing to return consistent assignments. See [How bucketing works in Full Stack](🔗) for more information.
### Mobile SDKs
On mobile, the SDK defaults to a User Profile Service that stores this state directly on the device. See the [Android SDK User Profile Service](🔗) and the [iOS SDK User Profile Service](🔗).
For both Android and iOS, use `
manager.userProfileService.lookup` to read a customer’s user profile.
Though the iOS SDK provides a no op (specifically, `
OPTLYUserProfileServiceNoOp`), the Android SDK doesn't provide an equivalent `
userProfileServiceNoOp`. Instead, you must extend the default `
UserProfileService` class in the Android SDK; see the example below.
## Implement a service
Refer to the code samples below to provide your own User Profile Service. It should expose two functions with the following signatures:
lookup`: Takes a user ID string and returns a user profile matching the schema below.
save`: Takes a user profile and persists it.
If you want to use the User Profile Service purely for tracking purposes and not sticky bucketing, you can implement only the `
save` method (always return `
nil` from `
The code example below shows the JSON schema for the user profile object.
experiment_bucket_map` to override the default bucketing behavior and define an alternate experiment variation for a given user. For each experiment that you want to override, add an object to the map. Use the experiment ID as the key and include a `
variation_id` property that specifies the desired variation. If there isn't an entry for an experiment, then the default bucketing behavior persists.
In the example below, `
^[a-zA-Z0-9]+$` is the experiment ID.
The SDK uses the User Profile Service you provide to override Optimizely's default bucketing behavior in cases when an experiment assignment has been saved.
For both iOS and Android apps, the User Profile Service will persist variation assignments across app updates. However, the User Profile Service will not persist variation assignments across app re-installs.
When implementing your own User Profile Service, we recommend loading the user profiles into the User Profile Service on initialization and avoiding performing expensive, blocking lookups on the lookup function to minimize the performance impact of incorporating the service.
When implementing in a multi-server or stateless environment, we suggest using this interface with a backend like [Cassandra](🔗) or [Redis](🔗). You can decide how long you want to keep your sticky bucketing around by configuring these services.
attributes.$opt_experiment_bucket_map` to perform asynchronous lookups of users' previous variations. The SDK handles `
attributes.$opt_experiment_bucket_map` the same way it would `
userProfileService.lookup`, and this allows you to do an asynchronous lookup of the experiment bucket map before passing it to the `
attributes.$opt_experiment_bucket_map` will always take precedence over an implemented `
Get Variation`, or `
The example below shows how to implement consistent bucketing via attributes.
You can use the asynchronous service example below to try this functionality in a test environment. If you implement this example in a production environment, be sure to modify `
UserProfileDB` to the correct database.