add wheel

This commit is contained in:
anlicheng 2025-12-16 23:44:50 +08:00
parent 4bd53d0afb
commit 46a4d1e015
4 changed files with 70 additions and 97 deletions

View File

@ -1,46 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 16. 12 2025 14:38
%%%-------------------------------------------------------------------
-module(dns_pending_queries).
-author("anlicheng").
-include("dns_proxy.hrl").
%% API
-export([new/0, put/3, take/1, delete/1]).
-spec new() -> no_return().
new() ->
ets:new(?PENDING_TAB, [set, public, {read_concurrency, true}, {write_concurrency, true}]),
ets:new(?PENDING_INDEX_TAB, [bag, public, {read_concurrency, true}, {write_concurrency, true}]).
-spec put(Key :: any(), Value :: any(), TTL :: integer()) -> no_return().
put(Key, Value, TTL) when is_integer(TTL) ->
ExpireAt = erlang:monotonic_time(millisecond) + TTL,
ets:insert(?PENDING_TAB, {Key, {Value, ExpireAt}}),
ets:insert(?PENDING_INDEX_TAB, {ExpireAt, Key}).
-spec take(Key :: any()) -> error | {ok, Value :: any()}.
take(Key) ->
case ets:lookup(?PENDING_TAB, Key) of
[{Key, {Value, ExpireAt}}] ->
ets:delete(?PENDING_TAB, Key),
ets:delete(?PENDING_INDEX_TAB, ExpireAt),
{ok, Value};
[] ->
error
end.
-spec delete(Key :: any()) -> no_return().
delete(Key) ->
case ets:lookup(?PENDING_TAB, Key) of
[{Key, {_Value, ExpireAt}}] ->
ets:delete(?PENDING_TAB, Key),
ets:delete(?PENDING_INDEX_TAB, ExpireAt);
[] ->
ok
end.

View File

@ -1,51 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 16. 12 2025 14:46
%%%-------------------------------------------------------------------
-module(dns_pending_scanner).
-author("anlicheng").
-include("dns_proxy.hrl").
%%
-define(MAX_NUM, 500).
-record(state, {
}).
%% API
-export([start_link/0, init/0]).
start_link() ->
{ok, spawn_link(?MODULE, init, [])}.
init() ->
cleaner_loop(#state{}).
cleaner_loop(State) ->
Now = erlang:monotonic_time(millisecond),
do_cleanup(Now, ?MAX_NUM),
cleaner_loop(State).
do_cleanup(Now, Max) ->
do_cleanup(Now, Max, ets:first(?PENDING_INDEX_TAB)).
do_cleanup(_Now, 0, _Key) ->
ok;
do_cleanup(_Now, _Max, '$end_of_table') ->
ok;
do_cleanup(Now, Max, ExpireAt) when ExpireAt =< Now ->
case ets:lookup(?PENDING_INDEX_TAB, ExpireAt) of
[{ExpireAt, Key}] ->
ets:delete(?PENDING_INDEX_TAB, ExpireAt),
ets:delete(?PENDING_TAB, Key),
do_cleanup(Now, Max - 1, ets:next(?PENDING_INDEX_TAB, ExpireAt));
[] ->
do_cleanup(Now, Max - 1, ets:next(?PENDING_INDEX_TAB, ExpireAt))
end;
do_cleanup(_Now, _Max, _ExpireAt) ->
ok.

View File

@ -0,0 +1,69 @@
-module(dns_pending_wheel).
-export([start/0, put/2, get/1, delete/1]).
-define(TTL, 5).
-define(TICK_MS, 1000).
-define(WHEEL_SIZE, (?TTL + 1)).
%%% =====================================================
%%% Public API
%%% =====================================================
start() ->
ets:new(dns_pending_data, [ordered_set, public, named_table]),
ets:new(dns_pending_wheel, [bag, public, named_table]),
start_scanner().
-spec put(Key :: any(), Val :: any()) -> ok.
put(Key, Val) ->
Tick = now_tick(),
Slot = Tick rem ?WHEEL_SIZE,
ets:insert(dns_pending_data, {Key, {Val, Tick}}),
ets:insert(dns_pending_wheel, {Slot, {Key, Tick}}),
ok.
-spec get(Key :: any()) -> error | {ok, Val :: any()}.
get(Key) ->
case ets:lookup(dns_pending_data, Key) of
[{Key, {Val, _Tick}}] ->
{ok, Val};
[] ->
error
end.
-spec delete(Key :: any()) -> ok.
delete(Key) ->
ets:delete(dns_pending_data, Key),
ok.
%%% =====================================================
%%% Internal
%%% =====================================================
start_scanner() ->
{ok, spawn_link(fun tick_loop/0)}.
%% Tick, Tick + 1
tick_loop() ->
Tick = now_tick(),
CleanSlot = (Tick + 1) rem ?WHEEL_SIZE,
spawn(fun() -> clean_slot(CleanSlot) end),
timer:sleep(?TICK_MS),
tick_loop().
clean_slot(Slot) ->
Items = ets:lookup(dns_pending_wheel, Slot),
true = ets:delete(dns_pending_wheel, Slot),
lists:foreach(fun({_, {Key, InsertTick}}) ->
case ets:lookup(dns_pending_data, Key) of
[{Key, {_Val, InsertTick}}] ->
ets:delete(dns_pending_data, Key);
_ ->
ok
end
end, Items).
-spec now_tick() -> integer().
now_tick() ->
erlang:system_time(second).

View File

@ -19,6 +19,7 @@ start(_StartType, _StartArgs) ->
%%
sdlan_hostname_regedit:init(),
sdlan_domain_regedit:init(),
dns_pending_wheel:start(),
start_http_server(),
start_tcp_server(),