diff --git a/apps/sdlan/include/dns_proxy.hrl b/apps/sdlan/include/dns_proxy.hrl index 6ff16e7..7000b7f 100644 --- a/apps/sdlan/include/dns_proxy.hrl +++ b/apps/sdlan/include/dns_proxy.hrl @@ -8,6 +8,9 @@ %%%------------------------------------------------------------------- -author("anlicheng"). +-define(PENDING_TAB, ets_dns_pending_queries). +-define(PENDING_INDEX_TAB, ets_dns_expire_index). + -record(dns_cache, { %% {Qname, QType, QClass} key, diff --git a/apps/sdlan/src/dns_proxy/dns_pending_queries.erl b/apps/sdlan/src/dns_proxy/dns_pending_queries.erl new file mode 100644 index 0000000..7bea076 --- /dev/null +++ b/apps/sdlan/src/dns_proxy/dns_pending_queries.erl @@ -0,0 +1,46 @@ +%%%------------------------------------------------------------------- +%%% @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 new file mode 100644 index 0000000..ae9dac5 --- /dev/null +++ b/apps/sdlan/src/dns_proxy/dns_pending_scanner.erl @@ -0,0 +1,51 @@ +%%%------------------------------------------------------------------- +%%% @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