blob: 2d410d9052ad2b67b3c4452f05d7c7a2639db370 [file] [log] [blame]
% 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(couch_expiring_cache_tests).
-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch_expiring_cache/include/couch_expiring_cache.hrl").
-define(CACHE_NAME, atom_to_binary(?MODULE, utf8)).
% max int 64 bit
-define(FOREVER, 576460752303423488).
couch_expiring_cache_basic_test_() ->
{
"Test expiring cache basics",
{
setup,
fun setup_couch/0,
fun teardown_couch/1,
{
foreach,
fun setup/0,
fun teardown/1,
[
fun simple_lifecycle/1
]
}
}
}.
setup_couch() ->
test_util:start_couch([fabric, couch_jobs]).
teardown_couch(Ctx) ->
test_util:stop_couch(Ctx).
setup() ->
Opts = #{
cache_name => ?CACHE_NAME,
period => 10,
max_jitter => 0
},
{ok, Pid} = couch_expiring_cache_server:start_link(?MODULE, Opts),
true = unlink(Pid),
#{pid => Pid}.
teardown(#{pid := Pid}) ->
exit(Pid, kill).
simple_lifecycle(_) ->
% The entire test is racing against FDB being faster than timeout seconds
{timeout, 20,
?_test(begin
Start = couch_expiring_cache_server:now_ts(),
% Race Alert!
% We're betting on FDB returning a lookup faster than these:
Stale = 500,
Expires = 1000,
Timeout = 5000,
Interval = 5,
StaleTS = Start + Stale,
ExpiresTS = Start + Expires,
Name = ?CACHE_NAME,
Key = <<"key">>,
Val = <<"val">>,
?assertEqual(ok, couch_expiring_cache_fdb:clear_all(Name)),
?assertEqual(not_found, couch_expiring_cache:lookup(Name, Key)),
?assertEqual([], entries(Name)),
?assertEqual(
ok,
couch_expiring_cache:insert(
Name,
Key,
Val,
StaleTS,
ExpiresTS
)
),
ok = attempt_fresh_and_stale_lookups(Name, Key, Timeout, Interval),
% Refresh the existing key with updated timestamps
Refresh = couch_expiring_cache_server:now_ts(),
?assertEqual(
ok,
couch_expiring_cache:insert(
Name,
Key,
Val,
Refresh + Stale,
Refresh + Expires
)
),
ok = attempt_fresh_and_stale_lookups(Name, Key, Timeout, Interval),
?assertEqual(1, length(entries(Name))),
% These last 2 are also races, betting on FDB to be reasonably
% fast on the home stretch
ok = wait_lookup(Name, Key, expired, Timeout, Interval),
ok = wait_lookup(Name, Key, not_found, Timeout, Interval),
?assertEqual([], entries(Name))
end)}.
% In this race we're betting on FDB to take less than `Stale` and then
% `Expired` milliseconds to respond
attempt_fresh_and_stale_lookups(Name, Key, Timeout, Interval) ->
case couch_expiring_cache:lookup(Name, Key) of
{fresh, Val} ->
% We won that race, let's bet on another!
ok = wait_lookup(Name, Key, {stale, Val}, Timeout, Interval);
_ ->
% Unlucky! But don't fail the test just yet...
ok
end.
entries(Name) ->
couch_expiring_cache_fdb:get_range_to(Name, ?FOREVER, _Limit = 100).
% This lookup races against Timeout
wait_lookup(Name, Key, Expect, Timeout, Interval) ->
wait(
fun() ->
case couch_expiring_cache:lookup(Name, Key) of
Expect -> ok;
_ -> wait
end
end,
Timeout,
Interval
).
wait(Fun, Timeout, Delay) ->
Now = couch_expiring_cache_server:now_ts(),
wait(Fun, Timeout, Delay, Now, Now).
wait(_Fun, Timeout, _Delay, Started, Prev) when Prev - Started > Timeout ->
timeout;
wait(Fun, Timeout, Delay, Started, _Prev) ->
case Fun() of
wait ->
% http://erlang.org/doc/man/timer.html#sleep-1
% always millisecond
ok = timer:sleep(Delay),
wait(
Fun,
Timeout,
Delay,
Started,
couch_expiring_cache_server:now_ts()
);
Else ->
Else
end.