summaryrefslogtreecommitdiff
path: root/src/perm.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/perm.erl')
-rw-r--r--src/perm.erl96
1 files changed, 78 insertions, 18 deletions
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) ->