Current section

Files

Jump to
harness_ff_erlang_server_sdk src cfclient_cache.erl
Raw

src/cfclient_cache.erl

%% @doc
%% Functions to manage cache of Flag and Segment data from server.
%% @end
-module(cfclient_cache).
-include_lib("kernel/include/logger.hrl").
-include("cfclient_config.hrl").
-export(
[
cache_flag/1,
cache_flag/2,
cache_segment/1,
cache_segment/2,
get_value/1,
get_value/2,
set_pid/1,
set_value/2
]
).
% -type flag() :: cfapi_feature_config:cfapi_feature_config().
-type flag() :: cfclient_evaluator:flag().
-type segment() :: cfapi_segment:cfapi_segment().
-type config() :: cfclient:config().
% @doc Get Flag or Segment from cache.
-spec get_value({flag, binary()} | {segment, binary()}) ->
{ok, flag() | segment()} | {error, undefined}.
get_value({Type, Identifier}) ->
Config = cfclient_config:get_config(),
get_value({Type, Identifier}, Config).
-spec get_value({flag, binary()} | {segment, binary()}, config()) ->
{ok, flag() | segment()} | {error, undefined}.
get_value({Type, Identifier}, Config) ->
#{cache_table := CacheTable} = Config,
Key = format_key({Type, Identifier}),
case cfclient_ets:get(CacheTable, Key) of
undefined -> {error, undefined};
Value -> {ok, Value}
end.
% @doc Store flag or segment into cache with new value.
-spec set_value({flag, binary()} | {segment, binary()}, flag() | segment()) ->
ok | {error, outdated}.
set_value({Type, Identifier}, Value) ->
Config = cfclient_config:get_config(),
set_value({Type, Identifier}, Value, Config).
-spec set_value({flag, binary()} | {segment, binary()}, flag() | segment(), config()) ->
ok | {error, outdated}.
set_value({Type, Identifier}, Value, Config) ->
#{cache_table := CacheTable} = Config,
case is_outdated({Type, Identifier}, Value, Config) of
true ->
% This should not happen
?LOG_ERROR("Outdated type ~p, identifier ~p", [Type, Identifier]),
{error, outdated};
false ->
Key = format_key({Type, Identifier}),
true = ets:insert(CacheTable, {Key, Value}),
?LOG_DEBUG("Cached type ~p, identifier ~p", [Type, Identifier]),
ok
end.
-spec is_outdated({flag, binary()} | {segment, binary()}, flag() | segment(), config()) ->
boolean().
is_outdated(Key, NewValue, Config) ->
case get_value(Key, Config) of
{error, undefined} -> false;
{ok, OldValue} ->
#{version := OldVersion} = OldValue,
#{version := NewVersion} = NewValue,
OldVersion > NewVersion
end.
% @doc Create binary key from flag or segment.
-spec format_key({flag, binary()} | {segment, binary()}) -> binary().
format_key({flag, Identifier}) -> <<"flags/", Identifier/binary>>;
format_key({segment, Identifier}) -> <<"segments/", Identifier/binary>>.
-spec cache_segment(segment()) -> ok | {error, outdated}.
cache_segment(#{identifier := Id} = Value) -> set_value({segment, Id}, Value).
-spec cache_segment(segment(), config()) -> ok | {error, outdated}.
cache_segment(#{identifier := Id} = Value, Config) -> set_value({segment, Id}, Value, Config).
-spec cache_flag(flag()) -> ok | {error, outdated}.
cache_flag(#{feature := Id} = Value) -> set_value({flag, Id}, Value).
-spec cache_flag(flag(), config()) -> ok | {error, outdated}.
cache_flag(#{feature := Id} = Value, Config) -> set_value({flag, Id}, Value, Config).
-spec set_pid(pid()) -> ok.
set_pid(_) -> ok.