Current section

Files

Jump to
launchdarkly_server_sdk src ldclient_instance.erl
Raw

src/ldclient_instance.erl

%%-------------------------------------------------------------------
%% @doc `ldclient_instance' module
%% @private
%% @end
%%-------------------------------------------------------------------
-module(ldclient_instance).
%% API
-export([start/3]).
-export([stop/1]).
-export([stop_all/0]).
-export([update_processor_initialized/1]).
-export([feature_store_initialized/1]).
-type options() :: #{
base_uri => string(),
stream_uri => string(),
feature_store => atom(),
events_capacity => pos_integer(),
events_flush_interval => pos_integer(),
events_dispatcher => atom(),
context_keys_capacity => pos_integer(),
private_attributes => ldclient_config:private_attributes(),
stream => boolean(),
polling_interval => pos_integer(),
polling_update_requestor => atom(),
offline => boolean(),
redis_host => string(),
redis_port => pos_integer(),
redis_database => integer(),
redis_password => string(),
redis_prefix => string(),
redis_tls => [ssl:tls_option()] | undefined,
use_ldd => boolean(),
cache_ttl => integer(),
send_events => boolean(),
file_datasource => boolean(),
file_paths => [string()],
file_auto_update => boolean(),
file_poll_interval => pos_integer(),
file_allow_duplicate_keys => boolean(),
http_options => ldclient_config:http_options(),
testdata_tag => atom(),
datasource => atom()
}.
%% Options for starting an SDK instance
-export_type([options/0]).
%%===================================================================
%% API
%%===================================================================
%% @doc Start a client instance
%%
%% @end
-spec start(Tag :: atom(), SdkKey :: string(), Options :: options()) ->
ok | {error, already_started, term()}.
start(Tag, SdkKey, Options) ->
% TODO check if Tag already exists and return already_started error
% Parse options into settings
Settings = ldclient_config:parse_options(SdkKey, Options),
ok = ldclient_config:register(Tag, Settings),
% Start instance supervisor
SupName = get_ref_from_tag(instance, Tag),
UpdateSupName = get_ref_from_tag(instance_stream, Tag),
UpdateWorkerModule = get_update_processor(Settings),
EventsSupName = get_ref_from_tag(instance_events, Tag),
{ok, _} = supervisor:start_child(ldclient_sup, ldclient_instance_sup:child_spec(SupName, [SupName, UpdateSupName, UpdateWorkerModule, EventsSupName, Tag])),
% Start storage backend
true = ldclient_update_processor_state:create_storage_initialized_state(Tag, false),
FeatureStore = maps:get(feature_store, Settings),
ok = FeatureStore:init(SupName, Tag, []),
% Start stream client
true = ldclient_update_processor_state:create_initialized_state(Tag, false),
start_updater(UpdateSupName, UpdateWorkerModule, Tag).
%% @doc Stop a client instance
%%
%% @end
-spec stop(Tag :: atom()) -> ok.
stop(Tag) when is_atom(Tag) ->
% TODO only stop stream instance if it's running
% Terminate stream
StreamSupName = get_ref_from_tag(instance_stream, Tag),
ok = ldclient_updater:stop(StreamSupName),
% Terminate storage
FeatureStore = ldclient_config:get_value(Tag, feature_store),
ok = FeatureStore:terminate(Tag),
% Terminate instance supervisors
SupName = get_ref_from_tag(instance, Tag),
ok = supervisor:terminate_child(ldclient_sup, SupName),
ok = supervisor:delete_child(ldclient_sup, SupName),
ldclient_update_processor_state:delete_initialized_state(Tag),
ldclient_update_processor_state:delete_storage_initialized_state(Tag),
ldclient_config:unregister(Tag).
%% @doc Stop all client instances
%%
%% @end
-spec stop_all() -> ok.
stop_all() ->
Tags = ldclient_config:get_registered_tags(),
lists:foreach(fun stop/1, Tags).
%% @doc Whether an instance's update processor has initialized
%%
%% @end
-spec update_processor_initialized(Tag :: atom()) -> boolean().
update_processor_initialized(Tag) ->
ldclient_update_processor_state:get_initialized_state(Tag).
%% @doc Whether an instance's feature store has initialized
%%
%% @end
-spec feature_store_initialized(Tag :: atom()) -> boolean().
feature_store_initialized(Tag) ->
ldclient_update_processor_state:get_storage_initialized_state(Tag).
%%===================================================================
%% Internal functions
%%===================================================================
%% @doc Get a supervisor registration name for a given tag
%% @private
%%
%% @end
-spec get_ref_from_tag(atom(), Tag :: atom()) -> atom().
get_ref_from_tag(instance, Tag) when is_atom(Tag) ->
list_to_atom("ldclient_instance_" ++ atom_to_list(Tag));
get_ref_from_tag(instance_stream, Tag) when is_atom(Tag) ->
list_to_atom("ldclient_instance_stream_" ++ atom_to_list(Tag));
get_ref_from_tag(instance_events, Tag) when is_atom(Tag) ->
list_to_atom("ldclient_instance_events_" ++ atom_to_list(Tag)).
%% @doc Initialize update processor client and start listening
%% @private
%%
%% @end
-spec start_updater(atom(), atom(), atom()) ->
ok.
start_updater(UpdateSupName, UpdateWorkerModule, Tag) ->
{ok, _Pid} = ldclient_updater:start(UpdateSupName, UpdateWorkerModule, Tag),
ok.
%% @doc Get update processor module name depending on settings
%% @private
%%
%% @end
-spec get_update_processor(Settings :: #{ stream := boolean(),
offline := boolean(),
use_ldd := boolean(),
file_datasource := boolean(),
datasource := atom(),
_ => _
}) -> atom().
get_update_processor(#{ offline := true }) ->
ldclient_update_null_server;
get_update_processor(#{ use_ldd := true }) ->
ldclient_update_null_server;
get_update_processor(#{ datasource := DataSource }) when DataSource /= undefined ->
list_to_atom("ldclient_update_" ++ atom_to_list(DataSource) ++ "_server");
get_update_processor(#{ file_datasource := true }) ->
ldclient_update_file_server;
get_update_processor(#{ stream := false }) ->
ldclient_update_poll_server;
get_update_processor(#{ stream := true }) ->
ldclient_update_stream_server.