From fbf64f31f34a12a9fc983f74bec27e5fbbe85f34 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Tue, 9 Sep 2014 16:29:53 +0200 Subject: New hash tree implementation, using an ETS table for the hashes. Also, add an untested entry storage implementation, using multiple DETS tables. --- src/es.erl | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/es.erl (limited to 'src/es.erl') 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. -- cgit v1.1