Statsig.PersistentStorage behaviour (statsig_elixir v0.22.0)
Behaviour and helper APIs for receiving persistent-assignment (sticky value) updates from the Statsig SDK.
Configuring %Statsig.Options{persistent_storage: reference} enables
persistent assignment: user_persisted_values supplied via
Statsig.ExperimentEvaluationOptions / Statsig.LayerEvaluationOptions are
honored, and the SDK notifies this process when sticky values should be
saved or deleted.
Reads are NOT bridged through the SDK evaluation path: callers load values
from their own store and pass them per call via the evaluation options.
get_values_for_user/3 is the caller-side helper for that read — it derives
the storage key from the user and calls the implementation's load/2.
handle_save/4 receives the sticky values as a decoded map, matching the
shape the SDK expects back in user_persisted_values.
Storage keys have the format "<unit id>:<id type>" — see storage_key/2.
Important: once persistent storage is configured, evaluating an experiment
or layer with user_persisted_values: nil signals that the caller has no
persisted values, and the SDK issues a handle_delete for that config.
Always pass the loaded values (or an empty map for a user with nothing
stored) when sticky assignment should stay active.
Use start_link/3 to launch the bridge process and pass the returned
%Statsig.PersistentStorage.Reference{} into Statsig.Options.
Summary
Types
Opaque state returned from user callbacks.
Sticky values for one config, as a decoded map.
Map of config name to sticky values for one storage key.
Functions
Loads the persisted values for a user, deriving the storage key via
storage_key/2 and calling the implementation's load/2.
Starts a bridge process for the provided implementation module.
Stops the bridge process for the given reference.
Derives the storage key for a user and ID type, in the format
"<unit id>:<id type>" used by the SDK when issuing save/delete
notifications.
Types
@type state() :: term()
Opaque state returned from user callbacks.
Sticky values for one config, as a decoded map.
@type user_persisted_values() :: %{optional(String.t()) => sticky_values()}
Map of config name to sticky values for one storage key.
Callbacks
@callback load(key :: String.t(), state()) :: {:ok, user_persisted_values() | nil, state()} | {:error, term()}
Functions
@spec get_values_for_user( Statsig.PersistentStorage.Reference.t(), Statsig.User.t(), String.t(), timeout() ) :: {:ok, user_persisted_values() | nil} | {:error, term()}
Loads the persisted values for a user, deriving the storage key via
storage_key/2 and calling the implementation's load/2.
This is a synchronous call intended for the caller side of an evaluation:
fetch the values here, then pass them as user_persisted_values in the
experiment/layer evaluation options.
@spec start_link(module(), term(), Keyword.t()) :: {:ok, Statsig.PersistentStorage.Reference.t()} | {:error, term()}
Starts a bridge process for the provided implementation module.
Returns {:ok, %Statsig.PersistentStorage.Reference{}} which can be
assigned to %Statsig.Options{persistent_storage: reference}.
@spec stop(Statsig.PersistentStorage.Reference.t(), term(), non_neg_integer()) :: :ok
Stops the bridge process for the given reference.
@spec storage_key(Statsig.User.t(), String.t()) :: String.t()
Derives the storage key for a user and ID type, in the format
"<unit id>:<id type>" used by the SDK when issuing save/delete
notifications.
For "userID" (any casing, or "user_id") the unit ID is the user's
user_id; for any other ID type it is looked up in custom_ids. A missing
unit ID yields an empty string, mirroring the other SDK bindings.