Skip to content

Commit

Permalink
Merge pull request duomark#4 from duomark/master
Browse files Browse the repository at this point in the history
TS-1178 Cache refresh item with no_value_available
  • Loading branch information
georgeye committed Feb 3, 2015
2 parents 07d5d74 + b06f755 commit 2bdb22d
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 51 deletions.
4 changes: 2 additions & 2 deletions example/cxy_synch_trace.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2014, DuoMark International, Inc.
%%% @copyright (c) 2014-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]> [http://duomark.com/]
%%% @reference 2013 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2014-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down
52 changes: 34 additions & 18 deletions src/cxy_cache.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down Expand Up @@ -67,7 +67,7 @@
-type cached_key() :: term().
-type cached_value() :: term().
-type cached_value_vsn() :: term().
-callback create_key_value(cached_key()) -> {cached_value_vsn(), cached_value()}.
-callback create_key_value(cached_key()) -> {cached_value_vsn(), cached_value()} | no_value_available.

%% Return 'true' if Vsn2 later than Vsn1, otherwise 'false'.
%% -optional_callback is_later_version(Vsn1::cached_value_vsn(), Vsn2::cached_value_vsn()) -> boolean().
Expand Down Expand Up @@ -308,13 +308,12 @@ replace_check_generation_fun(Cache_Name, Fun)
%%%------------------------------------------------------------------------------

-spec delete_item (cache_name(), cached_key()) -> true.
-spec fetch_item (cache_name(), cached_key()) -> cached_value() | {error, tuple()}.
-spec refresh_item (cache_name(), cached_key()) -> cached_value() | {error, tuple()}.
-spec fetch_item (cache_name(), cached_key()) -> cached_value() | no_value_available | {error, tuple()}.
-spec refresh_item (cache_name(), cached_key()) -> cached_value() | no_value_available | {error, tuple()}.
-spec refresh_item (cache_name(), cached_key(), {cached_value_vsn(), cached_value()})
-> cached_value() | {error, tuple()}.
-spec fetch_item_version (cache_name(), cached_key()) -> cached_value_vsn() | {} | {error, tuple()}.
-spec get_and_clear_counts(cache_name())
-> {cache_name(), proplists:proplist()}.
-> cached_value() | no_value_available | {error, tuple()}.
-spec fetch_item_version (cache_name(), cached_key()) -> cached_value_vsn() | no_value_available | {error, tuple()}.
-spec get_and_clear_counts(cache_name()) -> {cache_name(), proplists:proplist()}.

-define(WHEN_GEN_EXISTS(__Gen_Id, __Code), ets:info(__Gen_Id, type) =:= set andalso __Code).

Expand Down Expand Up @@ -357,7 +356,7 @@ fetch_item(Cache_Name, Key) ->
Not_Found_Fn = fun(Fn_Cache_Name, Fn_New_Gen_Id, Fn_Old_Gen_Id, Fn_Mod, Fn_Key, _Object) ->
copy_old_value_if_found(Fn_Cache_Name, Fn_New_Gen_Id, Fn_Old_Gen_Id, Fn_Mod,Fn_Key)
end,
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, {}).
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, no_value_available).

%% Fetch from Generation 1 or 2, after updating with a newer key version or leaving existing newest key version.
refresh_item(Cache_Name, Key) ->
Expand All @@ -370,7 +369,7 @@ refresh_item(Cache_Name, Key) ->
Fn_Mod,Fn_Key,Fn_Obj),
return_cache_miss(Fn_Cache_Name, Fn_New_Cached_Value)
end,
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, {}).
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, no_value_available).

%% Fetch from Generation 1 or 2, after updating with a newer obj version or leaving existing newest obj version.
refresh_item(Cache_Name, Key, {Possibly_New_Vsn, Possibly_New_Value} = Possibly_New_Object) ->
Expand All @@ -393,7 +392,7 @@ fetch_item_version(Cache_Name, Key) ->
Not_Found_Fn = fun(Fn_Cache_Name, _New_Gen_Id, Fn_Old_Gen_Id, _Mod, Fn_Key, _Obj) ->
fetch_gen2_version(Fn_Cache_Name, Fn_Old_Gen_Id, Fn_Key)
end,
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, {}).
access_item(Cache_Name, Key, Found_Fn, Not_Found_Fn, no_value_available).

%% Old generation function for getting just the version of a cached value entry.
fetch_gen2_version(Cache_Name, Old_Gen_Id, Key) ->
Expand All @@ -403,12 +402,13 @@ fetch_gen2_version(Cache_Name, Old_Gen_Id, Key) ->
[#cxy_cache_value{key=Key, version=Version}] ->
Version;
[] ->
{}
no_value_available
end.

%% Migrate an old value forward to the new generation and then clobber
%% if the key generates a newer version. Otherwise leave the existing
%% version in place.
%% version in place, unless there is now no_value_available, in which
%% case the entry is deleted from the cache.
refresh_item(Type, Cache_Name, New_Gen_Id, Old_Gen_Id, Mod, Key, Object) ->
try ets:lookup(Old_Gen_Id, Key) of

Expand All @@ -419,7 +419,22 @@ refresh_item(Type, Cache_Name, New_Gen_Id, Old_Gen_Id, Mod, Key, Object) ->
[#cxy_cache_value{key=Key} = Old_Obj] ->
insert_to_new_gen(Cache_Name, New_Gen_Id, Mod, Old_Obj),
%% Then try to clobber it with a newly created value.
refresh_now(Type, Cache_Name, New_Gen_Id, Mod, Key, Object)
Cached_Value = case {Type, Object} of
{key, _} ->
create_new_value(Cache_Name, New_Gen_Id, Mod, Key);
{obj, no_value_available} ->
no_value_available;
{obj, _} ->
{Possibly_New_Vsn, Possibly_New_Value} = Object,
insert_value_if_newer(Cache_Name, New_Gen_Id, Mod,
Key, Possibly_New_Vsn, Possibly_New_Value)
end,
case Cached_Value of
no_value_available -> ?WHEN_GEN_EXISTS(New_Gen_Id, ets:delete(New_Gen_Id, Key)),
?WHEN_GEN_EXISTS(Old_Gen_Id, ets:delete(Old_Gen_Id, Key)),
return_cache_miss(Cache_Name, no_value_available);
{_Version, _Value} -> return_cache_refresh(Cache_Name, Cached_Value)
end
catch
%% Old generation was likely eliminated by another request, try creating a new value.
%% The value will get inserted into 'old_gen' because New_Gen_Id must've been demoted.
Expand Down Expand Up @@ -456,7 +471,8 @@ copy_old_value_if_found(Cache_Name, New_Gen_Id, Old_Gen_Id, Mod, Key) ->

cache_miss(Cache_Name, New_Gen_Id, Mod, Key) ->
case create_new_value(Cache_Name, New_Gen_Id, Mod, Key) of
{error, _} = Error -> Error;
{error, _} = Error -> return_cache_miss(Cache_Name, Error);
no_value_available -> return_cache_miss(Cache_Name, no_value_available);
Cached_Value -> return_cache_miss(Cache_Name, Cached_Value)
end.

Expand Down Expand Up @@ -501,8 +517,8 @@ get_and_clear_counts(Cache_Name) ->
%% Create a new value from the cache Mod:create_key_value(Key) but watch for errors in generating it.
create_new_value(Cache_Name, New_Gen_Id, Mod, Key) ->
try Mod:create_key_value(Key) of
{Version, Value} ->
insert_value_if_newer(Cache_Name, New_Gen_Id, Mod, Key, Version, Value)
no_value_available -> no_value_available;
{Version, Value} -> insert_value_if_newer(Cache_Name, New_Gen_Id, Mod, Key, Version, Value)
catch Type:Class ->
Error = {error, {Type,Class, {creating_new_value_with, Mod, Key}}},
return_cache_error(Cache_Name, Error)
Expand Down
4 changes: 2 additions & 2 deletions src/cxy_cache_fsm.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down
4 changes: 2 additions & 2 deletions src/cxy_cache_sup.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down
32 changes: 18 additions & 14 deletions src/cxy_ctl.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]> [http://duomark.com/]
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down Expand Up @@ -238,8 +238,8 @@ adjust_task_limits(Task_Limits) ->
%% many, execute the task inline. Returns neither a pid nor a result.
%% @end

-spec execute_task(atom(), atom(), atom(), list()) -> ok.
-spec execute_task(atom(), atom(), atom(), list(), dict_props()) -> ok.
-spec execute_task(atom(), atom(), atom(), list()) -> ok.
-spec execute_task(atom(), atom(), atom(), list(), all_keys | dict_props()) -> ok.

execute_task(Task_Type, Mod, Fun, Args) ->
internal_execute_task(Task_Type, Mod, Fun, Args, inline, none).
Expand All @@ -254,8 +254,9 @@ execute_task(Task_Type, Mod, Fun, Args, Dict_Props) ->
%% many, return {max_pids, Max} without executing, rather than ok.
%% @end

-spec maybe_execute_task(atom(), atom(), atom(), list()) -> ok | {max_pids, non_neg_integer()}.
-spec maybe_execute_task(atom(), atom(), atom(), list(), dict_props()) -> ok | {max_pids, non_neg_integer()}.
-spec maybe_execute_task(atom(), atom(), atom(), list()) -> ok | {max_pids, non_neg_integer()}.
-spec maybe_execute_task(atom(), atom(), atom(), list(),
all_keys | dict_props()) -> ok | {max_pids, non_neg_integer()}.

maybe_execute_task(Task_Type, Mod, Fun, Args) ->
internal_execute_task(Task_Type, Mod, Fun, Args, refuse, none).
Expand Down Expand Up @@ -294,8 +295,8 @@ internal_execute_task(Task_Type, Mod, Fun, Args, Over_Limit_Action, Dict_Props)
%% if inlined.
%% @end

-spec execute_pid_link(atom(), atom(), atom(), list()) -> pid() | {inline, any()}.
-spec execute_pid_link(atom(), atom(), atom(), list(), dict_props()) -> pid() | {inline, any()}.
-spec execute_pid_link(atom(), atom(), atom(), list()) -> pid() | {inline, any()}.
-spec execute_pid_link(atom(), atom(), atom(), list(), all_keys | dict_props()) -> pid() | {inline, any()}.

execute_pid_link(Task_Type, Mod, Fun, Args) ->
internal_execute_pid(Task_Type, Mod, Fun, Args, link, inline, none).
Expand All @@ -309,8 +310,9 @@ execute_pid_link(Task_Type, Mod, Fun, Args, Dict_Props) ->
%% many, return {max_pids, Max_Count} instead of linked pid.
%% @end

-spec maybe_execute_pid_link(atom(), atom(), atom(), list()) -> pid() | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_link(atom(), atom(), atom(), list(), dict_props()) -> pid() | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_link(atom(), atom(), atom(), list()) -> pid() | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_link(atom(), atom(), atom(), list(),
all_keys | dict_props()) -> pid() | {max_pids, non_neg_integer()}.

maybe_execute_pid_link(Task_Type, Mod, Fun, Args) ->
internal_execute_pid(Task_Type, Mod, Fun, Args, link, refuse, none).
Expand All @@ -325,8 +327,9 @@ maybe_execute_pid_link(Task_Type, Mod, Fun, Args, Dict_Props) ->
%% so the process can be monitored, or results if inlined.
%% @end

-spec execute_pid_monitor(atom(), atom(), atom(), list()) -> {pid(), reference()} | {inline, any()}.
-spec execute_pid_monitor(atom(), atom(), atom(), list(), dict_props()) -> {pid(), reference()} | {inline, any()}.
-spec execute_pid_monitor(atom(), atom(), atom(), list()) -> {pid(), reference()} | {inline, any()}.
-spec execute_pid_monitor(atom(), atom(), atom(), list(),
all_keys | dict_props()) -> {pid(), reference()} | {inline, any()}.

execute_pid_monitor(Task_Type, Mod, Fun, Args) ->
internal_execute_pid(Task_Type, Mod, Fun, Args, monitor, inline, none).
Expand All @@ -340,8 +343,9 @@ execute_pid_monitor(Task_Type, Mod, Fun, Args, Dict_Props) ->
%% many, return {max_pids, Max_Count} instead of {pid(), reference()}.
%% @end

-spec maybe_execute_pid_monitor(atom(), atom(), atom(), list()) -> {pid(), reference()} | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_monitor(atom(), atom(), atom(), list(), dict_props()) -> {pid(), reference()} | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_monitor(atom(), atom(), atom(), list()) -> {pid(), reference()} | {max_pids, non_neg_integer()}.
-spec maybe_execute_pid_monitor(atom(), atom(), atom(), list(),
all_keys | dict_props()) -> {pid(), reference()} | {max_pids, non_neg_integer()}.

maybe_execute_pid_monitor(Task_Type, Mod, Fun, Args) ->
internal_execute_pid(Task_Type, Mod, Fun, Args, monitor, refuse, none).
Expand Down
4 changes: 2 additions & 2 deletions src/cxy_synch.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2014, DuoMark International, Inc.
%%% @copyright (c) 2014-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]> [http://duomark.com/]
%%% @reference 2013 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2014-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down
3 changes: 2 additions & 1 deletion src/epocxy.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et


{application, epocxy,
[
{id, "epocxy"},
{vsn, "0.9.8c"},
{vsn, "0.9.8d"},
{description, "Erlang Patterns of Concurrency"},
{modules, [
ets_buffer,
Expand Down
4 changes: 2 additions & 2 deletions src/ets_buffer.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]> [http://duomark.com/]
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down
8 changes: 4 additions & 4 deletions test/epocxy/cxy_cache_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2014, DuoMark International, Inc.
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2014 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
Expand Down Expand Up @@ -380,11 +380,11 @@ validate_force_refresh(Type, Cache_Name, Obj_Record_Type, Obj_Instance_Key, Old_

%% Now test the old generation with refresh...
ct:comment("Create a new generation for cache: ~p", [Cache_Name]),
{} = ?TM:fetch_item_version(Cache_Name, missing_object),
no_value_available = ?TM:fetch_item_version(Cache_Name, missing_object),
Expected_Frog = ?TM:fetch_item(Cache_Name, Obj_Instance_Key),
true = ?TM:maybe_make_new_generation(Cache_Name),
true = check_version(Type, Cache_Name, Obj_Instance_Key, New_Time),
{} = ?TM:fetch_item_version(Cache_Name, missing_object),
no_value_available = ?TM:fetch_item_version(Cache_Name, missing_object),

%% Refresh the old generation item...
Expected_Frog = refresh(Type, Cache_Name, Obj_Instance_Key, {Old_Time, Expected_Frog}),
Expand Down
20 changes: 16 additions & 4 deletions test/epocxy/cxy_ctl_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
%%% Tests for cxy_ctl using common test.
%%%
%%% @since 0.9.6
%%% @end
%%%------------------------------------------------------------------------------
-module(cxy_ctl_SUITE).
-auth('[email protected]').
-vsn('').
Expand Down Expand Up @@ -123,7 +135,7 @@ check_execute_task(_Config) ->
true = ?TM:init(Limits),
Ets_Table = ets:new(check_execute_task, [public, named_table]),

try
_ = try
%% Inline update the shared ets table...
ok = ?TM:execute_task(Inline_Type, ets, insert_new, [Ets_Table, {joe, 5}]),
[{joe, 5}] = ets:lookup(Ets_Table, joe),
Expand Down Expand Up @@ -191,7 +203,7 @@ check_execute_pid_link(_Config) ->

%% When inline, update our process dictionary...
Old_Joe = erase(joe),
try
_ = try
{inline, undefined} = ?TM:execute_pid_link(Inline_Type, erlang, put, [joe, 5]),
5 = get(joe),
{inline, 5} = ?TM:execute_pid_link(Inline_Type, erlang, put, [joe, 7]),
Expand All @@ -202,7 +214,7 @@ check_execute_pid_link(_Config) ->
%% When spawned, it affects a new process dictionary, not ours.
Self = self(),
Old_Joe = erase(joe),
try
_ = try
undefined = get(joe),
New_Pid = ?TM:execute_pid_link(Spawn_Type, ?MODULE, put_pdict, [joe, 5]),
false = (New_Pid =:= Self),
Expand Down Expand Up @@ -386,7 +398,7 @@ get_pdict() ->

filter_pdict() -> [{K, V} || {{cxy_ctl, K}, V} <- get()].

-spec fetch_ages() -> pdict_timeout | {get_dict, pid(), proplists:proplist()}.
-spec fetch_ages() -> pdict_timeout | {get_pdict, pid(), proplists:proplist()}.
fetch_ages() -> get_pdict().

-spec fetch_ets_ages(atom() | ets:tid()) -> ok.
Expand Down
12 changes: 12 additions & 0 deletions test/epocxy/ets_buffer_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2013-2015, DuoMark International, Inc.
%%% @author Jay Nelson <[email protected]>
%%% @reference 2013-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
%%% Tests for ets_buffer using common test.
%%%
%%% @since 0.9.6
%%% @end
%%%------------------------------------------------------------------------------
-module(ets_buffer_SUITE).
-auth('[email protected]').
-vsn('').
Expand Down
Loading

0 comments on commit 2bdb22d

Please sign in to comment.