diff --git a/README.md b/README.md index b297d5e..5d41f34 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Least Frequently Used -Potent implementation of LFU algorithm based on processes-counters with support of counter by every keys up to once quadrillion hits. +Potent implementation of LFU algorithm based counters with support of counter by every keys up to once quadrillion hits. Reference Guide =============== ## description -This is implementation of LFU algorithm based on processes-counters with support of counter by every keys up to once quadrillion hits. +This is implementation of LFU algorithm based on counters with support of counter by every keys up to once quadrillion hits. #### tasks: @@ -48,38 +48,6 @@ Note that the implementation of algorithm stores keys in binary, that is, for se <<"moscow">> -#### notice: -Note this implementation lfu algorithm use named processes-counters, that is atoms. -System quantity atoms is permissible 1048576 by default. -Maximum possible number named processes dynamic create, counts as follows: - -###### processes of high-order counters - - (MAX_ORDER-1) div MAX_LIMIT - -###### by default configuration: - - (100000000000000-1) div 1000000000 - -###### processes of low-order counters - - MAX_LIMIT div MIN_LIMIT - -###### by default configuration: - - 1000000000 div 100000 - -###### full expression: - - ((100000000000000-1) div 1000000000) + (1000000000 div 100000) = 109 998 - -But it is value may be more if MAX_ORDER raise to quadrillion: - - ((1000000000000000-1) div 1000000000) + (1000000000 div 100000) = 1 009 998 - -In this case you necessary is launch the Erlang-node with key '+t'. - - ## launch options [{lfu,[ @@ -194,262 +162,81 @@ max key size #### put key ###### internal: - lfu:point(K). + lfu:point(K). %% ok ###### external: - POINT:key %% "OK" + POINT:key %% "OK" #### get counter on key ###### internal: - lfu:count(K). + lfu:count(K). %% counter ###### external: - COUNT:key %% "NUMBER" + COUNT:key %% "NUMBER" -#### get offset counter and counter all keys +#### get least counter, most counter and quantity of keys ###### internal: - lfu:state(). + lfu:state(). %% [least counter,most counter,quantity of keys] ###### external: - STATE %% JSON: "{O:NUMBER,Q:NUMBER}" + STATE %% JSON: "{L:NUMBER,M:NUMBER,Q:NUMBER}" #### store algorithm state to disk ###### Please pay attantion, 'store' call executes asynchronously! ###### internal: - lfu:store(). - -###### external: - - STORE %% "OK" - -#### execute scoring of offset counter -###### internal: - - lfu:score(). + lfu:store(). %% ok ###### external: - SCORE %% "READY" + STORE %% "OK" -#### execute scoring of offset counter and get keys by it into internal table +#### get key with least counter ###### internal: -###### Please pay attantion, that exist of internal table expires after following request to fetching 'fetch/0' or to clean 'clean/0'! - T = lfu:fetch(). %% tid() - ets:tab2list(T). + lfu:fetch(). %% {counter,[<<"key">>]} ###### external: - FETCH %% JSON: "[{number1:[key1,key2,key3]},{number2:[key1,key2,key3]},{number3:[key1,key2,key3]},...]" + FETCH %% JSON: "{counter:[key]}" -#### execute scoring of offset counter and get keys by it into external table -###### Please pay attantion, that it`s preferably using interface with internal table 'fetch/0', because it ensures a data consistency with your system! -###### internal: - - T = ets:new(stub,[ %% tid() - bag,public,{write_concurrency,true}, - {decentralized_counters,true} - ]). - lfu:fetch(T). - ets:tab2list(T). - -#### execute scoring of offset counter and get keys by it into internal table for follow delete (support both interaction types) +#### get and delete key with least counter ##### without confirm ###### internal: -###### Please pay attantion, that exist of internal table expires after following request to fetching 'fetch/0' or to clean 'clean/0'! - T = lfu:clean(). %% tid() + lfu:clean(). %% {counter,[<<"key">>]} or - T = lfu:clean(async). %% tid() + lfu:clean(async). %% {counter,[<<"key">>]} -###### external: +###### external - CLEAN %% JSON: "[{number1:[key1,key2,key3]},{number2:[key1,key2,key3]},{number3:[key1,key2,key3]},...]" + CLEAN %% JSON: "{counter:[key]}" or - CLEAN:ASYNC %% JSON: "[{number1:[key1,key2,key3]},{number2:[key1,key2,key3]},{number3:[key1,key2,key3]},...]" + CLEAN:ASYNC %% JSON: "{counter:[key]}" ##### with confirm +###### Please, pay attention timeout exists to confirm, equal '90' seconds by default ###### internal: -###### Please pay attantion, that exist of internal table expires after following request to fetching 'fetch/0' or to clean 'clean/0'! - {T,R} = lfu:clean(sync). %% {tid(),ref()} - lfu:clean(R,T). + {K,R} = lfu:clean(sync). %% {{counter,[<<"key">>]},ref()} + lfu:clean(R,K). ###### external: - CLEAN:SYNC %% JSON: "{[{number1:[key1,key2,key3]},{number2:[key1,key2,key3]},{number3:[key1,key2,key3]},...]:UNIQ_REF}" - CLEAN:UNIQ_REF %% OK - -#### execute scoring of offset counter and get keys by it into external table for follow delete (support only internal interaction type) -###### Please pay attantion, that it`s preferably using interface with internal table 'clean/0', because it ensures a data consistency with your system! -##### without confirm -###### internal: - - T = ets:new(stub,[ %% tid() - bag,public,{write_concurrency,true}, - {decentralized_counters,true} - ]). - T = lfu:clean(T). %% ref() - or - T = lfu:clean(async,T). %% ref() - -##### with confirm -###### internal: - - T = ets:new(stub,[ %% tid() - bag,public,{write_concurrency,true}, - {decentralized_counters,true} - ]). - {T,R} = lfu:clean(sync,T). %% ref() - lfu:clean(R,T). - + CLEAN:SYNC %% JSON: "{{counter:[key]}:UNIQ_REF}" + CLEAN:UNIQ_REF %% "OK" #### put list keys with conters ###### initialization of state, for example, transfer of state from other implementation 'lfu' ###### internal: - lfu:cheat([{K1,C1},{K2,C2},{K3,C3}]). + lfu:cheat([{K1,C1},{K2,C2},{K3,C3}]). %% ok ###### external: - CHEAT:key1,counter1;key2,counter2;key3,counter3 %% OK - - -## configuration (under the hood) -#### Before corrects settings make sure you understand the implementation! - - -define(MIN_LIMIT,100000). - -define(MAX_LIMIT,1000000000). - - -define(MAX_ORDER,100000000000000). %% 1000000000 .. 100000000000000 - -define(MIN_ORDER,100). %% - - -define(MIN_OFFSET,10). %% low limit for step to next rank - -define(MAX_OFFSET,30). %% up limit for step to prev rank - - -define(SCORE_OFFSET,0). %% !!!!! must be less ?MIN_ORDER !!!!! && for example if it`s necessary begin score from 100 then need setting to 99 - - -define(TIMEOUT_STATE_OFFSET,90000). - -define(TIMEOUT_STATE_SELECT,90000). - -define(TIMEOUT_STATE_DELETE,90000). - - -define(PREFIX_KEY,"lfu___"). - -define(POSTFIX_KEY,"__lfu"). - - -define(ETS_PIDS_STORE_TABLE_NAME,lfu_pid). - -define(ETS_KEYS_STORE_TABLE_NAME,lfu_key). - - -define(ETS_KEYS_FETCH_TABLE_NAME,lfu_key_fetch). - -define(ETS_KEYS_FETCH_TABLE_OPTS,[ - public,bag,{write_concurrency,true}, - {decentralized_counters,true}]). - -#### MIN_LIMIT - -Range of values for the processes of low-order counters. - -###### Quantity the processes of low-order counters: - - 'MAX_LIMIT' div 'MIN_LIMIT' - -#### MAX_LIMIT - -Range of values for the processes of high-order counters. - -###### Quantity the processes of high-order counters: - - ('MAX_ORDER'-1) div 'MAX_LIMIT' - -#### MIN_ORDER - -Low (initial) value offset counter. - -#### MAX_ORDER - -Up (end) value for key counters and offset counter. -Keys counters reached this value will be no longer incremented. - -###### Allow values depending on system performance: - - 1000000000 - 10000000000 - 100000000000 - 1000000000000 - 10000000000000 - 100000000000000 - 1000000000000000 - -#### MIN_OFFSET - -Defines minimum permissible percentage of the number of keys, with a counter value equal to or less than the current measured value of the offset counter, of the total number of keys. -When the value is reached, the offset counter is incremented by one digit (provided that the following calculate value does not exceed 'MAX_OFFSET' value) and so on until an acceptable percentage is reached. - -The smaller it is, the fewer keys will be available for follow deletion. - -#### MAX_OFFSET - -Defines maximum permissible percentage of the number of keys, with a counter value equal to or less than the current measured value of the offset counter, of the total number of keys. -When the value is reached, the offset counter is decreases by one digit (provided that the following calculate value will more 'MIN_OFFSET' value) and so on until an acceptable percentage is reached. - - -The larger it is, the more keys will be available for follow deletion. - -#### SCORE_OFFSET - -The value of the key counter when a key begins to take into account by the algorithm. - -###### Must be less: - - 'MIN_ORDER' - -###### example: - - if it`s necessary begin score from 100 then need set to 99 - -#### TIMEOUT_STATE_OFFSET - -The timeout in timing that 'lfu' main process will waiting response on 'score' command from counter processes. - -This value can be incresed provided overload system. - -#### TIMEOUT_STATE_SELECT - -The timeout in timing that 'lfu' main process will waiting response on 'fetch' command from counter processes. - -This value can be incresed provided overload system. - -#### TIMEOUT_STATE_DELETE - -The timeout in timing that 'lfu' main process will waiting confirming response on 'clean' command from outside client. - -This value can be control depending on how long external client will handle the keys list. - -#### PREFIX_KEY - -The prefix key for service name of key for to store process counter pids in 'lfu_key' ets. - -#### POSTFIX_KEY - -The postfix key for service name of key for to store process counter pids in 'lfu_key' ets. - -#### ETS_PIDS_STORE_TABLE_NAME - -The ets for to store process counter pids. - -#### ETS_KEYS_STORE_TABLE_NAME - -The ets for to store counters by keys. - -#### ETS_KEYS_FETCH_TABLE_NAME - -The ets for fetching keys into internal table by commands: 'fetch/0', 'clean/0'. - -#### ETS_KEYS_FETCH_TABLE_OPTS - -The list of options for creating 'ETS_KEYS_FETCH_TABLE_OPTS' ets. + CHEAT:key1,counter1;key2,counter2;key3,counter3 %% "OK" diff --git a/include/lfu.hrl b/include/lfu.hrl index 2d103d0..84b6a9b 100644 --- a/include/lfu.hrl +++ b/include/lfu.hrl @@ -1,23 +1,9 @@ --define(MIN_LIMIT,100000). --define(MAX_LIMIT,1000000000). +-define(SERIE_SIZE,10000000). +-define(MAX_COUNTER,1000000000000000). --define(MAX_ORDER,100000000000000). %% 1000000000 .. 100000000000000 --define(MIN_ORDER,100). - --define(MIN_OFFSET,10). %% low limit for step to next rank --define(MAX_OFFSET,30). %% up limit for step to prev rank - --define(SCORE_OFFSET,0). %% must be less than ?MIN_ORDER && for example if it`s necessary begin score from 100 then need setting to 99 - --define(TIMEOUT_STATE_OFFSET,90000). --define(TIMEOUT_STATE_SELECT,90000). -define(TIMEOUT_STATE_DELETE,90000). --define(PREFIX_KEY,"lfu___"). --define(POSTFIX_KEY,"__lfu"). - --define(ETS_PIDS_STORE_TABLE_NAME,lfu_pid). --define(ETS_KEYS_STORE_TABLE_NAME,lfu_key). +-define(ETS_KEYS_STORE_TABLE_NAME,lfu_key_store). -define(ETS_KEYS_FETCH_TABLE_NAME,lfu_key_fetch). -define(ETS_KEYS_FETCH_TABLE_OPTS,[ public,bag,{write_concurrency,true}, @@ -29,25 +15,11 @@ %% %% following settings in progress develop %% --define(SPAWN_OPT_LFU,[ +-define(SPAWN_OPT_LRU,[ % {max_heap_size,0}, % {message_queue_data,off_heap}, {fullsweep_after,65535} ]). --define(SPAWN_OPT_EXACT_SCORE,[ -% {max_heap_size,0}, -% {message_queue_data,on_heap}, -% {min_bin_vheap_size,46422}, -% {min_heap_size,233}, - {fullsweep_after,65535} -]). --define(SPAWN_OPT_QUICK_SCORE,[ -% {max_heap_size,0}, -% {message_queue_data,on_heap}, -% {min_bin_vheap_size,46422}, -% {min_heap_size,233}, - {fullsweep_after,65535} -]). -ifdef(support). -define(SUPPORT,true). diff --git a/lfu.app b/lfu.app index 2fca70c..d50a6be 100644 --- a/lfu.app +++ b/lfu.app @@ -1,18 +1,12 @@ {application,lfu,[ {description,"Least Frequently Used Algorithm"}, - {vsn,"2.3.1"}, + {vsn,"3.0.0"}, {modules,[ lfu_app,lfu_sup,lfu, - lfu_score_sups_sup,lfu_protocol, - lfu_exact_score_sup,lfu_exact_score, - lfu_quick_score_sup,lfu_quick_score, - lfu_utils + lfu_protocol,lfu_utils ]}, {registered,[ - lfu_sup,lfu, - lfu_score_sups_sup, - lfu_exact_score_sup, - lfu_quick_score_sup + lfu_sup,lfu ]}, {applications,[kernel,stdlib]}, {included_applications,[ranch]}, @@ -20,5 +14,5 @@ {start_phases,[]}, {env,[]}, {maintainers,["Vladimir Solenkov"]}, - {links,[{"Github","https://github.com/Shpaky/LFU.git"}]} + {links,[{"Github","https://github.com/Algorithms-Lab/LFU"}]} ]}. diff --git a/priv/lfu.rel b/priv/lfu.rel index 4585538..00b3e0b 100644 --- a/priv/lfu.rel +++ b/priv/lfu.rel @@ -9,5 +9,5 @@ {crypto,"4.8"}, {public_key,"1.9"}, {asn1,"5.0.14"}, - {lfu, "2.3.1"}] + {lfu, "3.0.0"}] }. diff --git a/priv/lfu.tar.gz b/priv/lfu.tar.gz deleted file mode 100644 index c44625b..0000000 Binary files a/priv/lfu.tar.gz and /dev/null differ diff --git a/src/lfu.erl b/src/lfu.erl index eeeffe1..1013152 100644 --- a/src/lfu.erl +++ b/src/lfu.erl @@ -12,28 +12,20 @@ count/1, state/0, store/0, - score/0, fetch/0, clean/0, - fetch/1, clean/1, clean/2 ]). -export([ reset/1 ]). --export([ - score/2, - fetch/2 -]). -export([ init/1, callback_mode/0 ]). -export([ common/3, - offset/3, - select/3, delete/3 ]). -include("include/lfu.hrl"). @@ -43,17 +35,19 @@ start_link() -> gen_statem:start_link( {local,?MODULE}, - ?MODULE,[?MIN_ORDER,0,?ETS_KEYS_STORE_TABLE_NAME], - [{spawn_opt,?SPAWN_OPT_LFU}]). + ?MODULE,[0,0,0], + [{spawn_opt,?SPAWN_OPT_LRU}]). -init([O,_,T]) -> - Q = restorage(T), - {ok,common,[O,Q]}. + +init([_,_,_]) -> + [L,M,Q] = restorage(?ETS_KEYS_STORE_TABLE_NAME), + {ok,common,[L,M,Q]}. callback_mode() -> state_functions. + point(K) -> case lfu_utils:key_validation(K) of BK when is_binary(BK) -> @@ -98,768 +92,388 @@ state() -> gen_statem:call(?MODULE,state). store() -> gen_statem:cast(?MODULE,store). -score() -> - gen_statem:call(?MODULE,score). fetch() -> gen_statem:call(?MODULE,fetch). -fetch(T) -> - gen_statem:call(?MODULE,{fetch,T}). clean() -> gen_statem:call(?MODULE,{clean,async}). clean(async) -> gen_statem:call(?MODULE,{clean,async}); clean(sync) -> - gen_statem:call(?MODULE,{clean,sync}); -clean(T) -> - gen_statem:call(?MODULE,{clean,{async,T}}). -clean(async,T) -> - gen_statem:call(?MODULE,{clean,{async,T}}); -clean(sync,T) -> - gen_statem:call(?MODULE,{clean,{sync,T}}); -clean(R,T) -> - gen_statem:cast(?MODULE,{{clean,R},T}). + gen_statem:call(?MODULE,{clean,sync}). + +clean(R,K) -> + gen_statem:cast(?MODULE,{{clean,R},K}). reset(D) -> gen_statem:cast(?MODULE,{reset,D}). -score(R,C) -> - gen_statem:cast(?MODULE,{{score,R},C}). -fetch(R,C) -> - gen_statem:cast(?MODULE,{{fetch,R},C}). + +common(cast,{point,K},[L,M,Q]) -> + [NL,NM,NQ] = point_handler(K,L,M,Q), + {keep_state,[NL,NM,NQ]}; +common(cast,{cheat,KVL},[L,M,Q]) -> + [NL,NM,NQ] = cheat_handler(KVL,L,M,Q), + {keep_state,[NL,NM,NQ]}; +common(cast,store,[L,M,Q]) -> + lfu_utils:ets_reset([?ETS_KEYS_STORE_TABLE_NAME]), + {keep_state,[L,M,Q]}; +common(cast,{reset,D},[L,M,Q]) -> + [NL,NM,NQ] = reset_handler(D,L,M,Q), + {keep_state,[NL,NM,NQ]}; +common({call,F},{count,K},[L,M,Q]) -> + {keep_state,[L,M,Q],[{reply,F,get(K)}]}; +common({call,F},state,[L,M,Q]) -> + {keep_state,[L,M,Q],[{reply,F,[L,M,Q]}]}; +common({call,F},fetch,[L,M,Q]) -> + {keep_state,[L,M,Q],[{reply,F,fetch_handler(L,Q)}]}; +common({call,F},{clean,async},[L,M,Q]) -> + {next_state,delete,[L,M,Q,#{from => F}],[{next_event,internal,clean}]}; +common({call,F},{clean,sync},[L,M,Q]) -> + K = fetch_handler(L,Q), + R = make_ref(), + {next_state,delete,[L,M,Q,#{key => K, ref => R}],[{reply,F,{K,R}},{state_timeout,?TIMEOUT_STATE_DELETE,K}]}; +common(cast,{{clean,_R},_K},_StateData) -> + keep_state_and_data; +common(cast,EventContent,_StateData) -> + io:format('State: common~nEventType: cast~nEventContent: ~p~n',[EventContent]), + keep_state_and_data; +common({call,_F},EventContent,_StateData) -> + io:format('State: common~nEventType: cast~nEventContent: ~p~n',[EventContent]), + keep_state_and_data; +common(info,_EventContent,_StateData) -> + keep_state_and_data. + +delete(internal,clean,[L,M,Q,#{from := F}]) -> + [K,NL,NM,NQ] = clean_handler(L,M,Q), + {next_state,common,[NL,NM,NQ],[{reply,F,K}]}; +delete(state_timeout,K,[L,M,Q,#{key := K, ref := _}]) -> + {next_state,common,[L,M,Q]}; +delete(cast,{{clean,R},K},[L,M,Q,#{key := K, ref := R}]) -> + [K,NL,NM,NQ] = clean_handler(L,M,Q), + {next_state,common,[NL,NM,NQ]}; +delete(cast,{{clean,_R},_K},_StateData) -> + keep_state_and_data; +delete(cast,{point,_K},_StateData) -> + {keep_state_and_data,[postpone]}; +delete(cast,{cheat,_KVL},_StateData) -> + {keep_state_and_data,[postpone]}; +delete(cast,store,_StateData) -> + {keep_state_and_data,[postpone]}; +delete(cast,reset,_StateData) -> + {keep_state_and_data,[postpone]}; +delete({call,_F},{count,_K},_StateData) -> + {keep_state_and_data,[postpone]}; +delete({call,_F},state,_StateData) -> + {keep_state_and_data,[postpone]}; +delete({call,_F},fetch,_StateData) -> + {keep_state_and_data,[postpone]}; +delete({call,_F},{clean,async},_StateData) -> + {keep_state_and_data,[postpone]}; +delete({call,_F},{clean,sync},_StateData) -> + {keep_state_and_data,[postpone]}; +delete(cast,_EventContent,_StateData) -> + keep_state_and_data; +delete({call,_F},_EventContent,_StateData) -> + keep_state_and_data; +delete(info,_EventContent,_StateData) -> + keep_state_and_data. -common(cast,{point,K},[O,Q]) -> +point_handler(K,L,M,Q) -> case get(K) of undefined -> - N = list_to_atom("o0" ++ integer_to_list(0)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([0,0]), - lfu_exact_score:point(N,K); - _ -> - lfu_exact_score:point(N,K) - end, put(K,1), + case get(0) of + undefined -> put(0,<<"1">>); + QS -> put(0,integer_to_binary(binary_to_integer(QS)+1)) + end, ets:insert(?ETS_KEYS_STORE_TABLE_NAME,{K,1}), ?SUPPORT andalso erlang:apply(?AUXILIARY,point,[K]), - {keep_state,[O,if ?SCORE_OFFSET == 0 -> Q+1; true -> Q end]}; - C when C < ?MAX_ORDER -> + [1,if Q == 0 -> 1; true -> M end,Q+1]; + OC -> if - %% before MAX LIMIT - C div ?MAX_LIMIT == 0 -> - N = list_to_atom("o0" ++ integer_to_list(C div ?MIN_LIMIT)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([C div ?MIN_LIMIT,0]), - lfu_exact_score:point(N,K); - _ -> - lfu_exact_score:point(N,K) - end, - %% reset - if - C rem ?MIN_LIMIT == 0 -> - NR = list_to_atom("o0" ++ integer_to_list(C div ?MIN_LIMIT - 1)), - case whereis(NR) of - undefined -> - lfu_exact_score_sup:start([C div ?MIN_LIMIT - 1,0]), - lfu_exact_score:reset(NR,K); - _ -> - lfu_exact_score:reset(NR,K) - end; - true -> skip - end; - %% after MAX LIMIT - C rem ?MAX_LIMIT == 0 -> - N = list_to_atom("o" ++ integer_to_list(C div ?MAX_LIMIT)), - case whereis(N) of - undefined -> - lfu_quick_score_sup:start([C div ?MAX_LIMIT,0]), - lfu_quick_score:point(N,K); - _ -> - lfu_quick_score:point(N,K) - end, - %% reset - if - C div ?MAX_LIMIT > 1 -> - NR = list_to_atom("o" ++ integer_to_list(C div ?MAX_LIMIT - 1)), - case whereis(NR) of - undefined -> - lfu_quick_score_sup:start([C div ?MAX_LIMIT - 1,0]), - lfu_quick_score:reset(NR,K); - _ -> - lfu_quick_score:reset(NR,K) - end; - true -> - NR = list_to_atom("o0" ++ integer_to_list(C div ?MIN_LIMIT - 1)), - case whereis(NR) of - undefined -> - lfu_quick_score_sup:start([C div ?MIN_LIMIT - 1,0]), - lfu_exact_score:reset(NR,K); - _ -> - lfu_exact_score:reset(NR,K) - end - end; - true -> skip - end, - put(K,C+1), - ets:insert(?ETS_KEYS_STORE_TABLE_NAME,{K,C+1}), - ?SUPPORT andalso erlang:apply(?AUXILIARY,point,[K]), - {keep_state,[O,if (C+1) / (?SCORE_OFFSET+1) == 1 -> Q+1; true -> Q end]}; - _ -> - ?SUPPORT andalso erlang:apply(?AUXILIARY,point,[K]), - {keep_state,[O,Q]} - end; -common(cast,{cheat,KVL},[O,Q]) -> - NQ = lists:foldl( - fun({K,V},NQ) when V =< ?MAX_ORDER -> - case get(K) of - undefined -> skip; - C -> + OC =< ?MAX_COUNTER -> if - (C-1) div ?MAX_LIMIT == 0 -> - NR = list_to_atom("o0" ++ integer_to_list((C-1) div ?MIN_LIMIT)), - case whereis(NR) of - undefined -> - lfu_exact_score_sup:start([(C-1) div ?MIN_LIMIT,0]), - lfu_exact_score:reset(NR,K); - _ -> - lfu_exact_score:reset(NR,K) + OC rem ?SERIE_SIZE == 0 -> + case get((OC-1) div ?SERIE_SIZE) of + <<"1">> -> erase((OC-1) div ?SERIE_SIZE); + QS1 -> put((OC-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS1)-1)) + end, + put(K,OC+1), + case get(OC div ?SERIE_SIZE) of + undefined -> put(OC div ?SERIE_SIZE,<<"1">>); + QS2 -> put(OC div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS2)+1)) end; true -> - NR = list_to_atom("o" ++ integer_to_list((C-1) div ?MAX_LIMIT)), - case whereis(NR) of - undefined -> - lfu_quick_score:start([(C-1) div ?MAX_LIMIT,0]), - lfu_quick_score:reset(NR,K); - _ -> - lfu_quick_score:reset(NR,K) - end - end - end, - if - V > 0 -> - if - (V-1) div ?MAX_LIMIT == 0 -> - lfu_utils:for(0,(V-1) div ?MIN_LIMIT, - fun(I) -> - N = list_to_atom("o0" ++ integer_to_list(I)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([I,0]); - _ -> skip - end, - if - I == (V-1) div ?MIN_LIMIT -> - lfu_exact_score:cheat(N,K,V); - true -> skip - end - end - ); - true -> - lfu_utils:for(0,(?MAX_LIMIT-1) div ?MIN_LIMIT, - fun(I) -> - N = list_to_atom("o0" ++ integer_to_list(I)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([I,0]); - _ -> skip - end - end - ), - lfu_utils:for(1,(V-1) div ?MAX_LIMIT, - fun(I) -> - N = list_to_atom("o" ++ integer_to_list(I)), - case whereis(N) of - undefined -> - lfu_quick_score_sup:start([I,0]); - _ -> skip - end, - if - I == (V-1) div ?MAX_LIMIT -> - lfu_quick_score:point(N,K); - true -> skip - end - end - ) - end; - true -> skip - end, + put(K,OC+1) + end, + ets:insert(?ETS_KEYS_STORE_TABLE_NAME,{K,OC+1}), + ?SUPPORT andalso erlang:apply(?AUXILIARY,point,[K]), + [ + if + L == OC -> + case length(get_keys(L)) of + 0 -> L + 1; + _ -> L + end; + true -> L + end, + if + M == OC -> M + 1; + true -> M + end,Q + ]; + true -> + ?SUPPORT andalso erlang:apply(?AUXILIARY,point,[K]), + [L,M,Q] + end + end. + +cheat_handler(KVL,L,M,Q) -> + [NL,NM,NQ] = lists:foldl( + fun({K,V},[NL,NM,NQ]) when V =< ?MAX_COUNTER -> case get(K) of undefined -> if V > 0 -> put(K,V), + put((V-1) div ?SERIE_SIZE, + case get((V-1) div ?SERIE_SIZE) of + undefined -> <<"1">>; + QS -> integer_to_binary(binary_to_integer(QS) + 1) + end + ), ets:insert(?ETS_KEYS_STORE_TABLE_NAME,{K,V}), if - V > ?SCORE_OFFSET -> NQ + 1; - true -> NQ + NQ == 0 -> + [V,V,1]; + NM < V -> + [NL,V,NQ+1]; + NL > V -> + [V,NM,NQ+1]; + true -> + [NL,NM,NQ+1] end; - true -> NQ + true -> + [NL,NM,NQ] end; OV -> if V > 0 -> put(K,V), + get((V-1) div ?SERIE_SIZE) /= get((OV-1) div ?SERIE_SIZE) andalso + case get((OV-1) div ?SERIE_SIZE) of + <<"1">> -> erase((OV-1) div ?SERIE_SIZE),true; + QS1 -> put((OV-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS1)-1)),true + end andalso + case get((V-1) div ?SERIE_SIZE) of + undefined -> put((V-1) div ?SERIE_SIZE,<<"1">>); + QS2 -> put((V-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS2)-1)) + end, ets:insert(?ETS_KEYS_STORE_TABLE_NAME,{K,V}), if - OV =< ?SCORE_OFFSET andalso V > ?SCORE_OFFSET -> NQ + 1; - true -> NQ + NM < V -> + if + NL == OV -> + [ + case length(get_keys(NL)) of + 0 -> compute_oldest_key((NL-1) div ?SERIE_SIZE,(V-1) div ?SERIE_SIZE); + _ -> NL + end, + V,NQ + ]; + true -> + [NL,V,NQ] + end; + NL > V -> + [V,NM,NQ]; + NL == OV -> + [ + case length(get_keys(NL)) of + 0 -> compute_oldest_key((NL-1) div ?SERIE_SIZE,(NM-1) div ?SERIE_SIZE); + _ -> NL + end, + NM,NQ + ]; + true -> + [NL,NM,NQ] end; true -> + erase(K), + case get((OV-1) div ?SERIE_SIZE) of + <<"1">> -> erase((OV-1) div ?SERIE_SIZE); + QS3 -> put((OV-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS3)-1)) + end, + ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K), if - OV > ?SCORE_OFFSET -> - erase(K),ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K),NQ - 1; + NL == OV -> + [ + case length(get_keys(NL)) of + 0 -> compute_oldest_key((NL-1) div ?SERIE_SIZE,(NM-1) div ?SERIE_SIZE); + _ -> NL + end,NM,NQ-1 + ]; true -> - erase(K),ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K),NQ + [NL,NM,NQ-1] end end end; - (_,NQ) -> - NQ + (_,[NL,NM,NQ]) -> + [NL,NM,NQ] end, - Q,KVL), - {keep_state,[O,NQ]}; -common({call,From},{count,K},[O,Q]) -> - {keep_state,[O,Q],[{reply,From,get(K)}]}; -common({call,From},state,[O,Q]) -> - {keep_state,[O,Q],[{reply,From,[O,Q]}]}; -common(cast,store,[O,Q]) -> - lfu_utils:ets_reset([?ETS_KEYS_STORE_TABLE_NAME,?ETS_PIDS_STORE_TABLE_NAME]), - {keep_state,[O,Q]}; -common({call,From},score,[O,Q]) -> - {next_state,offset,[O,Q,#{from => From, order => score}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},fetch,[O,Q]) -> - T = lfu_utils:ets_create(), - {next_state,offset,[O,Q,#{from => From, tid => T, ets => internal, order => fetch}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},{fetch,T},[O,Q]) -> - {next_state,offset,[O,Q,#{from => From, tid => T, ets => external, order => fetch}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},{clean,async},[O,Q]) -> - T = lfu_utils:ets_create(), - {next_state,offset,[O,Q,#{from => From, tid => T, ets => internal, order => clean, mode => async}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},{clean,sync},[O,Q]) -> - T = lfu_utils:ets_create(), - {next_state,offset,[O,Q,#{from => From, tid => T, ets => internal, order => clean, mode => sync}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},{clean,{async,T}},[O,Q]) -> - {next_state,offset,[O,Q,#{from => From, tid => T, ets => external, order => clean, mode => async}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common({call,From},{clean,{sync,T}},[O,Q]) -> - {next_state,offset,[O,Q,#{from => From, tid => T, ets => external, order => clean, mode => sync}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O}}}}]}; -common(cast,{reset,D},[O,Q]) -> - NQ = resetting(D,Q), - {keep_state,[O,NQ]}; -common(cast,{{score,_R},_S},_StateData) -> - keep_state_and_data; -common(cast,{{fetch,_R},ready},_StateData) -> - keep_state_and_data; -common(cast,{{clean,_R},_T},_StateData) -> - keep_state_and_data; -common(cast,EventContent,_StateData) -> - io:format('State: common~nEventType: cast~nEventContent: ~p~n',[EventContent]), - keep_state_and_data; -common({call,_From},EventContent,_StateData) -> - io:format('State: common~nEventType: cast~nEventContent: ~p~n',[EventContent]), - keep_state_and_data; -common(info,_EventContent,_StateData) -> - keep_state_and_data. - -offset(internal,{score,{Step,{L,U}}},[O,Q,MD]) -> - R = make_ref(), - N = scoring(L,U,R), - % io:format("State:~p~nCommand:~p~nO:~p~nQ:~p~nL:~p~nU:~p~nStep:~p~nN:~p~nMD~p~n~n",[offset,score,O,Q,L,U,Step,N,MD]), - if - N > 0 -> - {keep_state,[O,Q,MD#{number => N, step => Step, ref => R}],[{state_timeout,?TIMEOUT_STATE_OFFSET,Step}]}; - true -> - {keep_state,[O,Q,MD#{number => N, step => Step, ref => R}],[{state_timeout,0,Step}]} - end; -offset(state_timeout,Step,[O,Q,#{step := Step} = MD]) -> - % io:format("TimeoutState:~p~nMD:~p~nO:~p~nQ~p~n~n",[offset,MD,O,Q]), - if - Step =:= previous -> - case maps:is_key(current,MD) of - false -> - {keep_state,[O,Q,MD#{Step => maps:get(Step,MD,0)}],[{next_event,internal,{score,{current,{O,O*10}}}}]}; - true -> - {keep_state,[O,Q,MD#{Step => maps:get(Step,MD,0) + maps:get(current,MD)}],[{next_event,internal,count}]} - end; - Step =:= current -> - {keep_state,[O,Q,MD#{Step => maps:get(Step,MD,0) + maps:get(previous,MD)}],[{next_event,internal,{score,{following,{O*10,O*100}}}}]}; - Step =:= following -> - {keep_state,[O,Q,MD#{Step => maps:get(Step,MD,0) + maps:get(current,MD)}],[{next_event,internal,count}]} - end; -offset(cast,{{score,R},S},[O,Q,#{number := N, step := Step, ref := R} = MD]) -> - if - N > 1 -> - {keep_state,[O,Q,MD#{Step => S + maps:get(Step,MD,0), number => N-1}],[{state_timeout,?TIMEOUT_STATE_OFFSET,Step}]}; - true -> - if - Step =:= previous -> - case maps:is_key(current,MD) of - false -> - {keep_state,[O,Q,MD#{Step => S + maps:get(Step,MD,0)}],[{state_timeout,cancel},{next_event,internal,{score,{current,{O,O*10}}}}]}; - true -> - {keep_state,[O,Q,MD#{Step => S + maps:get(Step,MD,0)}],[{state_timeout,cancel},{next_event,internal,count}]} - end; - Step =:= current -> - {keep_state,[O,Q,MD#{Step => S + maps:get(Step,MD,0) + maps:get(previous,MD)}],[{state_timeout,cancel},{next_event,internal,{score,{following,{O*10,O*100}}}}]}; - Step =:= following -> - {keep_state,[O,Q,MD#{Step => S + maps:get(Step,MD,0) + maps:get(current,MD)}],[{state_timeout,cancel},{next_event,internal,count}]} - end - end; -offset(internal,count,[O,Q,#{previous := P, current := C, following := F, from := From, order := Order} = MD]) -> - % io:format("State:~p~nCommand:~p~nP:~p~nC:~p~nF:~p~nO:~p~nQ~p~n~n",[offset,count,P,C,F,O,Q]), - if - Q < 1 -> - if - Order =:= score -> - {next_state,common,[O,Q],[{reply,From,ready}]}; - Order =:= fetch -> - %% idle iteration but neccesary - {next_state,select,[O,Q,#{from => From, tid => maps:get(tid,MD), ets => maps:get(ets,MD), order => fetch}],[{next_event,internal,fetch}]}; - Order =:= clean -> - %% idle iteration but necessary - {next_state,select,[O,Q,#{from => From, tid => maps:get(tid,MD), ets => maps:get(ets,MD), order => clean, mode => maps:get(mode,MD)}],[{next_event,internal,fetch}]} - end; - C / Q * 100 < ?MIN_OFFSET andalso O*10 =< ?MAX_ORDER andalso F / Q * 100 =< ?MAX_OFFSET -> - {keep_state,[O*10,Q,MD#{previous => C, current => F, following => 0}],[{next_event,internal,{score,{following,{O*100,O*1000}}}}]}; - C / Q * 100 > ?MAX_OFFSET andalso O div 10 >= ?MIN_ORDER andalso P / Q * 100 >= ?MIN_OFFSET -> - {keep_state,[O div 10,Q,MD#{previous => 0, current => P, following => C}],[{next_event,internal,{score,{previous,{?SCORE_OFFSET,O div 10}}}}]}; - true -> - if - Order =:= score -> - {next_state,common,[O,Q],[{reply,From,ready}]}; - Order =:= fetch -> - {next_state,select,[O,Q,#{from => From, tid => maps:get(tid,MD), ets => maps:get(ets,MD), order => fetch}],[{next_event,internal,fetch}]}; - Order =:= clean -> - {next_state,select,[O,Q,#{from => From, tid => maps:get(tid,MD), ets => maps:get(ets,MD), order => clean, mode => maps:get(mode,MD)}],[{next_event,internal,fetch}]} - end - end; -offset(cast,{{score,_},_S},_StateData) -> - keep_state_and_data; -offset(cast,{{fetch,_R},_S},_StateData) -> - keep_state_and_data; -offset(cast,{{clean,_R},_T},_StateData) -> - keep_state_and_data; -offset(cast,{point,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -offset(cast,{cheat,_KVL},_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},{count,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},state,_StateData) -> - {keep_state_and_data,[postpone]}; -offset(cast,store,_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},score,_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},fetch,_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},{fetch,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -offset({call,_From},{clean,_D},_StateData) -> %% _D = {async,tid()} || {sync,tid()} || async || sync - {keep_state_and_data,[postpone]}; -offset(cast,{reset,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -offset(cast,_EventContent,_StateData) -> - keep_state_and_data; -offset({call,_From},_EventContent,_StateData) -> - keep_state_and_data; -offset(info,_EventContent,_StateData) -> - keep_state_and_data. - -select(internal,fetch,[O,Q,#{tid := T} = MD]) -> - % io:format("State:~p~nCommand:~p~nMD:~p~nO:~p~nQ~p~n~n",[select,fetch,MD,O,Q]), - R = make_ref(), - N = fetching(O*10,T,R), - if - N > 0 -> - {keep_state,[O,Q,MD#{number => N, ref => R}],[{state_timeout,?TIMEOUT_STATE_SELECT,T}]}; - true -> - {keep_state,[O,Q,MD#{number => N, ref => R}],[{state_timeout,0,T}]} - end; -select(state_timeout,T,[O,Q,#{tid := T, from := From, order := Order, ets := internal} = MD]) -> - % io:format("TimeoutState:~p~nMD:~p~nO:~p~nQ~p~n~n",[select,MD,O,Q]), - NT = lfu_utils:ets_re_create(), - if - Order =:= fetch -> - {next_state,common,[O,Q],[{reply,From,NT}]}; - Order =:= clean -> - case maps:get(mode,MD) of - async -> - {next_state,delete,[O,Q,#{tid => NT, from => From}],[{next_event,internal,clean}]}; - sync -> - R = make_ref(), - {next_state,delete,[O,Q,#{tid => NT, ref => R}],[{reply,From,{NT,R}},{state_timeout,?TIMEOUT_STATE_DELETE,NT}]} - end - end; -select(state_timeout,T,[O,Q,#{tid := T, from := From, order := Order, ets := external} = MD]) -> - % io:format("TimeoutState:~p~nMD:~p~nO:~p~nQ~p~n~n",[select,MD,O,Q]), - if - Order =:= fetch -> - {next_state,common,[O,Q],[{reply,From,T}]}; - Order =:= clean -> - case maps:get(mode,MD) of - async -> - {next_state,delete,[O,Q,#{tid => T, from => From}],[{next_event,internal,clean}]}; - sync -> - R = make_ref(), - {next_state,delete,[O,Q,#{tid => T, ref => R}],[{reply,From,{T,R}},{state_timeout,?TIMEOUT_STATE_DELETE,T}]} - end - end; -select(cast,{{fetch,R},ready},[O,Q,#{number := N, tid := T, from := From, order := Order, ref := R} = MD]) -> - if - N > 1 -> - {keep_state,[O,Q,MD#{number => N - 1}],[{state_timeout,?TIMEOUT_STATE_SELECT,T}]}; - true -> - if - Order =:= fetch -> - {next_state,common,[O,Q],[{reply,From,T}]}; - Order =:= clean -> - case maps:get(mode,MD) of - async -> - {next_state,delete,[O,Q,#{tid => T, from => From}],[{next_event,internal,clean}]}; - sync -> - R1 = make_ref(), - {next_state,delete,[O,Q,#{tid => T, ref => R1}],[{reply,From,{T,R1}},{state_timeout,?TIMEOUT_STATE_DELETE,T}]} - end - end - end; -select(cast,{{fetch,_R},ready},_StateData) -> - keep_state_and_data; -select(cast,{{score,_R},_S},_StateData) -> - keep_state_and_data; -select(cast,{{clean,_R},_T},_StateData) -> - keep_state_and_data; -select(cast,{point,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -select(cast,{cheat,_KVL},_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},{count,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},state,_StateData) -> - {keep_state_and_data,[postpone]}; -select(cast,store,_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},score,_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},fetch,_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},{fetch,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -select({call,_From},{clean,_D},_StateData) -> %% _D = {async,tid()} || {sync,tid()} || async || sync - {keep_state_and_data,[postpone]}; -select(cast,{reset,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -select(cast,_EventContent,_StateData) -> - keep_state_and_data; -select({call,_From},_EventContent,_StateData) -> - keep_state_and_data; -select(info,_EventContent,_StateData) -> - keep_state_and_data. - -delete(internal,clean,[O,Q,#{tid := T, from := From}]) -> - NQ = resetting(T,Q), - ?SUPPORT andalso erlang:apply(?AUXILIARY,reset,[T]), - {next_state,common,[O,NQ],[{reply,From,T}]}; -delete(state_timeout,T,[O,Q,#{tid := T, ref := _R}]) -> - % io:format("TimeoutState:~p~nT:~p~nO:~p~nQ~p~n~n",[delete,T,O,Q]), - {next_state,common,[O,Q]}; -delete(cast,{{clean,R},T},[O,Q,#{tid := T, ref := R}]) -> - NQ = resetting(T,Q), - ?SUPPORT andalso erlang:apply(?AUXILIARY,reset,[T]), - {next_state,common,[O,NQ]}; -delete(cast,{{clean,_R},_T},_StateData) -> - keep_state_and_data; -delete(cast,{{fetch,_R},ready},_StateData) -> - keep_state_and_data; -delete(cast,{{score,_R},_S},_StateData) -> - keep_state_and_data; -delete(cast,{point,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -delete(cast,{cheat,_KVL},_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},{count,_K},_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},state,_StateData) -> - {keep_state_and_data,[postpone]}; -delete(cast,store,_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},score,_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},fetch,_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},{fetch,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -delete({call,_From},{clean,_D},_StateData) -> %% _D = {async,tid()} || {sync,tid()} || async || sync - {keep_state_and_data,[postpone]}; -delete(cast,{reset,_T},_StateData) -> - {keep_state_and_data,[postpone]}; -delete(cast,_EventContent,_StateData) -> - keep_state_and_data; -delete({call,_From},_EventContent,_StateData) -> - keep_state_and_data; -delete(info,_EventContent,_StateData) -> - keep_state_and_data. - + [L,M,Q],KVL), + [NL,if NQ == 0 -> 0; true -> NM end,NQ]. -fetching(O,T,R) -> +fetch_handler(L,Q) -> if - (O-1) div ?MAX_LIMIT == 0 -> - lfu_utils:for(0,(O-1) div ?MIN_LIMIT, - fun(I) -> - N = list_to_atom("o0" ++ integer_to_list(I)), - case whereis(N) of - undefined -> skip; - _ -> - lfu_exact_score:fetch(N,R,T, - if - I == 0 -> - ?SCORE_OFFSET+1; - true -> ?MIN_LIMIT*I+1 - end, - if - O > ?MIN_LIMIT*(I+1) -> - ?MIN_LIMIT*(I+1); - true -> O - end - ) - end - end - ), - length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end - end, - lists:seq(0,(O-1) div ?MIN_LIMIT))); + Q =:= 0 -> + {L,[]}; %% that is: {0,[]}; true -> - lfu_utils:for(0,(?MAX_LIMIT-1) div ?MIN_LIMIT, - fun(I) -> - N = list_to_atom("o0" ++ integer_to_list(I)), - case whereis(N) of - undefined -> skip; - _ -> - lfu_exact_score:fetch(N,R,T, - if - I == 0 -> - ?SCORE_OFFSET+1; - true -> ?MIN_LIMIT*I+1 - end, - if - O > ?MIN_LIMIT*(I+1) -> - ?MIN_LIMIT*(I+1); - true -> O %% never hit in this branch - end - ) - end - end - ), - L1 = length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end - end, - lists:seq(0,(?MAX_LIMIT-1) div ?MIN_LIMIT))), - - lfu_utils:for(1,(O-1) div ?MAX_LIMIT, - fun(I) -> - N = list_to_atom("o" ++ integer_to_list(I)), - case whereis(N) of - undefined -> skip; - _ -> - lfu_quick_score:fetch(N,R,T) - end - end - ), - L2 = length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end - end, - lists:seq(1,(O-1) div ?MAX_LIMIT))), - L1 + L2 + [K|_] = get_keys(L), + {L,[K]} end. -scoring(L,U,R) -> +clean_handler(L,M,Q) -> if - U =< ?MAX_LIMIT -> - lfu_utils:for(L div ?MIN_LIMIT,(U-1) div ?MIN_LIMIT, - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> - skip; - N -> - if - I == 0 -> - if - U > ?MIN_LIMIT*(I+1) -> - lfu_exact_score:score(N,R,L+1,?MIN_LIMIT*(I+1)); - true -> - lfu_exact_score:score(N,R,L+1,U) - end; - true -> - lfu_exact_score:score(N,R,?MIN_LIMIT*I+1,?MIN_LIMIT*(I+1)) - end - end - end - ), - length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end - end, - lists:seq(L div ?MIN_LIMIT,(U-1) div ?MIN_LIMIT))); + Q =:= 0 -> + [{L,[]},L,M,Q]; %% that is: [{0,[]},0,0,0]; true -> - lfu_utils:for(L div ?MIN_LIMIT,(?MAX_LIMIT-1) div ?MIN_LIMIT, - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> - skip; - N -> - if - I == 0 -> - lfu_exact_score:score(N,R,L+1,?MIN_LIMIT*(I+1)); - true -> - lfu_exact_score:score(N,R,?MIN_LIMIT*I+1,?MIN_LIMIT*(I+1)) - end - end - end - ), - L1 = + [K|_] = get_keys(L), + erase(K), + case get((L-1) div ?SERIE_SIZE) of + <<"1">> -> erase((L-1) div ?SERIE_SIZE); + QS -> put((L-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS)-1)) + end, + ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K), + ?SUPPORT andalso erlang:apply(?AUXILIARY,reset,[[{L,[K]}]]), + [ + {L,[K]}, + case length(get_keys(L)) of + 0 -> compute_oldest_key((L-1) div ?SERIE_SIZE,(M-1) div ?SERIE_SIZE); + _ -> L + end, if - L < ?MAX_LIMIT -> - length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o0" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end - end, - lists:seq(L div ?MIN_LIMIT,(?MAX_LIMIT-1) div ?MIN_LIMIT))); - true -> 0 - end, - - lfu_utils:for(if L div ?MAX_LIMIT > 0 -> L div ?MAX_LIMIT; true -> 1 end,U div ?MAX_LIMIT - 1, - fun(I) -> - case whereis(list_to_atom("o" ++ integer_to_list(I))) of - undefined -> skip; - N -> lfu_quick_score:score(N,R) - end - end - ), - L2 = length(lists:filter( - fun(I) -> - case whereis(list_to_atom("o" ++ integer_to_list(I))) of - undefined -> false; - _ -> true - end + Q-1 == 0 -> 0; + true -> M end, - lists:seq(if L div ?MAX_LIMIT > 0 -> L div ?MAX_LIMIT; true -> 1 end,U div ?MAX_LIMIT - 1))), - L1 + L2 + Q-1 + ] end. -resetting({_,KL},Q) -> - lists:foldl( - fun(K,NQ) -> +reset_handler({_,KL},L,M,Q) -> + [NL,NQ] = lists:fold1( + fun(K,[NL,NQ]) -> case erase(K) of - undefined -> NQ; - C -> + undefined -> [NL,NQ]; + OC -> + case get((OC-1) div ?SERIE_SIZE) of + <<"1">> -> erase((OC-1) div ?SERIE_SIZE); + QS -> put((OC-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS)-1)) + end, ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K), if - (C-1) div ?MAX_LIMIT == 0 -> - N = list_to_atom("o0" ++ integer_to_list((C-1) div ?MIN_LIMIT)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([(C-1) div ?MIN_LIMIT,0]), - lfu_exact_score:reset(N,K); - _ -> - lfu_exact_score:reset(N,K) - end; + OC == NL -> + [ + case length(get_keys(NL)) of + 0 -> compute_oldest_key((NL-1) div ?SERIE_SIZE,(M-1) div ?SERIE_SIZE); + _ -> NL + end,NQ-1 + ]; true -> - N = list_to_atom("o" ++ integer_to_list((C-1) div ?MAX_LIMIT)), - case whereis(N) of - undefined -> - lfu_quick_score_sup:start([(C-1) div ?MAX_LIMIT,0]), - lfu_quick_score:reset(N,K); - _ -> - lfu_quick_score:reset(N,K) - end - end, - NQ - 1 + [NL,NQ-1] + end end end, - Q,KL); -resetting(T,Q) -> - case ets:info(T) of - undefined -> Q; + [L,Q],KL), + [NL,if NQ == 0 -> 0; true -> M end,NQ]; +reset_handler(T,L,M,Q) -> + [NL,NQ] = case ets:info(T) of + undefined -> [L,Q]; _ -> ets:foldl( - fun({_,KL},NQ) -> + fun({_,KL},[NL,NQ]) -> lists:foldl( - fun(K,NQ) -> + fun(K,[NL,NQ]) -> case erase(K) of - undefined -> NQ; - C -> + undefined -> [NL,NQ]; + OC -> + case get((OC-1) div ?SERIE_SIZE) of + <<"1">> -> erase((OC-1) div ?SERIE_SIZE); + QS -> put((OC-1) div ?SERIE_SIZE,integer_to_binary(binary_to_integer(QS)-1)) + end, ets:delete(?ETS_KEYS_STORE_TABLE_NAME,K), if - (C-1) div ?MAX_LIMIT == 0 -> - N = list_to_atom("o0" ++ integer_to_list((C-1) div ?MIN_LIMIT)), - case whereis(N) of - undefined -> - lfu_exact_score_sup:start([(C-1) div ?MIN_LIMIT,0]), - lfu_exact_score:reset(N,K); - _ -> - lfu_exact_score:reset(N,K) - end; + OC == NL -> + [ + case length(get_keys(NL)) of + 0 -> compute_oldest_key((NL-1) div ?SERIE_SIZE,(M-1) div ?SERIE_SIZE); + _ -> NL + end,NQ-1 + ]; true -> - N = list_to_atom("o" ++ integer_to_list((C-1) div ?MAX_LIMIT)), - case whereis(N) of - undefined -> - lfu_quick_score_sup:start([(C-1) div ?MAX_LIMIT,0]), - lfu_quick_score:reset(N,K); - _ -> - lfu_quick_score:reset(N,K) - end - end, - NQ - 1 + [NL,NQ-1] + end end end, - NQ,KL) + [NL,NQ],KL) end, - Q,T) + [L,Q],T) + end, + [NL,if NQ == 0 -> 0; true -> M end,NQ]. + + +compute_oldest_key(U,U) -> + case get(U) of + undefined -> 0; + _ -> check_keys_serie((U*?SERIE_SIZE)+1,(U+1)*?SERIE_SIZE) + end; +compute_oldest_key(L,U) -> + case get(L) of + undefined -> compute_oldest_key(L+1,U); + _ -> check_keys_serie((L*?SERIE_SIZE)+1,(L+1)*?SERIE_SIZE) + end. + +check_keys_serie(U,U) -> + case get_keys(U) of + [] -> 0; + [_|_] -> U + end; +check_keys_serie(L,U) -> + case get_keys(L) of + [] -> check_keys_serie(L+1,U); + [_|_] -> L end. + restorage(T) -> case ets:whereis(T) of - undefined -> 0; + undefined -> [0,0,0]; _ -> ets:foldl( - fun({K,V},Q) -> + fun({K,C},[NL,NM,NQ]) -> if - V =< ?MAX_ORDER -> + C =< ?MAX_COUNTER andalso C > 0 -> + QS = get((C-1) div ?SERIE_SIZE), + put((C-1) div ?SERIE_SIZE, + if + QS == undefined -> <<"1">>; + true -> integer_to_binary(binary_to_integer(QS) + 1) + end + ), + put(K,C), if - V div ?MAX_LIMIT =< 1 -> - N = list_to_atom("o0" ++ integer_to_list((V -1)div ?MIN_LIMIT)), - whereis(N) =:= undefined andalso lfu_exact_score_sup:start([(V-1) div ?MIN_LIMIT,0]); + NQ == 0 -> + [C,C,1]; + C < NL -> + [C,NM,NQ+1]; + C > NM -> + [NL,C,NQ+1]; true -> - N = list_to_atom("o" ++ integer_to_list((V-1) div ?MAX_LIMIT)), - whereis(N) =:= undefined andalso lfu_quick_score_sup:start([(V-1) div ?MAX_LIMIT,0]) - end, - put(K,V), - if - V > ?SCORE_OFFSET -> Q + 1; - true -> Q + [NL,NM,NQ+1] end; true -> - Q + [NL,NM,NQ] end end, - 0,T) + [0,0,0],T) end. diff --git a/src/lfu_exact_score.erl b/src/lfu_exact_score.erl deleted file mode 100644 index 150a5a4..0000000 --- a/src/lfu_exact_score.erl +++ /dev/null @@ -1,193 +0,0 @@ --module('lfu_exact_score'). --author('VSolenkov'). - --behavior('gen_server'). - --export([ - start/1, - state/1, - point/2, - reset/2, - cheat/3, - score/4, - fetch/5 -]). - --export([ - init/1 -]). --export([ - handle_cast/2, - handle_call/3, - handle_info/2 -]). --include("include/lfu.hrl"). - - -start([O,Q]) -> - gen_server:start_link( - {local,list_to_atom("o0" ++ integer_to_list(O))}, - ?MODULE,[O,Q], - [{spawn_opt,?SPAWN_OPT_EXACT_SCORE}]). - - -init([O,Q]) -> - case ets:lookup(?ETS_PIDS_STORE_TABLE_NAME,list_to_binary(?PREFIX_KEY ++ "o0" ++ integer_to_list(O) ++ ?POSTFIX_KEY)) of - [] -> - ets:insert(?ETS_PIDS_STORE_TABLE_NAME,{ - list_to_binary(?PREFIX_KEY ++ "o0" ++ integer_to_list(O) ++ ?POSTFIX_KEY), - self()}), - {ok,[O,Q]}; - _ -> - NQ = restorage(?ETS_KEYS_STORE_TABLE_NAME,?MIN_LIMIT*O+1,?MIN_LIMIT*(O+1),O), - ets:insert(?ETS_PIDS_STORE_TABLE_NAME,{ - list_to_binary(?PREFIX_KEY ++ "o0" ++ integer_to_list(O) ++ ?POSTFIX_KEY), - self()}), - {ok,[O,NQ]} - end. - - -point(N,K) -> - gen_server:cast(N,{point,K}). -reset(N,K) -> - gen_server:cast(N,{reset,K}). -cheat(N,K,V) -> - gen_server:cast(N,{cheat,{K,V}}). -score(N,R,L,U) -> - gen_server:cast(N,{score,{L,U,R}}). -fetch(N,R,T,L,U) -> - gen_server:cast(N,{fetch,{L,U,T,R}}). -state(N) -> - gen_server:call(N,state,90000). - - - -handle_cast({point,K},[O,Q]) -> - [NO,NQ] = point_handler(K,O,Q), - {noreply,[NO,NQ]}; -handle_cast({cheat,{K,V}},[O,Q]) -> - [NO,NQ] = cheat_handler(K,V,O,Q), - {noreply,[NO,NQ]}; -handle_cast({reset,K},[O,Q]) -> - [NO,NQ] = reset_handler(K,O,Q), - {noreply,[NO,NQ]}; -handle_cast({score,{L,U,R}},[O,Q]) -> - [NO,NQ] = score_handler(L,U,R,O,Q), - {noreply,[NO,NQ]}; -handle_cast({fetch,{L,U,T,R}},[O,Q]) -> - [NO,NQ] = fetch_handler(L,U,T,R,O,Q), - {noreply,[NO,NQ]}. - -handle_call(state,_From,[O,Q]) -> - {reply,[O,Q],[O,Q]}. - -handle_info({'EXIT',_P,normal},[O,Q]) -> - {noreply,[O,Q]}; -handle_info({'EXIT',P,R},[O,Q]) -> - io:format("Process: ~p over by reason: ~p~n",[P,R]), - {noreply,[O,Q]}; -handle_info(_,[O,Q]) -> - {noreply,[O,Q]}. - - -point_handler(K,O,Q) -> - case get(K) of - undefined -> - put(K,?MIN_LIMIT*O+1), - if - O == 0 -> - if - ?SCORE_OFFSET == 0 -> - [O,Q+1]; - true -> - [O,Q] - end; - true -> - [O,Q+1] - end; - C -> - put(K,C+1), - if - (C+1) / (?SCORE_OFFSET+1) == 1 -> - [O,Q+1]; - true -> - [O,Q] - end - end. -cheat_handler(K,V,O,Q) -> - put(K,V), - if - V > ?SCORE_OFFSET -> - [O,Q+1]; - true -> - [O,Q] - end. -reset_handler(K,O,Q) -> - V = erase(K), - if - V > ?SCORE_OFFSET -> - [O,Q-1]; - true -> - [O,Q] - end. -score_handler(L,U,R,O,Q) -> - C = if O > 0 orelse (L == ?SCORE_OFFSET+1 andalso U == ?MIN_LIMIT*(O+1)) -> Q; true -> scoring(L,U) end, - lfu:score(R,C), - [O,Q]. -fetch_handler(L,U,T,R,O,Q) -> - if - Q > 0 -> - catch insert(L,U,T); - true -> skip - end, - lfu:fetch(R,ready), - [O,Q]. - - -scoring(L,U) -> - put(counter,0.0), - lfu_utils:for(L,U, - fun(I) -> - put(counter,get(counter) + length(get_keys(I))) - end - ), - get(counter). - -insert(L,U,T) -> - lfu_utils:for(L,U, - fun(I) -> - KL = get_keys(I), - KL =/= [] andalso ets:insert(T,{I,KL}) - end - ). - -restorage(T,L,U,O) -> - if - O == 0 -> - ets:foldl( - fun({K,V},Q) -> - if - V >= L andalso V =< U -> - put(K,V), - if - V > ?SCORE_OFFSET -> Q + 1; - true -> Q - end; - true -> - Q - end - end, - 0,T); - true -> - ets:foldl( - fun({K,V},Q) -> - if - V >= L andalso V =< U -> - put(K,V), - Q + 1; - true -> - Q - end - end, - 0,T) - end. diff --git a/src/lfu_exact_score_sup.erl b/src/lfu_exact_score_sup.erl deleted file mode 100644 index 9d59013..0000000 --- a/src/lfu_exact_score_sup.erl +++ /dev/null @@ -1,36 +0,0 @@ --module(lfu_exact_score_sup). --author('VSolenkov'). - --behavior(supervisor). - --export([ - start_link/0 -]). --export([ - start/1, - stop/0 -]). - --export([ - init/1 -]). - - -start_link() -> - supervisor:start_link({local,?MODULE},?MODULE,[]). - - -init(_) -> - {ok,{ - {simple_one_for_one,100,300},[{ - exact_score,{lfu_exact_score,start,[]}, - permanent,brutal_kill,worker,[lfu_exact_score] - }] - }}. - - -start([O,Q]) -> - supervisor:start_child(?MODULE,[[O,Q]]). - -stop() -> - exit(whereis(?MODULE),shutdown). diff --git a/src/lfu_protocol.erl b/src/lfu_protocol.erl index dbb51f8..306a4d5 100644 --- a/src/lfu_protocol.erl +++ b/src/lfu_protocol.erl @@ -52,7 +52,10 @@ common(info,{tcp,S,<<"CHEAT:",P/binary>>},[S,T]) -> fun(KV) -> case binary:split(KV,<<",">>,[global]) of [K,V] -> - {true,{K,binary_to_integer(V)}}; + case catch binary_to_integer(V) of + {'EXIT',_} -> false; + I -> {true,{K,I}} + end; _ -> false end @@ -89,10 +92,11 @@ common(info,{tcp,S,<<"COUNT:",P/binary>>},[S,T]) -> common(info,{tcp,S,<<"STATE",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> T:setopts(S,[{active,once}]), case lfu:state() of - [O,Q] -> - BO = integer_to_binary(O), + [L,M,Q] -> + BL = integer_to_binary(L), + BM = integer_to_binary(M), BQ = integer_to_binary(Q), - T:send(S,<<"{","O",":",BO/binary,",","Q",":",BQ/binary,"}">>); + T:send(S,<<"{","L",":",BL/binary,",","M",":",BM/binary,",","Q",":",BQ/binary,"}">>); _ -> T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) end, @@ -106,123 +110,76 @@ common(info,{tcp,S,<<"STORE",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<" T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) end, keep_state_and_data; -common(info,{tcp,S,<<"SCORE",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> - T:setopts(S,[{active,once}]), - case lfu:score() of - ready -> - T:send(S,<<"READY">>); - _ -> - T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) - end, - keep_state_and_data; common(info,{tcp,S,<<"FETCH",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> T:setopts(S,[{active,once}]), - case catch ets:info(lfu:fetch()) of - I when is_list(I) -> - BD = ets:foldl( - fun({K,V},BA) -> - BK = integer_to_binary(K), - BV = pack_list_to_binary(V,<<>>), - case BA of - <<>> -> - <<"{",BK/binary,":",BV/binary,"}">>; - BA -> - <> - end - end, - <<>>,proplists:get_value(id,I)), - T:send(S,<<"[",BD/binary,"]">>); + case lfu:fetch() of + {C,KL} when is_list(KL) -> + BC = integer_to_binary(C), + BK = pack_list_to_binary(KL,<<>>), + BD = <<"{",BC/binary,":",BK/binary,"}">>, + T:send(S,BD); _ -> T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) end, keep_state_and_data; -common(info,{tcp,S,<<"CLEAN",":","SYNC",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> +common(info,{tcp,S,<<"CLEAN:SYNC",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> T:setopts(S,[{active,once}]), case lfu:clean(sync) of - {TID,R} when is_reference(TID) andalso is_reference(R) -> - BD = ets:foldl( - fun({K,V},BA) -> - BK = integer_to_binary(K), - BV = pack_list_to_binary(V,<<>>), - case BA of - <<>> -> - <<"{",BK/binary,":",BV/binary,"}">>; - BA -> - <> - end - end, - <<>>,TID), + {{C,KL},R} when is_list(KL) andalso is_reference(R) -> + BC = integer_to_binary(C), + BK = pack_list_to_binary(KL,<<>>), + BD = <<"{",BC/binary,":",BK/binary,"}">>, BR = list_to_binary(ref_to_list(R)), T:send(S,<<"{","[",BD/binary,"]",":",BR/binary,"}">>), - {next_state,delete,[S,T,#{ref => BR, tid => TID}],[{state_timeout,?TIMEOUT_STATE_DELETE,BR}]}; + {next_state,delete,[S,T,#{ref => BR, key => {C,KL}}],[{state_timeout,?TIMEOUT_STATE_DELETE,BR}]}; _ -> T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>), keep_state_and_data end; -common(info,{tcp,S,<<"CLEAN",":","ASYNC",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> +common(info,{tcp,S,<<"CLEAN:ASYNC",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> T:setopts(S,[{active,once}]), - case lfu:clean(async) of - TID when is_reference(TID) -> - BD = ets:foldl( - fun({K,V},BA) -> - BK = integer_to_binary(K), - BV = pack_list_to_binary(V,<<>>), - case BA of - <<>> -> - <<"{",BK/binary,":",BV/binary,"}">>; - BA -> - <> - end - end, - <<>>,TID), - T:send(S,<<"[",BD/binary,"]">>), - keep_state_and_data; + case lfu:clean() of + {C,KL} when is_list(KL) -> + BC = integer_to_binary(C), + BK = pack_list_to_binary(KL,<<>>), + BD = <<"{",BC/binary,":",BK/binary,"}">>, + T:send(S,BD); _ -> - T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>), - keep_state_and_data - end; + T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) + end, + keep_state_and_data; common(info,{tcp,S,<<"CLEAN",":",_P/binary>>},[S,T]) -> T:setopts(S,[{active,once}]), T:send(S,<<"{","ERROR",":","EXPIRED_REF","}">>), keep_state_and_data; common(info,{tcp,S,<<"CLEAN",E/binary>>},[S,T]) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> T:setopts(S,[{active,once}]), - case lfu:clean(async) of - TID when is_reference(TID) -> - BD = ets:foldl( - fun({K,V},BA) -> - BK = integer_to_binary(K), - BV = pack_list_to_binary(V,<<>>), - case BA of - <<>> -> - <<"{",BK/binary,":",BV/binary,"}">>; - BA -> - <> - end - end, - <<>>,TID), - T:send(S,<<"[",BD/binary,"]">>), - keep_state_and_data; + case lfu:clean() of + {C,KL} when is_list(KL) -> + BC = integer_to_binary(C), + BK = pack_list_to_binary(KL,<<>>), + BD = <<"{",BC/binary,":",BK/binary,"}">>, + T:send(S,BD); _ -> - T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>), - keep_state_and_data - end; + T:send(S,<<"{","ERROR",":","UNKNOW_ERROR","}">>) + end, + keep_state_and_data; common(info,{tcp,S,_B},[S,T]) -> T:setopts(S,[{active,once}]), T:send(S,<<"{","ERROR",":","UNKNOW_COMMAND","}">>), keep_state_and_data. -delete(state_timeout,BR,[S,T,#{ref := BR, tid := _T}]) -> +delete(state_timeout,BR,[S,T,#{ref := BR, key := _K}]) -> {next_state,common,[S,T]}; delete(info,{tcp,_S,<<"CLEAN:ASYNC",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; delete(info,{tcp,_S,<<"CLEAN:SYNC",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; -delete(info,{tcp,S,<<"CLEAN",":",P/binary>>},[S,T,#{ref := BR, tid := TID}]) -> +delete(info,{tcp,S,<<"CLEAN",":",P/binary>>},[S,T,#{ref := BR, key := K}]) -> T:setopts(S,[{active,once}]), case break_binary_string(byte_size(P),P) =:= BR of true -> - case lfu:clean(list_to_ref(binary_to_list(BR)),TID) of + case lfu:clean(list_to_ref(binary_to_list(BR)),K) of ok -> T:send(S,<<"OK">>), {next_state,common,[S,T]}; @@ -234,18 +191,16 @@ delete(info,{tcp,S,<<"CLEAN",":",P/binary>>},[S,T,#{ref := BR, tid := TID}]) -> T:send(S,<<"{","ERROR",":","UNKNOW_REF","}">>), keep_state_and_data end; -delete(info,{tcp,_S,<<"POINT:",_P/binary>>},_StateData) -> +delete(info,{tcp,_S,<<"POINT:",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; -delete(info,{tcp,_S,<<"CHEAT:",_P/binary>>},_StateData) -> +delete(info,{tcp,_S,<<"CHEAT:",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; -delete(info,{tcp,_S,<<"COUNT:",_P/binary>>},_StateData) -> +delete(info,{tcp,_S,<<"COUNT:",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; delete(info,{tcp,_S,<<"STATE",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; delete(info,{tcp,_S,<<"STORE",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; -delete(info,{tcp,_S,<<"SCORE",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> - {keep_state_and_data,[postpone]}; delete(info,{tcp,_S,<<"FETCH",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> {keep_state_and_data,[postpone]}; delete(info,{tcp,_S,<<"CLEAN",E/binary>>},_StateData) when E =:= <<>> orelse E =:= <<"\r\n">> orelse E =:= <<"\n">> orelse E =:= <<"\r">> -> @@ -256,17 +211,6 @@ delete(info,{tcp,S,_B},[S,T,_MD]) -> keep_state_and_data. -pack_ets_to_binary([],B) -> <<"[",B/binary,"]">>; -pack_ets_to_binary([{K,V}|T],B) -> - BC = integer_to_binary(K), - BV = pack_list_to_binary(V,<<>>), - case B of - <<>> -> - pack_ets_to_binary(T,<<"{",BC/binary,":",BV/binary,"}">>); - B -> - pack_ets_to_binary(T,<>) - end. - pack_list_to_binary([],B) -> <<"[",B/binary,"]">>; pack_list_to_binary([H|T],B) -> case B of @@ -275,7 +219,7 @@ pack_list_to_binary([H|T],B) -> B -> pack_list_to_binary(T,<>) end. - + break_binary_string(S,B) -> case B of <> -> diff --git a/src/lfu_quick_score.erl b/src/lfu_quick_score.erl deleted file mode 100644 index 4e70e41..0000000 --- a/src/lfu_quick_score.erl +++ /dev/null @@ -1,133 +0,0 @@ --module(lfu_quick_score). --author('VSolenkov'). - --behavior('gen_server'). - --export([ - start/1, - state/1, - point/2, - reset/2, - cheat/3, - score/2, - fetch/3 -]). - --export([ - init/1 -]). --export([ - handle_cast/2, - handle_call/3, - handle_info/2 -]). --include("include/lfu.hrl"). - - -start([O,Q]) -> - gen_server:start_link( - {local,list_to_atom("o" ++ integer_to_list(O))}, - ?MODULE,[O,Q], - [{spawn_opt,?SPAWN_OPT_QUICK_SCORE}]). - - -init([O,Q]) -> - case ets:lookup(?ETS_PIDS_STORE_TABLE_NAME,list_to_binary(?PREFIX_KEY ++ "o" ++ integer_to_list(O) ++ ?POSTFIX_KEY)) of - [] -> - ets:insert(?ETS_PIDS_STORE_TABLE_NAME,{ - list_to_binary(?PREFIX_KEY ++ "o" ++ integer_to_list(O) ++ ?POSTFIX_KEY), - self()}), - {ok,[O,Q]}; - _ -> - NQ = restorage(?ETS_KEYS_STORE_TABLE_NAME,?MAX_LIMIT*O+1,?MAX_LIMIT*(O+1)), - ets:insert(?ETS_PIDS_STORE_TABLE_NAME,{ - list_to_binary(?PREFIX_KEY ++ "o" ++ integer_to_list(O) ++ ?POSTFIX_KEY), - self()}), - {ok,[O,NQ]} - end. - -point(N,K) -> - gen_server:cast(N,{point,K}). -reset(N,K) -> - gen_server:cast(N,{reset,K}). -cheat(N,K,V) -> - gen_server:cast(N,{cheat,{K,V}}). -score(N,R) -> - gen_server:cast(N,{score,R}). -fetch(N,R,T) -> - gen_server:cast(N,{fetch,{T,R}}). -state(N) -> - gen_server:call(N,state,90000). - - - -handle_cast({point,K},[O,Q]) -> - NQ = point_handler(K,Q), - {noreply,[O,NQ]}; -handle_cast({cheat,{K,V}},[O,Q]) -> - NQ = cheat_handler(K,V,Q), - {noreply,[O,NQ]}; -handle_cast({reset,K},[O,Q]) -> - NQ = reset_handler(K,Q), - {noreply,[O,NQ]}; -handle_cast({score,R},[O,Q]) -> - score_handler(R,Q), - {noreply,[O,Q]}; -handle_cast({fetch,{T,R}},[O,Q]) -> - fetch_handler(T,R,O,Q), - {noreply,[O,Q]}. - -handle_call(state,_From,[O,Q]) -> - {reply,[O,Q],[O,Q]}. - -handle_info({'EXIT',_P,normal},[O,Q]) -> - {noreply,[O,Q]}; -handle_info({'EXIT',P,R},[O,Q]) -> - io:format("Process: ~p over by reason: ~p~n",[P,R]), - {noreply,[O,Q]}; -handle_info(_,[O,Q]) -> - {noreply,[O,Q]}. - - -point_handler(K,Q) -> - case get(K) of - undefined -> - put(K,1), - Q+1; - _ -> - Q - end. -cheat_handler(K,V,Q) -> - put(K,V), - Q+1. -reset_handler(K,Q) -> - erase(K), - Q-1. -score_handler(R,Q) -> - lfu:score(R,Q). -fetch_handler(T,R,O,Q) -> - if - Q > 0 -> - catch insert(O,T), - lfu:fetch(R,ready); - true -> - lfu:fetch(R,ready) - end. - - -insert(I,T) -> - KL = get_keys(1), - KL =/= [] andalso ets:insert(T,{I*?MAX_LIMIT,KL}). - -restorage(T,L,U) -> - ets:foldl( - fun({K,V},Q) -> - if - V >= L andalso V =< U -> - put(K,1), - Q + 1; - true -> - Q - end - end, - 0,T). diff --git a/src/lfu_quick_score_sup.erl b/src/lfu_quick_score_sup.erl deleted file mode 100644 index 14703c7..0000000 --- a/src/lfu_quick_score_sup.erl +++ /dev/null @@ -1,36 +0,0 @@ --module(lfu_quick_score_sup). --author('VSolenkov'). - --behavior(supervisor). - --export([ - start_link/0 -]). --export([ - start/1, - stop/0 -]). - --export([ - init/1 -]). - - -start_link() -> - supervisor:start_link({local,?MODULE},?MODULE,[]). - - -init(_) -> - {ok,{ - {simple_one_for_one,100,300},[{ - quick_score,{lfu_quick_score,start,[]}, - permanent,brutal_kill,worker,[lfu_quick_score] - }] - }}. - - -start([O,Q]) -> - supervisor:start_child(?MODULE,[[O,Q]]). - -stop() -> - exit(whereis(?MODULE),shutdown). diff --git a/src/lfu_score_sups_sup.erl b/src/lfu_score_sups_sup.erl deleted file mode 100644 index 3a28812..0000000 --- a/src/lfu_score_sups_sup.erl +++ /dev/null @@ -1,37 +0,0 @@ --module(lfu_score_sups_sup). --author('VSolenkov'). - --behavior(supervisor). - --export([ - start_link/0 -]). --export([ - stop/0 -]). - --export([ - init/1 -]). - - -start_link() -> - supervisor:start_link({local,?MODULE},?MODULE,[]). - - -init(_) -> - {ok,{ - {one_for_one,1,1},[ - { - exact_score_sup,{lfu_exact_score_sup,start_link,[]}, - permanent,5000,supervisor,[lfu_exact_score_sup] - }, - { - quick_score_sup,{lfu_quick_score_sup,start_link,[]}, - permanent,5000,supervisor,[lfu_quick_score_sup] - } - ] - }}. - -stop() -> - exit(whereis(?MODULE),shutdown). diff --git a/src/lfu_sup.erl b/src/lfu_sup.erl index ac6b8cd..55e61df 100644 --- a/src/lfu_sup.erl +++ b/src/lfu_sup.erl @@ -17,18 +17,14 @@ start_link() -> - {ok,PID} = supervisor:start_link({local,?MODULE},?MODULE,[?ETS_KEYS_STORE_TABLE_NAME,?ETS_PIDS_STORE_TABLE_NAME]), - {ok,PID,[?ETS_KEYS_STORE_TABLE_NAME,?ETS_PIDS_STORE_TABLE_NAME]}. + {ok,PID} = supervisor:start_link({local,?MODULE},?MODULE,[?ETS_KEYS_STORE_TABLE_NAME]), + {ok,PID,[?ETS_KEYS_STORE_TABLE_NAME]}. init(ETS_TABLES) -> init_tables(ETS_TABLES), {ok,{ {rest_for_one,5,300},[ - { - lfu_score_sups_sup,{lfu_score_sups_sup,start_link,[]}, - permanent,5000,supervisor,[lfu_score_sups_sup] - }, { lfu,{lfu,start_link,[]}, permanent,5000,worker,[lfu]