From 90760d10d14c11ee4c99826163c206bbf20a77f6 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Thu, 24 Sep 2015 16:38:03 +0200 Subject: Change perm interface to be add/commit based --- src/perm.erl | 96 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 18 deletions(-) (limited to 'src/perm.erl') diff --git a/src/perm.erl b/src/perm.erl index 5de8231..6c62b59 100644 --- a/src/perm.erl +++ b/src/perm.erl @@ -2,7 +2,83 @@ %%% See LICENSE for licensing information. -module(perm). --export([ensurefile/3, ensurefile_nosync/3, readfile/2]). +-behaviour(gen_server). + +-export([start_link/2, stop/1, init_directory_table/0]). +-export([getvalue/2, addvalue/3, commit/1, commit/2]). + +%% gen_server callbacks. +-export([init/1, handle_call/3, terminate/2, handle_cast/2, handle_info/2, + code_change/3]). + +-record(state, {name, dirtylistname}). + +-define(DIRECTORY_TABLE, perm_directory). + +init_directory_table() -> + case ets:info(?DIRECTORY_TABLE) of + undefined -> ok; + _ -> ets:delete(?DIRECTORY_TABLE) + end, + ets:new(?DIRECTORY_TABLE, [set, public, named_table]). + +start_link(Name, Filename) -> + gen_server:start_link({local, Name}, ?MODULE, + [Name, Filename], []). + +stop(Name) -> + gen_server:call(Name, stop). + +addfsyncfiles(Name, Files) -> + gen_server:call(Name, {addfsyncfiles, Files}). + +init([Name, Filename]) -> + Dirtylistname = list_to_atom(atom_to_list(Name) ++ "_dirty"), + true = ets:insert(?DIRECTORY_TABLE, {Name, Filename}), + ets:new(Dirtylistname, [set, public, named_table]), + {ok, #state{name = Name, dirtylistname = Dirtylistname}}. + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + io:format("~p terminating~n", [?MODULE]), + ok. + +handle_call({addfsyncfiles, Files}, _From, State) -> + Insert = lists:map(fun (File) -> {File} end, Files), + ets:insert(State#state.dirtylistname, Insert), + {reply, ok, State}; +handle_call({commit, Timeout}, _From, State) -> + lager:debug("doing commit for ~p", [State#state.name]), + Files = lists:map(fun ([File]) -> File end, ets:match(State#state.dirtylistname, {'$1'})), + util:fsync(Files, Timeout), + ets:delete_all_objects(State#state.dirtylistname), + {reply, ok, State}; +handle_call(stop, _From, State) -> + {stop, normal, stopped, State}. + +getvalue(Name, Key) -> + [{_, Filename}] = ets:lookup(?DIRECTORY_TABLE, Name), + readfile(Filename, Key). + +addvalue(Name, Key, Value) -> + [{_, Filename}] = ets:lookup(?DIRECTORY_TABLE, Name), + {Result, FsyncFiles} = ensurefile(Filename, Key, Value), + addfsyncfiles(Name, FsyncFiles), + Result. + +commit(Name) -> + commit(Name, 5000). + +commit(Name, Timeout) -> + gen_server:call(Name, {commit, Timeout}, Timeout). -spec readfile_and_verify(string(), binary()) -> ok | differ | {error, atom()}. readfile_and_verify(Name, Content) -> @@ -47,15 +123,7 @@ path_for_key(Rootdir, Key) -> Fullpath = Thirdlevel ++ "/" ++ Name, {[Firstlevel, Secondlevel, Thirdlevel], Fullpath}. --spec ensurefile(string(), binary(), binary()) -> ok | differ. ensurefile(Rootdir, Key, Content) -> - ensurefile(Rootdir, Key, Content, sync). - --spec ensurefile_nosync(string(), binary(), binary()) -> ok | differ. -ensurefile_nosync(Rootdir, Key, Content) -> - ensurefile(Rootdir, Key, Content, nosync). - -ensurefile(Rootdir, Key, Content, Syncflag) -> lager:debug("dir ~p key ~s", [Rootdir, mochihex:to_hex(Key)]), {Dirs, Path} = path_for_key(Rootdir, Key), Result = @@ -81,15 +149,7 @@ ensurefile(Rootdir, Key, Content, Syncflag) -> {error, Error} -> util:exit_with_error(Error, readfile, "Error reading file") end, - case Syncflag of - sync -> - lager:debug("key ~s added, fsync", [mochihex:to_hex(Key)]), - ok = util:fsync([Path, Rootdir | Dirs]), - lager:debug("key ~s fsynced", [mochihex:to_hex(Key)]), - Result; - nosync -> - Result - end. + {Result, [Path, Rootdir | Dirs]}. -spec readfile(string(), binary()) -> binary() | noentry. readfile(Rootdir, Key) -> -- cgit v1.1