From 46a4d1e015d130678518d55a1587d07dd9368d54 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Tue, 16 Dec 2025 23:44:50 +0800 Subject: [PATCH] add wheel --- .../src/dns_proxy/dns_pending_queries.erl | 46 ------------- .../src/dns_proxy/dns_pending_scanner.erl | 51 -------------- .../sdlan/src/dns_proxy/dns_pending_wheel.erl | 69 +++++++++++++++++++ apps/sdlan/src/sdlan_app.erl | 1 + 4 files changed, 70 insertions(+), 97 deletions(-) delete mode 100644 apps/sdlan/src/dns_proxy/dns_pending_queries.erl delete mode 100644 apps/sdlan/src/dns_proxy/dns_pending_scanner.erl create mode 100644 apps/sdlan/src/dns_proxy/dns_pending_wheel.erl diff --git a/apps/sdlan/src/dns_proxy/dns_pending_queries.erl b/apps/sdlan/src/dns_proxy/dns_pending_queries.erl deleted file mode 100644 index 7bea076..0000000 --- a/apps/sdlan/src/dns_proxy/dns_pending_queries.erl +++ /dev/null @@ -1,46 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author anlicheng -%%% @copyright (C) 2025, -%%% @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. \ No newline at end of file diff --git a/apps/sdlan/src/dns_proxy/dns_pending_scanner.erl b/apps/sdlan/src/dns_proxy/dns_pending_scanner.erl deleted file mode 100644 index ae9dac5..0000000 --- a/apps/sdlan/src/dns_proxy/dns_pending_scanner.erl +++ /dev/null @@ -1,51 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author anlicheng -%%% @copyright (C) 2025, -%%% @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. \ No newline at end of file diff --git a/apps/sdlan/src/dns_proxy/dns_pending_wheel.erl b/apps/sdlan/src/dns_proxy/dns_pending_wheel.erl new file mode 100644 index 0000000..622aea1 --- /dev/null +++ b/apps/sdlan/src/dns_proxy/dns_pending_wheel.erl @@ -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). \ No newline at end of file diff --git a/apps/sdlan/src/sdlan_app.erl b/apps/sdlan/src/sdlan_app.erl index 9122000..92ac12d 100644 --- a/apps/sdlan/src/sdlan_app.erl +++ b/apps/sdlan/src/sdlan_app.erl @@ -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(),