diff --git a/.travis.yml b/.travis.yml index 132cdf7..b2f3aed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ -install: - - make rebar3 -script: - - make && make test +script: make && make test language: erlang otp_release: - - 18.2.1 - - 17.5 + - 19.2 + - 18.3 diff --git a/Makefile b/Makefile index 7645a11..1ef1996 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ ## @description ## Makefile to build and release Erlang applications using standard development tools ## -## @version 0.11.5 +## @version 0.11.8 ##################################################################### ## @@ -22,9 +22,12 @@ TEST ?= ${APP} S3 ?= VMI ?= fogfish/erlang NET ?= lo0 -URL ?= undefined +URL ?= undefined LATEST ?= latest +## rebar version (no spaces at end) +REBAR ?= 3.3.2 + ## root path to benchmark framework BB = ../basho_bench SSHENV = /tmp/ssh-agent.conf @@ -46,17 +49,17 @@ EFLAGS = \ ## self-extracting bundle wrapper BUNDLE_INIT = PREFIX=${PREFIX}\nREL=${PREFIX}/${REL}\nAPP=${APP}\nVSN=${VSN}\nLINE=`grep -a -n "BUNDLE:$$" $$0`\nmkdir -p $${REL}\ntail -n +$$(( $${LINE%%%%:*} + 1)) $$0 | gzip -vdc - | tar -C $${REL} -xvf - > /dev/null\n BUNDLE_FREE = exit\nBUNDLE:\n -BUILDER = FROM ${VMI}\nRUN mkdir ${APP}\nCOPY . ${APP}/\nRUN cd ${APP} && make && make rel\n +BUILDER = FROM ${VMI}\nARG VERSION=\nRUN mkdir ${APP}\nCOPY . ${APP}/\nRUN cd ${APP} && make VSN=\x24{VERSION} && make rel VSN=\x24{VERSION}\n CTRUN = \ - -module(test). \ - -export([run/1]). \ - run(Spec) -> \ - {ok, Test} = file:consult(Spec), \ - case lists:keyfind(node, 1, Test) of \ - false -> ct:run_test([{spec, Spec}]); \ + -module(test). \ + -export([run/1]). \ + run(Spec) -> \ + {ok, Test} = file:consult(Spec), \ + Error = case lists:keyfind(node, 1, Test) of \ + false -> element(2, ct:run_test([{spec, Spec}])); \ true -> ct_master:run(Spec) \ - end, \ - erlang:halt(). + end, \ + erlang:halt(Error). ##################################################################### ## @@ -119,7 +122,7 @@ relx.config: rel/relx.config.src @cat $< | sed 's/release/release, {${APP}, "${VSN}"}/' > $@ else ${PKG}.tar.gz: _build/dockermake - @docker build --file=$< --force-rm=true --tag=build/${APP}:latest . ;\ + @docker build --file=$< --force-rm=true --build-arg="VERSION=${VSN}" --tag=build/${APP}:latest . ;\ I=`docker create build/${APP}:latest` ;\ docker cp $$I:/${APP}/$@ $@ ;\ docker rm -f $$I ;\ @@ -136,7 +139,7 @@ docker: rel/Dockerfile --build-arg APP=${APP} \ --build-arg VSN=${VSN} \ -t ${URL}/${APP}:${VSN} -f $< . - docker tag -f ${URL}/${APP}:${VSN} ${URL}/${APP}:${LATEST} + docker tag ${URL}/${APP}:${VSN} ${URL}/${APP}:${LATEST} @@ -207,8 +210,9 @@ console: ${PKG}.tar.gz ## ##################################################################### rebar3: - @curl -L -O https://s3.amazonaws.com/rebar3/rebar3 ; \ - chmod ugo+x $@ + @echo "==> install rebar (${REBAR})" ;\ + curl -L -O --progress-bar https://github.com/erlang/rebar3/releases/download/${REBAR}/rebar3 ;\ + chmod +x $@ .PHONY: test rel deps all pkg diff --git a/src/cache.app.src b/src/cache.app.src index 45f2782..03ba8a2 100644 --- a/src/cache.app.src +++ b/src/cache.app.src @@ -1,7 +1,7 @@ {application, cache, [ {description, "in-memory cache"}, - {vsn, "2.1.1"}, + {vsn, "2.2.0"}, {modules, []}, {registered, []}, {applications,[ diff --git a/src/cache.erl b/src/cache.erl index 14b75c6..ff06ac0 100644 --- a/src/cache.erl +++ b/src/cache.erl @@ -62,7 +62,11 @@ remove/2, remove/3, remove_/2, - remove_/3 + remove_/3, + apply/3, + apply/4, + apply_/4, + apply_/3 ]). %% extended cache i/o interface -export([ @@ -303,6 +307,36 @@ remove_(Cache, Key, true) -> remove_(Cache, Key, false) -> send(Cache, {remove, Key}). +%% +%% synchronous apply function to entity on cache +%% the function maps element, the new value is returned +%% the operation prolongs value ttl +-spec apply(cache(), key(), fun((_) -> _)) -> val() | undefined. +-spec apply(cache(), key(), fun((_) -> _), timeout()) -> val() | undefined. + +apply(Cache, Key, Fun) -> + cache:apply(Cache, Key, Fun, ?CONFIG_TIMEOUT). + +apply(Cache, Key, Fun, Timeout) -> + call(Cache, {apply, Key, Fun}, Timeout). + + +%% +%% asynchronous apply function to entity on cache +%% the function maps element, the new value is returned +%% the operation prolongs value ttl +-spec apply_(cache(), key(), fun((_) -> _)) -> ok | reference(). +-spec apply_(cache(), key(), fun((_) -> _), true | false) -> ok | reference(). + +apply_(Cache, Key, Fun) -> + cache:apply_(Cache, Key, Fun, false). + +apply_(Cache, Key, Fun, true) -> + cast(Cache, {apply, Key, Fun}); + +apply_(Cache, Key, Fun, false) -> + cast(Cache, {apply, Key, Fun}). + %%%---------------------------------------------------------------------------- %%% %%% extended cache i/o interface diff --git a/src/cache_bucket.erl b/src/cache_bucket.erl index a8d9425..9210bc8 100644 --- a/src/cache_bucket.erl +++ b/src/cache_bucket.erl @@ -121,6 +121,10 @@ handle_call({ttl, Key}, _, State) -> handle_call({remove, Key}, _, State) -> {reply, ok, cache_remove(Key, State)}; +handle_call({apply, Key, Fun}, _, State0) -> + {Result, State1} = cache_apply(Key, Fun, State0), + {reply, Result, State1}; + handle_call({acc, Key, Val}, _, State0) -> {Result, State1} = cache_acc(Key, Val, State0), {reply, Result, State1}; @@ -328,6 +332,16 @@ cache_remove(Key, #cache{name=_Name, heap=Heap}=State) -> ?DEBUG("cache ~p: remove ~p~n", [_Name, Key]), State. +%% +%% +cache_apply(Key, Fun, State) -> + case Fun(cache_get(Key, State)) of + undefined -> + {undefined, State}; + Val -> + {Val, cache_put(Key, Val, undefined, State)} + end. + %% %% @todo: reduce one write cache_acc(Key, Val, State) diff --git a/test/cache_SUITE.erl b/test/cache_SUITE.erl index eb85c14..c6e3e86 100644 --- a/test/cache_SUITE.erl +++ b/test/cache_SUITE.erl @@ -42,7 +42,9 @@ lookup/1, has/1, remove/1, - remove_/1 + remove_/1, + apply/1, + apply_/1 ]). %% @@ -77,7 +79,7 @@ groups() -> {primitives, [parallel], [lifecycle]}, {basic_io, [parallel], - [put, put_, get, lookup, has, remove, remove_]}, + [put, put_, get, lookup, has, remove, remove_, apply, apply_]}, {extended_io, [parallel], [acc, set, add, replace, append, append_, prepend, prepend_, delete]} ]. @@ -169,6 +171,22 @@ remove_(_Config) -> false= cache:has(Cache, key), ok = cache:drop(Cache). +apply(_Config) -> + {ok, Cache} = cache:start_link([]), + val = cache:apply(Cache, key, fun(undefined) -> val end), + val = cache:get(Cache, key), + lav = cache:apply(Cache, key, fun(val) -> lav end), + lav = cache:get(Cache, key), + ok = cache:drop(Cache). + +apply_(_Config) -> + {ok, Cache} = cache:start_link([]), + cache:apply_(Cache, key, fun(undefined) -> val end), + val = cache:get(Cache, key), + cache:apply_(Cache, key, fun(val) -> lav end), + lav = cache:get(Cache, key), + ok = cache:drop(Cache). + %%%---------------------------------------------------------------------------- %%% %%% cache extended i/o