Current section

Files

Jump to
diint_utilites_common_app src utilites kv_storage.erl
Raw

src/utilites/kv_storage.erl

%%%-------------------------------------------------------------------
%%% @author cheese
%%%-------------------------------------------------------------------
-module(kv_storage).
-author("cheese").
-include_lib("kernel/include/logger.hrl").
-behaviour(gen_server).
%% API
-export([start_link/2, add/3, get/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, search/4, internal_clear_old_messages/4, print_all_messages/1]).
-define(SERVER, ?MODULE).
-define(TIMEOUT_CHECK_STATE, 120000).
-record(one_message, {key, value, time}).
-record(state, {lifetime_seconds, server_name, log_file, messages :: [#one_message{}]}).
%%%===================================================================
%%% API
%%%===================================================================
start_link(LifeTimeSeconds, ServerName) ->
gen_server:start_link({local, ServerName}, ?MODULE, [LifeTimeSeconds, ServerName], []).
add(Key, Value, ServerName) ->
gen_server:call(ServerName, {add, Key, Value}).
get(Key, RemoveValue, ServerName) ->
gen_server:call(ServerName, {get, Key, RemoveValue}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([LifeTimeSeconds, ServerName]) ->
?LOG_INFO("Start key-value storage ~w. LifeTimeSeconds: ~w~n", [ServerName, LifeTimeSeconds]),
State = #state{
messages = [],
lifetime_seconds = LifeTimeSeconds,
server_name = ServerName},
erlang:send_after(?TIMEOUT_CHECK_STATE, self(), check_state),
{ok, State}.
handle_call({add, Key, Value}, _From, #state{ messages = OldMessages} = State) ->
NewMessage = #one_message{key = Key, value = Value, time = calendar:local_time()},
?LOG_INFO("Add ~p~n", [Key]),
{reply, ok, State#state{messages = [NewMessage | OldMessages]}};
handle_call({get, Key, RemoveValue}, _From, #state{ messages = OldMessages} = State) ->
case search(Key, RemoveValue, OldMessages, []) of
{ok, Value, NewMessages} ->
?LOG_INFO("Get ~p ok~n", [Key]),
{reply, {ok, Value}, State#state{messages = NewMessages}};
{error, Reason} ->
?LOG_INFO("Get ~p error. ~w~n", [Key, Reason]),
{reply, {error, Reason}, State}
end;
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(check_state, #state{ messages = Messages, lifetime_seconds = LifetimeSeconds} = State) ->
?LOG_INFO("Start check_state for requests length=~w~n", [length(Messages)]),
AllSeconds = time_utilites:get_seconds(calendar:local_time()),
NewMessages = internal_clear_old_messages(AllSeconds, LifetimeSeconds, Messages, []),
?LOG_INFO("New requests length=~w~n", [length(NewMessages)]),
erlang:send_after(?TIMEOUT_CHECK_STATE, self(), check_state),
{noreply, State#state{ messages = NewMessages}};
handle_info(state, #state{ messages = Messages} = State) ->
?LOG_INFO("****************************************************~n", []),
print_all_messages(Messages),
?LOG_INFO("****************************************************~n", []),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
search(_Key, _RemoveValue, [], _MessagesTail) ->
{error, not_found_message};
search(Key, RemoveValue, [#one_message{key = Key, value = Value} = OneMessage | Tail], MessagesTail) ->
case RemoveValue of
true ->
{ok, Value, MessagesTail ++ Tail};
false ->
{ok, Value, [OneMessage | MessagesTail] ++ Tail}
end;
search(Key, RemoveValue, [ OneMessage | Tail], MessagesTail) ->
search(Key, RemoveValue, Tail, [OneMessage | MessagesTail]).
internal_clear_old_messages(_AllSeconds, _LifeTimeSeconds, [], OtherMessage) ->
OtherMessage;
internal_clear_old_messages(AllSeconds, LifeTimeSeconds, [#one_message{time = DateCreate, key = Key} = Client | Tail], OtherMessage) ->
SubSeconds = AllSeconds - time_utilites:get_seconds(DateCreate) - LifeTimeSeconds,
case SubSeconds > 0 of
true ->
?LOG_INFO("Delete request [Key: ~p] [date: ~w] [seconds sub: ~w]~n", [Key, DateCreate, SubSeconds]),
internal_clear_old_messages(AllSeconds, LifeTimeSeconds, Tail, OtherMessage);
false ->
internal_clear_old_messages(AllSeconds, LifeTimeSeconds, Tail, [Client | OtherMessage])
end.
print_all_messages([]) ->
ok;
print_all_messages([#one_message{time = DateCreate, key = Key, value = Value} | Tail]) ->
?LOG_INFO("* [Key: ~p] [Value: ~p] [Date: ~p]~n", [Key, Value, DateCreate]),
print_all_messages(Tail).