Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Commit

Permalink
use monic_file_lru to keep cap on open files.
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Newson committed Mar 13, 2011
1 parent 84b33ab commit cce2543
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 38 deletions.
58 changes: 28 additions & 30 deletions src/monic_lru.erl → src/monic_file_lru.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,46 @@
%% License for the specific language governing permissions and limitations under
%% the License.

%% Remove least recently used items when capacity is reached.
%% Remove least recently used files when capacity is reached.

-module(monic_lru).
-module(monic_file_lru).
-behavior(gen_server).

%% public API
-export([start_link/2, update/2]).
-export([start_link/1, update/1]).

%% gen_server API
-export([init/1, terminate/2, code_change/3,handle_call/3, handle_cast/2, handle_info/2]).

-record(state, {
by_time,
by_item,
capacity,
eviction_fun
by_file,
capacity
}).

%% public functions.

start_link(Capacity, EvictionFun) when is_function(EvictionFun) ->
gen_server:start_link(?MODULE, {Capacity, EvictionFun}, []).
start_link(Capacity) when is_integer(Capacity) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Capacity, []).

update(File) when is_pid(File) ->
gen_server:call(?MODULE, {update, File}).

%% gen_server callbacks

init({0, _}) ->
{stop, capacity_too_low};
init({Capacity, EvictionFun}) ->
init(Capacity) ->
State = #state{
capacity = Capacity,
eviction_fun = EvictionFun,
by_item = ets:new(monic_by_item, [set, private]),
by_file = ets:new(monic_by_file, [set, private]),
by_time = ets:new(monic_by_time, [ordered_set, private])
},
{ok, State}.

update(Pid, Item) ->
gen_server:call(Pid, {update, Item}).

handle_call({update, Item}, _From, State) ->
maybe_evict_items(State),
update_item(Item, State),
handle_call({update, File}, _From, State) ->
maybe_evict_files(State),
update_file(File, State),
{reply, ok, State}.

handle_cast(_Msg, State) ->
Expand All @@ -62,33 +60,33 @@ handle_cast(_Msg, State) ->
handle_info(_Info, State) ->
{noreply, State}.

terminate(_Reason, #state{by_item=ByItem,by_time=ByTime,eviction_fun=EvictionFun}) ->
lists:foreach(fun({Item, _}) -> EvictionFun(Item) end, ets:tab2list(ByItem)),
ets:delete(ByItem),
terminate(_Reason, #state{by_file=ByFile,by_time=ByTime}) ->
lists:foreach(fun({File, _}) -> monic_file:close(File) end, ets:tab2list(ByFile)),
ets:delete(ByFile),
ets:delete(ByTime).

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

maybe_evict_items(#state{by_item=ByItem, by_time=ByTime,
capacity=Capacity, eviction_fun=EvictionFun}) ->
case ets:info(ByItem, size) of
maybe_evict_files(#state{by_file=ByFile, by_time=ByTime,
capacity=Capacity}) ->
case ets:info(ByFile, size) of
Capacity ->
[{OldestTime, OldestItem}] = ets:lookup(ByTime, ets:first(ByTime)),
true = ets:delete(ByItem, OldestItem),
[{OldestTime, OldestFile}] = ets:lookup(ByTime, ets:first(ByTime)),
true = ets:delete(ByFile, OldestFile),
true = ets:delete(ByTime, OldestTime),
EvictionFun(OldestItem);
monic_file:close(OldestFile);
_ ->
ok
end.

update_item(Item, #state{by_item=ByItem, by_time=ByTime}) ->
case ets:lookup(ByItem, Item) of
update_file(File, #state{by_file=ByFile, by_time=ByTime}) ->
case ets:lookup(ByFile, File) of
[{_, PrevTime}] ->
true = ets:delete(ByTime, PrevTime);
[] ->
ok
end,
Now = now(),
true = ets:insert(ByItem, {Item, Now}),
true = ets:insert(ByTime, {Now, Item}).
true = ets:insert(ByFile, {File, Now}),
true = ets:insert(ByTime, {Now, File}).
2 changes: 1 addition & 1 deletion src/monic_file_resource.erl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ create_file(ReqData, Context) ->
end.

add_item(ReqData, Context) ->
case monic_file:open(monic_utils:path(ReqData, Context)) of
case monic_utils:open(ReqData, Context) of
{ok, Pid} ->
Size = list_to_integer(wrq:get_req_header("Content-Length", ReqData)),
StreamBody = wrq:stream_req_body(ReqData, ?BUFFER_SIZE),
Expand Down
5 changes: 2 additions & 3 deletions src/monic_item_resource.erl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ init(ConfigProps) ->
resource_exists(ReqData, Context) ->
Key = list_to_integer(wrq:path_info(key, ReqData)),
Cookie = list_to_integer(wrq:path_info(cookie, ReqData)),
Exists = case monic_file:open(monic_utils:path(ReqData, Context)) of
Exists = case monic_utils:open(ReqData, Context) of
{ok, Pid} ->
case monic_file:info(Pid, Key, Cookie) of
{ok, _} ->
Expand All @@ -48,10 +48,9 @@ resource_exists(ReqData, Context) ->
{Exists, ReqData, Context}.

fetch(ReqData, Context) ->
Path = monic_utils:path(ReqData, Context),
Key = list_to_integer(wrq:path_info(key, ReqData)),
Cookie = list_to_integer(wrq:path_info(cookie, ReqData)),
case monic_file:open(Path) of
case monic_utils:open(ReqData, Context) of
{ok, Pid} ->
{ok, StreamBody} = monic_file:read(Pid, Key, Cookie),
{{stream, StreamBody}, ReqData, Context};
Expand Down
6 changes: 3 additions & 3 deletions src/monic_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ init([]) ->
Web = {webmachine_mochiweb,
{webmachine_mochiweb, start, [WebConfig]},
permanent, 5000, worker, dynamic},
LruConfig = [100, fun monic_file:close/1],
Lru = {monic_lru,
{monic_lru, start_link, LruConfig},
LruConfig = [100],
Lru = {monic_file_lru,
{monic_file_lru, start_link, LruConfig},
permanent, 5000, worker, dynamic},
Processes = [Web, Lru],
{ok, { {one_for_one, 10, 10}, Processes} }.
11 changes: 10 additions & 1 deletion src/monic_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
% the License.

-module(monic_utils).
-export([path/2, exists/2]).
-export([path/2, exists/2, open/2]).
-export([pread_header/2, pwrite_header/3]).
-export([pread_footer/2, pwrite_footer/3]).
-export([read_index/1, write_index/2]).
Expand All @@ -29,6 +29,15 @@ path(ReqData, Context) ->
File = wrq:path_info(file, ReqData),
filename:join(Root, File).

open(ReqData, Context) ->
case monic_file:open(path(ReqData, Context)) of
{ok, Pid} ->
monic_file_lru:update(Pid),
{ok, Pid};
Else ->
Else
end.

exists(ReqData, Context) ->
filelib:is_file(path(ReqData, Context)).

Expand Down

0 comments on commit cce2543

Please sign in to comment.