Current section

Files

Jump to
gibreel src g_storage.erl
Raw

src/g_storage.erl

%%
%% Copyright 2013-14 Joaquim Rocha
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
-module(g_storage).
-include("gibreel.hrl").
-include("gibreel_db.hrl").
-include("g_storage.hrl").
%% ====================================================================
%% API functions
%% ====================================================================
-export([create/3, drop/1]).
-export([insert/5, delete/3, touch/3]).
-export([find/2, keys/1, keys/2]).
-export([size/1, foldl/3]).
-export([expired/2, older/1]).
create(CacheName, Config, ?NO_STORAGE) ->
Table = create_table(CacheName),
Index = create_index(CacheName, Config),
#cache_storage{table=Table, index=Index};
create(_CacheName, _Config, DB) -> DB.
drop(#cache_storage{table=Table, index=Index}) ->
ets:delete(Table),
case Index of
none -> ok;
_ -> ets:delete(Index)
end.
insert(#cache_storage{table=Table, index=Index}, Key, Value, Version, Timeout) ->
insert_index(Key, Version, Index),
ets:insert(Table, ?DATA_RECORD(Key, Value, Version, Timeout)),
ets:update_counter(Table, ?RECORD_COUNTER, 1).
delete(#cache_storage{table=Table, index=Index}, Key, Version) ->
delete_index(Version, Index),
ets:delete(Table, Key),
ets:update_counter(Table, ?RECORD_COUNTER, -1).
touch(#cache_storage{table=Table}, Key, Timeout) ->
ets:update_element(Table, Key, {4, Timeout}).
find(DB, Key) ->
case ets:lookup(DB#cache_storage.table, Key) of
[] -> {error, not_found};
[?DATA_RECORD(_Key, Value, Version, Timeout)] -> {ok, Value, Version, Timeout}
end.
keys(#cache_storage{table=Table}) ->
Match = [{{'$1','$2','$3','$4'},[],['$1']}],
ets:select(Table, Match).
keys(#cache_storage{table=Table}, OlderThan) ->
Match = [{{'$1','$2','$3','$4'},[{'<','$3',OlderThan}],[{{'$1','$3'}}]}],
ets:select(Table, Match).
size(#cache_storage{table=Table}) ->
case ets:lookup(Table, ?RECORD_COUNTER) of
[] -> 0;
[{_Key, Count}] -> Count
end.
foldl(#cache_storage{table=Table}, Fun, Acc) when is_function(Fun, 4) ->
ets:foldl(fun(?DATA_RECORD(Key, Value, Version, _), FoldValue) -> Fun(Key, Value, Version, FoldValue);
(_, FoldValue) -> FoldValue
end, Acc, Table);
foldl(#cache_storage{table=Table}, Fun, Acc) ->
ets:foldl(fun(?DATA_RECORD(Key, Value, _, _), FoldValue) -> Fun(Key, Value, FoldValue);
(_, FoldValue) -> FoldValue
end, Acc, Table).
expired(DB, When) ->
Match = [{{'$1','$2','$3','$4'},[{'<','$4',When}],[{{'$1','$3'}}]}],
ets:select(DB#cache_storage.table, Match).
older(#cache_storage{index=?NO_INDEX_TABLE}) -> {error, not_found};
older(#cache_storage{index=Index}) ->
case ets:first(Index) of
'$end_of_table' -> ok;
Version ->
case ets:lookup(Index, Version) of
[] -> {error, not_found};
[?INDEX_RECORD(_Version, Key)] -> {ok, Key, Version}
end
end.
%% ====================================================================
%% Internal functions
%% ====================================================================
create_table(CacheName) ->
TableName = get_table_name(CacheName),
Table = ets:new(TableName, [set, public, {read_concurrency, true}, {write_concurrency, true}]),
ets:insert(Table, {?RECORD_COUNTER, 0}),
Table.
create_index(_CacheName, #cache_config{max_size=?NO_MAX_SIZE}) -> ?NO_INDEX_TABLE;
create_index(CacheName, _Config) ->
IndexName = get_index_name(CacheName),
ets:new(IndexName, [ordered_set, public, {write_concurrency, true}]).
get_table_name(CacheName) ->
make_name(CacheName, "_gcache_table").
get_index_name(CacheName) ->
make_name(CacheName, "_gcache_index").
make_name(Name, Posfix) ->
LName = atom_to_list(Name),
ConcatName = LName ++ Posfix,
list_to_atom(ConcatName).
insert_index(_Key, _Version, ?NO_INDEX_TABLE) -> ok;
insert_index(Key, Version, Index) ->
ets:insert(Index, ?INDEX_RECORD(Version, Key)).
delete_index(_Version, ?NO_INDEX_TABLE) -> ok;
delete_index(Version, Index) ->
ets:delete(Index, Version).