add cache select (based on ets select + match spec)

This commit is contained in:
Dmitry Kolesnikov 2014-06-27 19:56:39 +03:00
parent 3e078d11b8
commit da059104f8
8 changed files with 156 additions and 28 deletions

View File

@ -46,12 +46,7 @@ rebar:
chmod ugo+x rebar
benchmark:
$(BB)/basho_bench -N bb@127.0.0.0 -C nocookie -J ${id}@127.0.0.1 priv/${id}.benchmark
$(BB)/basho_bench -N bb@127.0.0.0 -C nocookie priv/${id}.benchmark
$(BB)/priv/summary.r -i tests/current
open tests/current/summary.png
# benchmark:
# $(BB)/basho_bench -N bb@127.0.0.0 -C nocookie -J ${id}@127.0.0.1 priv/${id}.benchmark
# $(BB)/priv/summary.r -i tests/current
# open tests/current/summary.png

View File

@ -6,14 +6,15 @@
%%
%% workload
{mode, max}.
{duration, 5}.
{duration, 1}.
{concurrent, 10}.
{key_generator, {uniform_int, 1000000}}.
{value_generator, {fixed_bin, 1000}}.
{value_generator, {fixed_bin, 1000}}.
{operations, [
{put, 5}
,{get, 5}
% ,{select, 1}
]}.
%{cache, {mycache, 'cache@127.0.0.1'}}.

View File

@ -1,7 +1,7 @@
{application, cache,
[
{description, "in-memory cache"},
{vsn, "0.10.0"},
{vsn, "0.11.0"},
{modules, [
cache,
cache_bucket,

View File

@ -31,6 +31,7 @@
-include("cache.hrl").
-export([start/0]).
-export([
start_link/2,
drop/1,
@ -50,6 +51,8 @@
remove_/2,
acc/3,
acc_/3,
select/2,
fold/4,
% memecached like interface
set/3,
set/4,
@ -79,6 +82,12 @@
-type(entity() :: any()).
-type(ttl() :: integer()).
%%
%% RnD start application
start() ->
application:start(pq),
application:start(cache).
%%
%% start new cache bucket, accepted options:
%% {policy, lru | mru} - cache eviction policy
@ -230,6 +239,61 @@ acc(Cache, Key, Val) ->
acc_(Cache, Key, Val) ->
gen_server:cast(Cache, {acc, Key, Val}).
%%
%% query cache segments using match specification
%% e.g.
%% -include_lib("stdlib/include/ms_transform.hrl").
%% cache:select(cache, ets:fun2ms(fun(X) -> X end)).
-spec(select/2 :: (cache(), function() | any()) -> {[any()], any()}).
select(Cache, {q, [Head|Tail], Req}) ->
%% compiled query
try
case ets:select(Head, Req, ?CONFIG_SELECT) of
'$end_of_table' ->
select(Cache, {q, Tail, Req});
{Result, Query} ->
{Result, {q, Tail, Req, Query}}
end
catch _:badarg ->
%% cache segment was evicted
select(Cache, {q, Tail, Req})
end;
select(_Cache, {q, [], _}=Req) ->
{eof, Req};
select(Cache, {q, Heap, Req, '$end_of_table'}) ->
select(Cache, {q, Heap, Req});
select(Cache, {q, Heap, Req, Query0}) ->
try
case ets:select(Query0) of
'$end_of_table' ->
select(Cache, {q, Heap, Req});
{Result, Query} ->
{Result, {q, Heap, Req, Query}}
end
catch _:badarg ->
%% cache segment was evicted
select(Cache, {q, Heap, Req})
end;
select(Cache, Req) ->
select(Cache, {q, lists:reverse(i(Cache, heap)), Req}).
%%
%% query cache segments and fold function
-spec(fold/4 :: (function(), any(), function(), cache()) -> any()).
fold(Fun, Acc, Req, Cache) ->
case select(Cache, Req) of
{eof, _} ->
Acc;
{List, Query} ->
fold(Fun, lists:foldl(Fun, Acc, List), Query, Cache)
end.
%%%----------------------------------------------------------------------------
%%%

View File

@ -14,6 +14,11 @@
%% limitations under the License.
%%
%%
%% define default select limit
-define(CONFIG_SELECT, 1000).
%-define(VERBOSE, true).
-ifdef(VERBOSE).
-define(DEBUG(Str, Args), error_logger:error_msg(Str, Args)).

View File

@ -18,6 +18,8 @@
-module(cache_benchmark).
-author('Dmitry Kolesnikov <dmkolesnikov@gmail.com>').
-include_lib("stdlib/include/ms_transform.hrl").
-export([new/1, run/4]).
%%
@ -43,7 +45,7 @@ run(put, KeyGen, ValGen, Cache) ->
E -> {error, failure(p, Key, E), Cache}
end;
run(get, KeyGen, _ValueGen, Cache) ->
run(get, KeyGen, _ValGen, Cache) ->
Key = KeyGen(),
case (catch cache:get(Cache, Key)) of
Val when is_binary(Val) -> {ok, Cache};
@ -51,17 +53,28 @@ run(get, KeyGen, _ValueGen, Cache) ->
E -> {error, failure(g, Key, E), Cache}
end;
run(remove, KeyGen, _ValueGen, Cache) ->
run(remove, KeyGen, _ValGen, Cache) ->
Key = KeyGen(),
case (catch cache:remove(cache, Key)) of
ok -> {ok, Cache};
E -> {error, failure(r, Key, E), Cache}
end.
end;
run(select, KeyGen, _ValGen, Cache) ->
Key0 = KeyGen(),
Key1 = KeyGen(),
cache:fold(
fun(_, Acc) -> Acc + 1 end,
0,
ets:fun2ms(fun({Key, Val}) when Key > Key0, Key < Key1 -> Key end),
cache
),
{ok, Cache}.
%%
local_init() ->
case application:start(cache) of
case cache:start() of
{error, {already_started, _}} ->
cache;
ok ->

56
src/cache_bucket_sup.erl Normal file
View File

@ -0,0 +1,56 @@
%%
%% Copyright 2012 Dmitry Kolesnikov, All Rights Reserved
%%
%% 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.
%%
%% @description
%% root cache supervisor
-module(cache_bucket_sup).
-behaviour(supervisor).
-author('Dmitry Kolesnikov <dmkolesnikov@gmail.com>').
-author('Jose Luis Navarro <jlnavarro@gmail.com>').
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%%
-define(CHILD(Type, I), {I, {I, start_link, []}, permanent, 5000, Type, dynamic}).
-define(CHILD(Type, I, Args), {I, {I, start_link, Args}, permanent, 5000, Type, dynamic}).
-define(CHILD(Type, ID, I, Args), {ID, {I, start_link, Args}, permanent, 5000, Type, dynamic}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
{ok,
{
{one_for_one, 4, 1800},
[?CHILD(worker, Name, cache, [Name, Opts]) || {Name, Opts} <- default_cache()]
}
}.
%%
%% list of default cache specification
default_cache() ->
proplists:delete(included_applications, application:get_all_env()).

View File

@ -26,17 +26,17 @@
%% Supervisor callbacks
-export([init/1]).
%%
-define(CHILD(Type, I), {I, {I, start_link, []}, permanent, 5000, Type, dynamic}).
-define(CHILD(Type, I, Args), {I, {I, start_link, Args}, permanent, 5000, Type, dynamic}).
-define(CHILD(Type, ID, I, Args), {ID, {I, start_link, Args}, permanent, 5000, Type, dynamic}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
{ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
lists:foreach(
fun default_cache/1,
proplists:delete(included_applications, application:get_all_env())
),
{ok, Sup}.
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
@ -46,15 +46,9 @@ init([]) ->
{ok,
{
{one_for_one, 4, 1800},
[]
[
?CHILD(supervisor, cache_bucket_sup)
]
}
}.
%%
%% create default cache
default_cache({Name, Opts}) ->
{ok, _} = supervisor:start_child(?MODULE, {
Name,
{cache, start_link, [Name, Opts]},
permanent, 900000, worker, dynamic
}).