-module(dns_pending_wheel). -export([start/0, insert/2, lookup/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, {read_concurrency, true}, {write_concurrency, true}]), ets:new(dns_pending_wheel, [bag, public, named_table, {read_concurrency, true}, {write_concurrency, true}]), start_scanner(). -spec insert(Key :: any(), Val :: any()) -> ok. insert(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 lookup(Key :: any()) -> [term()]. lookup(Key) -> ets:lookup(dns_pending_data, Key). -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).