From 6de81bcb8482bb0c2576c8b594c676e6d896b426 Mon Sep 17 00:00:00 2001 From: Evgeny Levenets Date: Thu, 18 May 2017 12:48:42 +0300 Subject: [PATCH] DC-34: added naive cache displacement implementation (#15) --- config/sys.config | 4 +++ rebar.config | 6 ++-- src/dmt_client_cache.erl | 56 +++++++++++++++++++++++++++++---- test/dmt_client_tests_SUITE.erl | 4 +++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/config/sys.config b/config/sys.config index 8426f28..447864d 100644 --- a/config/sys.config +++ b/config/sys.config @@ -1,6 +1,10 @@ [ {dmt_client, [ {cache_update_interval, 5000}, % milliseconds + {max_cache_size, #{ + elements => 20, + memory => 52428800 % 50Mb + }}, {service_urls, #{ 'Repository' => <<"dominant:8022/v1/domain/repository">>, 'RepositoryClient' => <<"dominant:8022/v1/domain/repository_client">> diff --git a/rebar.config b/rebar.config index bab05ed..fe45a33 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,8 @@ %% Common project erlang options. {erl_opts, [ + {parse_transform, lager_transform}, + % mandatory debug_info, warnings_as_errors, @@ -60,10 +62,6 @@ {plt_apps, all_deps} ]}. -{erl_opts, [ - {parse_transform, lager_transform} -]}. - {plugins, [ ]}. diff --git a/src/dmt_client_cache.erl b/src/dmt_client_cache.erl index 8370356..0327655 100644 --- a/src/dmt_client_cache.erl +++ b/src/dmt_client_cache.erl @@ -36,7 +36,7 @@ start_link() -> -spec put(dmt_client:snapshot()) -> dmt_client:snapshot(). put(Snapshot) -> - ok = gen_server:call(?SERVER, {put, Snapshot}), + ok = gen_server:cast(?SERVER, {put, Snapshot}), Snapshot. -spec get(dmt_client:version()) -> {ok, dmt_client:snapshot()} | {error, version_not_found}. @@ -47,7 +47,12 @@ get(Version) -> -spec get_latest() -> {ok, dmt_client:snapshot()} | {error, version_not_found}. get_latest() -> - latest_snapshot(). + case latest_snapshot() of + {ok, Snapshot} -> + {ok, Snapshot}; + {error, version_not_found} -> + gen_server:call(?SERVER, get_latest) + end. -spec update() -> {ok, dmt_client:version()} | {error, term()}. @@ -77,17 +82,21 @@ init(_) -> -spec handle_call(term(), {pid(), term()}, state()) -> {reply, term(), state()}. -handle_call({put, Snapshot}, _From, State) -> - {reply, put_snapshot(Snapshot), State}; - handle_call(update, _From, State) -> {reply, update_cache(), restart_timer(State)}; +handle_call(get_latest, _From, State) -> + {reply, latest_snapshot(), State}; + handle_call(_Msg, _From, State) -> {noreply, State}. -spec handle_cast(term(), state()) -> {noreply, state()}. +handle_cast({put, Snapshot}, State) -> + ok = put_snapshot(Snapshot), + {noreply, State}; + handle_cast(_Msg, State) -> {noreply, State}. @@ -114,7 +123,7 @@ code_change(_OldVsn, _State, _Extra) -> put_snapshot(Snapshot) -> true = ets:insert(?TABLE, Snapshot), - ok. + cleanup(). -spec get_snapshot(dmt_client:version()) -> {ok, dmt_client:snapshot()} | {error, version_not_found}. @@ -172,3 +181,38 @@ update_cache() -> {error, Error} end. +-spec cleanup() -> ok. + +cleanup() -> + {Elements, Memory} = get_cache_size(), + CacheLimits = genlib_app:env(dmt_client, max_cache_size), + MaxElements = genlib_map:get(elements, CacheLimits, 20), + MaxMemory = genlib_map:get(memory, CacheLimits, 52428800), % 50Mb by default + case Elements > MaxElements orelse Memory > MaxMemory of + true -> + ok = remove_earliest(), + cleanup(); + false -> + ok + end. + +-spec get_cache_size() -> {non_neg_integer(), non_neg_integer()}. + +get_cache_size() -> + WordSize = erlang:system_info(wordsize), + Info = ets:info(?TABLE), + {proplists:get_value(size, Info), WordSize * proplists:get_value(memory, Info)}. + +-spec remove_earliest() -> ok. + +remove_earliest() -> + % Naive implementation, but probably good enough + remove_earliest(ets:first(?TABLE)). + +-spec remove_earliest('$end_of_table' | dmt_client:version()) -> ok. + +remove_earliest('$end_of_table') -> + ok; +remove_earliest(Key) -> + true = ets:delete(?TABLE, Key), + ok. diff --git a/test/dmt_client_tests_SUITE.erl b/test/dmt_client_tests_SUITE.erl index f520eb5..38eaec6 100644 --- a/test/dmt_client_tests_SUITE.erl +++ b/test/dmt_client_tests_SUITE.erl @@ -32,6 +32,10 @@ groups() -> init_per_suite(C) -> Apps = genlib_app:start_application_with(dmt_client, [ {cache_update_interval, 5000}, % milliseconds + {max_cache_size, #{ + elements => 1, + memory => 2048 % 2Kb + }}, {service_urls, #{ 'Repository' => <<"dominant:8022/v1/domain/repository">>, 'RepositoryClient' => <<"dominant:8022/v1/domain/repository_client">>