104 lines
2.8 KiB
Erlang
104 lines
2.8 KiB
Erlang
%%%-------------------------------------------------------------------
|
|
%%% @author anlicheng
|
|
%%% @copyright (C) 2026, <COMPANY>
|
|
%%% @doc
|
|
%%% DETS backed outbound packet cache for efka_iot_client.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-module(efka_iot_cache).
|
|
-author("anlicheng").
|
|
|
|
-define(CACHE_TAB, cache).
|
|
-define(MAX_CACHE_ITEMS, 5000000).
|
|
-define(MAX_CACHE_FILE_SIZE, 1073741824).
|
|
|
|
-export([open/0, close/0, insert/1, fetch_next/0, delete/1]).
|
|
|
|
-spec open() -> ok | {error, term()}.
|
|
open() ->
|
|
{ok, DetsDir} = application:get_env(efka, dets_dir),
|
|
File = DetsDir ++ "cache.dets",
|
|
case dets:open_file(?CACHE_TAB, [{file, File}, {type, bag}, {keypos, 1}]) of
|
|
{ok, ?CACHE_TAB} ->
|
|
ok;
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end.
|
|
|
|
-spec close() -> ok.
|
|
close() ->
|
|
case dets:close(?CACHE_TAB) of
|
|
ok ->
|
|
ok;
|
|
{error, not_owner} ->
|
|
ok
|
|
end.
|
|
|
|
-spec insert(binary()) -> {ok, non_neg_integer()} | {error, term()}.
|
|
insert(Data) when is_binary(Data) ->
|
|
case dets:insert(?CACHE_TAB, {generate_cache_id(), Data}) of
|
|
ok ->
|
|
trim_limits(0);
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end.
|
|
|
|
-spec fetch_next() -> error | {ok, {integer(), binary()}}.
|
|
fetch_next() ->
|
|
case dets:first(?CACHE_TAB) of
|
|
'$end_of_table' ->
|
|
error;
|
|
Key ->
|
|
case dets:lookup(?CACHE_TAB, Key) of
|
|
[Entry | _] ->
|
|
{ok, Entry};
|
|
[] ->
|
|
fetch_next()
|
|
end
|
|
end.
|
|
|
|
-spec delete(integer()) -> ok | {error, term()}.
|
|
delete(Id) when is_integer(Id) ->
|
|
dets:delete(?CACHE_TAB, Id).
|
|
|
|
-spec generate_cache_id() -> integer().
|
|
generate_cache_id() ->
|
|
erlang:unique_integer([monotonic, positive]).
|
|
|
|
-spec over_limit() -> boolean().
|
|
over_limit() ->
|
|
item_count() > ?MAX_CACHE_ITEMS orelse file_size() > ?MAX_CACHE_FILE_SIZE.
|
|
|
|
-spec item_count() -> non_neg_integer().
|
|
item_count() ->
|
|
dets:info(?CACHE_TAB, size).
|
|
|
|
-spec file_size() -> non_neg_integer().
|
|
file_size() ->
|
|
dets:info(?CACHE_TAB, file_size).
|
|
|
|
-spec trim_limits(non_neg_integer()) -> {ok, non_neg_integer()} | {error, term()}.
|
|
trim_limits(DroppedCount) ->
|
|
case over_limit() of
|
|
true ->
|
|
case delete_oldest_entry() of
|
|
ok ->
|
|
trim_limits(DroppedCount + 1);
|
|
error ->
|
|
{ok, DroppedCount};
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end;
|
|
false ->
|
|
{ok, DroppedCount}
|
|
end.
|
|
|
|
-spec delete_oldest_entry() -> ok | error | {error, term()}.
|
|
delete_oldest_entry() ->
|
|
case dets:first(?CACHE_TAB) of
|
|
'$end_of_table' ->
|
|
error;
|
|
Key ->
|
|
dets:delete(?CACHE_TAB, Key)
|
|
end.
|