summaryrefslogtreecommitdiff
path: root/src/es.erl
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2014-09-09 16:29:53 +0200
committerLinus Nordberg <linus@nordberg.se>2014-09-09 16:52:57 +0200
commitfbf64f31f34a12a9fc983f74bec27e5fbbe85f34 (patch)
tree74ba2a26673c4686f0baaad658918e1cf216bc59 /src/es.erl
parente7a1ca43af470b6e99c0e11d845903f9bf00c723 (diff)
New hash tree implementation, using an ETS table for the hashes.
Also, add an untested entry storage implementation, using multiple DETS tables.
Diffstat (limited to 'src/es.erl')
-rw-r--r--src/es.erl72
1 files changed, 72 insertions, 0 deletions
diff --git a/src/es.erl b/src/es.erl
new file mode 100644
index 0000000..9e8be8d
--- /dev/null
+++ b/src/es.erl
@@ -0,0 +1,72 @@
+%%% Copyright (c) 2014, NORDUnet A/S.
+%%% See LICENSE for licensing information.
+%%%
+%%% Entry storage.
+%%%
+%%% BUGS: Retrieving entries don't update list of tables. The effects
+%%% are that 1) dets:open_file is called for each retrieval and 2)
+%%% tables only read from won't get closed by close/1.
+
+-module(es).
+-export_type([entry_store/0]).
+-export([open/0, close/1, store/3, retrieve/2]).
+-import(lists, [filtermap/2, map/2, foreach/2, nth/2, flatten/1]).
+
+-record(entry_store, {tables :: list()}). % [dets:tab_name()]
+-type entry_store() :: #entry_store{}.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Public interface.
+-spec open() -> entry_store().
+open() ->
+ Files = filtermap(fun(F) -> dets:is_dets_file(F) end,
+ filelib:wildcard("es*.dat")),
+ Tables = map(fun(F) -> list_to_atom(filename:basename(F, ".dat")) end,
+ Files),
+ foreach(fun(T) -> T = open_file(T) end, Tables),
+ #entry_store{tables = Tables}.
+
+-spec close(entry_store()) -> ok.
+close(#entry_store{tables = Tables}) ->
+ foreach(fun(Tab) -> dets:close(Tab) end, Tables).
+
+-spec store(entry_store(), non_neg_integer(), binary()) -> entry_store().
+store(Store, Index, Entry) ->
+ {Tables, Tab} = get_table(Store#entry_store.tables, Index),
+ true = dets:insert_new(Tab, {Index, Entry}),
+ Store#entry_store{tables = Tables}.
+
+-spec retrieve(entry_store(), non_neg_integer()) -> binary() | notfound.
+retrieve(#entry_store{tables = Tables}, Index) ->
+ {_, Tab} = get_table(Tables, Index),
+ case dets:lookup(Tab, Index) of
+ [E] -> element(2, E);
+ [] -> notfound;
+ Error -> exit(Error)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Internal functions.
+%%-define(TABLE_SIZE, 25000000). % < 2e9 / 32 + 8
+-define(TABLE_SIZE, 256). % For testing.
+-define(AUTO_SAVE_INTERVAL, 30000). % Milliseconds.
+
+-spec get_table(list(), non_neg_integer()) -> {list(), dets:tab_name()}.
+get_table(Tables, Index) ->
+ TableIndex = trunc(Index / ?TABLE_SIZE) + 1,
+ if TableIndex =< length(Tables) ->
+ {Tables, nth(TableIndex, Tables)};
+ true ->
+ Tab = open_file(list_to_atom(flatten(
+ io_lib:format("es~p", [TableIndex])))),
+ {Tables ++ [Tab], Tab} % Order is important, efficiency not.
+ end.
+
+-spec open_file(atom()) -> dets:tab_name().
+open_file(Name) ->
+ {ok, Tab} = dets:open_file(Name, [{type, set},
+ {file, atom_to_list(Name)++".dat"},
+ {auto_save, ?AUTO_SAVE_INTERVAL},
+ {min_no_slots, ?TABLE_SIZE},
+ {max_no_slots, ?TABLE_SIZE}]),
+ Tab.