mirror of
https://github.com/valitydev/cache.git
synced 2024-11-06 01:45:19 +00:00
new async api, returns reference to operation
This commit is contained in:
parent
572050affc
commit
ff404f45a8
243
Makefile
243
Makefile
@ -1,52 +1,237 @@
|
|||||||
.PHONY: deps test
|
## @author Dmitry Kolesnikov, <dmkolesnikov@gmail.com>
|
||||||
|
## @copyright (c) 2012 - 2014 Dmitry Kolesnikov. All Rights Reserved
|
||||||
|
##
|
||||||
|
## @description
|
||||||
|
## Makefile to build and release Erlang applications using
|
||||||
|
## rebar, reltool, etc (see README for details)
|
||||||
|
##
|
||||||
|
## application version schema (based on semantic version)
|
||||||
|
## ${APP}-${VSN}+${GIT}.${ARCH}.${PLAT}
|
||||||
|
##
|
||||||
|
## @version 0.8.2
|
||||||
|
.PHONY: test rel deps all pkg
|
||||||
|
|
||||||
ifeq ($(id),)
|
#####################################################################
|
||||||
export id=cache
|
##
|
||||||
|
## application config
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
|
ROOT = `pwd`
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
APP ?= $(notdir $(CURDIR))
|
||||||
|
ARCH?= $(shell uname -m)
|
||||||
|
PLAT?= $(shell uname -s)
|
||||||
|
HEAD?= $(shell git rev-parse --short HEAD)
|
||||||
|
TAG = ${HEAD}.${ARCH}.${PLAT}
|
||||||
|
TEST?= ${APP}
|
||||||
|
S3 =
|
||||||
|
GIT ?=
|
||||||
|
VMI =
|
||||||
|
NET ?= lo0
|
||||||
|
USER =
|
||||||
|
PASS =
|
||||||
|
|
||||||
|
## root path to benchmark framework
|
||||||
|
BB = ../basho_bench
|
||||||
|
SSHENV = /tmp/ssh-agent.conf
|
||||||
|
ADDR = $(shell ifconfig ${NET} | sed -En 's/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')
|
||||||
|
BRANCH = $(shell git symbolic-ref --short -q HEAD)
|
||||||
|
|
||||||
|
## erlang flags (make run only)
|
||||||
|
EFLAGS = \
|
||||||
|
-name ${APP}@${ADDR} \
|
||||||
|
-setcookie nocookie \
|
||||||
|
-pa ${ROOT}/ebin \
|
||||||
|
-pa ${ROOT}/deps/*/ebin \
|
||||||
|
-pa ${ROOT}/apps/*/ebin \
|
||||||
|
-pa rel/files \
|
||||||
|
-kernel inet_dist_listen_min 32100 \
|
||||||
|
-kernel inet_dist_listen_max 32199 \
|
||||||
|
+P 1000000 \
|
||||||
|
+K true +A 160 -sbt ts
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
##
|
||||||
|
## internal config
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
|
ifeq ($(wildcard rel/reltool.config),)
|
||||||
|
REL =
|
||||||
|
VSN =
|
||||||
|
TAR =
|
||||||
|
PKG =
|
||||||
|
else
|
||||||
|
IID = $(shell cat rel/reltool.config | sed -n 's/{target_dir,.*\"\([^-]*\).*\"}./\1/p')
|
||||||
|
REL = $(shell cat rel/reltool.config | sed -n 's/{target_dir,.*\"\(.*\)\"}./\1/p')
|
||||||
|
VSN = $(shell echo ${REL} | sed -n 's/.*-\(.*\)/\1/p')
|
||||||
|
ifeq (${VSN},)
|
||||||
|
VSN = $(shell cat rel/reltool.config | sed -n 's/.*{rel,.*\".*\",.*\"\(.*\)\".*/\1/p')
|
||||||
|
endif
|
||||||
|
ifeq (${config},)
|
||||||
|
RFLAGS =
|
||||||
|
VARIANT =
|
||||||
|
else
|
||||||
|
VARIANT = $(addprefix ., $(notdir $(basename ${config})))
|
||||||
|
RFLAGS = target_dir=${REL}${VARIANT} overlay_vars=${ROOT}/${config}
|
||||||
|
endif
|
||||||
|
ifeq (${VSN},)
|
||||||
|
TAR = ${IID}${VARIANT}+${TAG}.tgz
|
||||||
|
PKG = ${IID}${VARIANT}+${TAG}.bundle
|
||||||
|
else
|
||||||
|
TAR = ${IID}-${VSN}${VARIANT}+${TAG}.tgz
|
||||||
|
PKG = ${IID}-${VSN}${VARIANT}+${TAG}.bundle
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
FLAGS=\
|
## self-extracting bundle wrapper
|
||||||
-name ${id}@127.0.0.1 \
|
BUNDLE_INIT = PREFIX=${PREFIX}\nREL=${PREFIX}/${REL}${VARIANT}\nAPP=${APP}\nVSN=${VSN}\nLINE=\`grep -a -n 'BUNDLE:\x24' \x240\`\ntail -n +\x24(( \x24{LINE\x25\x25:*} + 1)) \x240 | gzip -vdc - | tar -C ${PREFIX} -xvf - > /dev/null\n
|
||||||
-setcookie nocookie \
|
BUNDLE_FREE = exit\nBUNDLE:\n
|
||||||
-pa ./deps/*/ebin \
|
BUILDER = cd /tmp && git clone -b ${BRANCH} ${GIT}/${APP} && cd /tmp/${APP} && make && make rel && sleep 300
|
||||||
-pa ./examples/*/ebin \
|
|
||||||
-pa ./ebin \
|
|
||||||
+K true +A 160 -sbt ts
|
|
||||||
|
|
||||||
BB=../basho_bench
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
##
|
||||||
|
## build
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
all: rebar deps compile
|
all: rebar deps compile
|
||||||
|
|
||||||
compile:
|
compile:
|
||||||
./rebar compile
|
@./rebar compile
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
./rebar get-deps
|
@./rebar get-deps
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
./rebar clean
|
@./rebar clean ; \
|
||||||
rm -rf test.*-temp-data
|
rm -rf test.*-temp-data ; \
|
||||||
|
rm -rf tests ; \
|
||||||
|
rm -rf log ; \
|
||||||
|
rm -f *.tgz ; \
|
||||||
|
rm -f *.bundle
|
||||||
|
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
./rebar delete-deps
|
@./rebar delete-deps
|
||||||
|
|
||||||
test: all
|
unit: all
|
||||||
./rebar skip_deps=true eunit
|
@./rebar skip_deps=true eunit
|
||||||
|
|
||||||
|
test:
|
||||||
|
@erl ${EFLAGS} -run deb test test/${TEST}.config
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
./rebar skip_deps=true doc
|
@./rebar skip_deps=true doc
|
||||||
|
|
||||||
dialyzer: compile
|
#####################################################################
|
||||||
@dialyzer -Wno_return -c ./ebin
|
##
|
||||||
|
## release
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
|
ifneq (${REL},)
|
||||||
|
|
||||||
|
rel: ${TAR}
|
||||||
|
|
||||||
|
## assemble VM release
|
||||||
|
ifeq (${PLAT},$(shell uname -s))
|
||||||
|
${TAR}:
|
||||||
|
@./rebar generate ${RFLAGS}; \
|
||||||
|
cd rel ; tar -zcf ${TAR} ${REL}${VARIANT}/ ; mv ${TAR} ../${TAR} ; cd - ;\
|
||||||
|
echo "==> tarball: ${TAR}"
|
||||||
|
|
||||||
|
else
|
||||||
|
ifneq (${VMI},)
|
||||||
|
${TAR}:
|
||||||
|
@echo "==> docker run ${VMI}" ;\
|
||||||
|
K=`test ${PASS} && cat ${PASS}` ;\
|
||||||
|
A=`test ${USER} && echo "mkdir -p /root/.ssh && echo \"$$K\" > /root/.ssh/id_rsa && chmod 0700 /root/.ssh/id_rsa && echo -e \"Host *\n\tUser ${USER}\n\tStrictHostKeyChecking no\n\" > /root/.ssh/config &&"` ;\
|
||||||
|
I=`docker run -d -a stdout -a stderr ${VMI} /bin/sh -c "$$A ${BUILDER}"` ;\
|
||||||
|
(docker attach $$I &) ;\
|
||||||
|
docker cp $$I:/tmp/${APP}/${TAR} . 1> /dev/null 2>&1 ;\
|
||||||
|
while [ $$? -ne 0 ] ;\
|
||||||
|
do \
|
||||||
|
sleep 10 ;\
|
||||||
|
docker cp $$I:/tmp/${APP}/${TAR} . 1> /dev/null 2>&1 ;\
|
||||||
|
done ;\
|
||||||
|
docker kill $$I ;\
|
||||||
|
docker rm $$I
|
||||||
|
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
## package VM release to executable bundle
|
||||||
|
pkg: rel/deploy.sh ${TAR}
|
||||||
|
@printf "${BUNDLE_INIT}" > ${PKG} ; \
|
||||||
|
cat rel/deploy.sh >> ${PKG} ; \
|
||||||
|
printf "${BUNDLE_FREE}" >> ${PKG} ; \
|
||||||
|
cat ${TAR} >> ${PKG} ; \
|
||||||
|
chmod ugo+x ${PKG} ; \
|
||||||
|
echo "==> bundle: ${PKG}"
|
||||||
|
|
||||||
|
## copy 'package' to s3
|
||||||
|
## copy 'package' to s3
|
||||||
|
s3: ${PKG}
|
||||||
|
aws s3 cp ${PKG} ${S3}/${APP}+${TAG}${VARIANT}.bundle
|
||||||
|
|
||||||
|
s3-latest: ${PKG}
|
||||||
|
aws s3 cp ${PKG} ${S3}/${APP}+latest${VARIANT}.bundle
|
||||||
|
endif
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
##
|
||||||
|
## deploy
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
|
ifneq (${host},)
|
||||||
|
${SSHENV}:
|
||||||
|
@echo "==> ssh: config keys" ;\
|
||||||
|
ssh-agent -s > ${SSHENV}
|
||||||
|
|
||||||
|
node: ${SSHENV}
|
||||||
|
@echo "==> deploy: ${host}" ;\
|
||||||
|
. ${SSHENV} ;\
|
||||||
|
k=`basename ${pass}` ;\
|
||||||
|
l=`ssh-add -l | grep $$k` ;\
|
||||||
|
if [ -z "$$l" ] ; then \
|
||||||
|
ssh-add ${pass} ;\
|
||||||
|
fi ;\
|
||||||
|
rsync -cav --rsh=ssh --progress ${PKG} ${host}:${PKG} ;\
|
||||||
|
ssh -t ${host} "sudo sh ./${PKG}"
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
##
|
||||||
|
## debug
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
run:
|
run:
|
||||||
erl ${FLAGS}
|
@erl ${EFLAGS}
|
||||||
|
|
||||||
rebar:
|
|
||||||
curl -O https://cloud.github.com/downloads/basho/rebar/rebar
|
|
||||||
chmod ugo+x rebar
|
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
$(BB)/basho_bench -N bb@127.0.0.0 -C nocookie priv/${id}.benchmark
|
@echo "==> benchmark: ${TEST}" ;\
|
||||||
$(BB)/priv/summary.r -i tests/current
|
$(BB)/basho_bench -N bb@127.0.0.1 -C nocookie priv/${TEST}.benchmark ;\
|
||||||
|
$(BB)/priv/summary.r -i tests/current ;\
|
||||||
open tests/current/summary.png
|
open tests/current/summary.png
|
||||||
|
|
||||||
|
ifneq (${REL},)
|
||||||
|
start:
|
||||||
|
@./rel/${REL}${VARIANT}/bin/${APP} start
|
||||||
|
|
||||||
|
stop:
|
||||||
|
@./rel/${REL}${VARIANT}/bin/${APP} stop
|
||||||
|
|
||||||
|
console:
|
||||||
|
@./rel/${REL}${VARIANT}/bin/${APP} console
|
||||||
|
|
||||||
|
attach:
|
||||||
|
@./rel/${REL}${VARIANT}/bin/${APP} attach
|
||||||
|
endif
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
##
|
||||||
|
## dependencies
|
||||||
|
##
|
||||||
|
#####################################################################
|
||||||
|
rebar:
|
||||||
|
@curl -L -O https://github.com/rebar/rebar/wiki/rebar ; \
|
||||||
|
chmod ugo+x rebar
|
||||||
|
@ -8,7 +8,10 @@ The write operation always uses youngest segment. The read operation lookup key
|
|||||||
|
|
||||||
The downside is inability to assign precise TTL per single cache entry. TTL is always approximated to nearest segment. (e.g. cache with 60 sec TTL and 10 segments has 6 sec accuracy on TTL)
|
The downside is inability to assign precise TTL per single cache entry. TTL is always approximated to nearest segment. (e.g. cache with 60 sec TTL and 10 segments has 6 sec accuracy on TTL)
|
||||||
|
|
||||||
|
## Change log
|
||||||
|
|
||||||
|
* 2.0.0 - various changes on asynchronous api, not compatible with version 1.x
|
||||||
|
* 1.0.1 - production release
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -16,8 +19,13 @@ The downside is inability to assign precise TTL per single cache entry. TTL is a
|
|||||||
application:start(cache).
|
application:start(cache).
|
||||||
{ok, _} = cache:start_link(my_cache, [{n, 10}, {ttl, 60}]).
|
{ok, _} = cache:start_link(my_cache, [{n, 10}, {ttl, 60}]).
|
||||||
|
|
||||||
|
%% synchronous i/o
|
||||||
ok = cache:put(my_cache, <<"my key">>, <<"my value">>).
|
ok = cache:put(my_cache, <<"my key">>, <<"my value">>).
|
||||||
Val = cache:get(my_cache, <<"my key">>).
|
Val = cache:get(my_cache, <<"my key">>).
|
||||||
|
|
||||||
|
%% asynchronous i/o
|
||||||
|
Ref = cache:get_(my_cache, <<"my key">>).
|
||||||
|
receive {Ref, Val} -> Val end.
|
||||||
```
|
```
|
||||||
|
|
||||||
### configuration via Erlang `sys.config`
|
### configuration via Erlang `sys.config`
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
,{get, 5}
|
,{get, 5}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
%{cache, {mycache, 'cache@127.0.0.1'}}.
|
|
||||||
{local, [
|
{local, [
|
||||||
{ttl, 20}
|
{ttl, 20}
|
||||||
,{n, 10}
|
,{n, 10}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{application, cache,
|
{application, cache,
|
||||||
[
|
[
|
||||||
{description, "in-memory cache"},
|
{description, "in-memory cache"},
|
||||||
{vsn, "1.0.1"},
|
{vsn, "2.0.0"},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,[
|
{applications,[
|
||||||
|
208
src/cache.erl
208
src/cache.erl
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
-include("cache.hrl").
|
-include("cache.hrl").
|
||||||
|
|
||||||
-export([start/0]).
|
%% cache management interface
|
||||||
-export([
|
-export([
|
||||||
start_link/1,
|
start_link/1,
|
||||||
start_link/2,
|
start_link/2,
|
||||||
@ -39,20 +39,27 @@
|
|||||||
purge/1,
|
purge/1,
|
||||||
i/1,
|
i/1,
|
||||||
i/2,
|
i/2,
|
||||||
heap/2,
|
heap/2
|
||||||
|
]).
|
||||||
|
%% basic cache i/o interface
|
||||||
|
-export([
|
||||||
put/3,
|
put/3,
|
||||||
put/4,
|
put/4,
|
||||||
put_/3,
|
put_/3,
|
||||||
put_/4,
|
put_/4,
|
||||||
get/2,
|
get/2,
|
||||||
|
get_/2,
|
||||||
lookup/2,
|
lookup/2,
|
||||||
|
lookup_/2,
|
||||||
has/2,
|
has/2,
|
||||||
ttl/2,
|
ttl/2,
|
||||||
remove/2,
|
remove/2,
|
||||||
remove_/2,
|
remove_/2
|
||||||
|
]).
|
||||||
|
%% extended cache i/o interface
|
||||||
|
-export([
|
||||||
acc/3,
|
acc/3,
|
||||||
acc_/3,
|
acc_/3,
|
||||||
% memecached like interface
|
|
||||||
set/3,
|
set/3,
|
||||||
set/4,
|
set/4,
|
||||||
set_/3,
|
set_/3,
|
||||||
@ -72,21 +79,26 @@
|
|||||||
delete/2,
|
delete/2,
|
||||||
delete_/2
|
delete_/2
|
||||||
]).
|
]).
|
||||||
|
-export([start/0]).
|
||||||
|
|
||||||
-export_type([cache/0]).
|
-export_type([cache/0]).
|
||||||
|
|
||||||
-type(cache() :: atom() | {atom(), atom()} | {global, atom()} | pid()).
|
-type(cache() :: atom() | pid()).
|
||||||
-type(name() :: atom() | {global, atom()}).
|
|
||||||
-type(key() :: any()).
|
-type(key() :: any()).
|
||||||
-type(entity() :: any()).
|
-type(val() :: any()).
|
||||||
-type(ttl() :: integer()).
|
-type(ttl() :: integer()).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% RnD start application
|
%% RnD start application
|
||||||
start() ->
|
start() ->
|
||||||
application:start(pq),
|
|
||||||
application:start(cache).
|
application:start(cache).
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
%%%
|
||||||
|
%%% cache management interface
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% start new cache bucket, accepted options:
|
%% start new cache bucket, accepted options:
|
||||||
%% {type, set | ordered_set} - cache segment type (default set)
|
%% {type, set | ordered_set} - cache segment type (default set)
|
||||||
@ -99,7 +111,7 @@ start() ->
|
|||||||
%% {stats, function() | {Mod, Fun}} - cache statistic aggregate functor
|
%% {stats, function() | {Mod, Fun}} - cache statistic aggregate functor
|
||||||
%% {heir, atom() | pid()} - heir of evicted cache segments
|
%% {heir, atom() | pid()} - heir of evicted cache segments
|
||||||
-spec(start_link/1 :: (list()) -> {ok, pid()} | {error, any()}).
|
-spec(start_link/1 :: (list()) -> {ok, pid()} | {error, any()}).
|
||||||
-spec(start_link/2 :: (name(), list()) -> {ok, pid()} | {error, any()}).
|
-spec(start_link/2 :: (atom(), list()) -> {ok, pid()} | {error, any()}).
|
||||||
|
|
||||||
start_link(Opts) ->
|
start_link(Opts) ->
|
||||||
cache_bucket:start_link(Opts).
|
cache_bucket:start_link(Opts).
|
||||||
@ -111,41 +123,21 @@ start_link(Cache, Opts) ->
|
|||||||
%% drop cache
|
%% drop cache
|
||||||
-spec(drop/1 :: (cache()) -> ok).
|
-spec(drop/1 :: (cache()) -> ok).
|
||||||
|
|
||||||
drop(undefined) ->
|
drop(Cache) ->
|
||||||
ok;
|
|
||||||
drop({global, Cache}) ->
|
|
||||||
drop(global:whereis_name(Cache));
|
|
||||||
drop({Cache, Node}) ->
|
|
||||||
drop(rpc:call(Node, cache, drop, [Cache]));
|
|
||||||
drop(Cache)
|
|
||||||
when is_atom(Cache) ->
|
|
||||||
drop(whereis(Cache));
|
|
||||||
drop(Cache)
|
|
||||||
when is_pid(Cache) ->
|
|
||||||
gen_server:call(Cache, drop).
|
gen_server:call(Cache, drop).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% purge cache
|
%% purge cache
|
||||||
-spec(purge/1 :: (cache()) -> ok).
|
-spec(purge/1 :: (cache()) -> ok).
|
||||||
|
|
||||||
purge(undefined) ->
|
purge(Cache) ->
|
||||||
ok;
|
|
||||||
purge({global, Cache}) ->
|
|
||||||
purge(global:whereis_name(Cache));
|
|
||||||
purge({Cache, Node}) ->
|
|
||||||
purge(rpc:call(Node, cache, purge, [Cache]));
|
|
||||||
purge(Cache)
|
|
||||||
when is_atom(Cache) ->
|
|
||||||
purge(whereis(Cache));
|
|
||||||
purge(Cache)
|
|
||||||
when is_pid(Cache) ->
|
|
||||||
gen_server:call(Cache, purge).
|
gen_server:call(Cache, purge).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% return cache meta data
|
%% return cache meta data
|
||||||
%% {heap, [integer()]} - cache segments references
|
%% {heap, [integer()]} - references to cache segments
|
||||||
%% {expire, [integer()]} - cache segments expire times
|
%% {expire, [integer()]} - cache segments expiration times
|
||||||
%% {size, [integer()]} - cardinality of cache segments
|
%% {size, [integer()]} - cardinality of cache segments
|
||||||
%% {memory, [integer()]} - memory occupied by each cache segment
|
%% {memory, [integer()]} - memory occupied by each cache segment
|
||||||
-spec(i/1 :: (cache()) -> list()).
|
-spec(i/1 :: (cache()) -> list()).
|
||||||
@ -165,94 +157,114 @@ heap(Cache, N) ->
|
|||||||
gen_server:call(Cache, {heap, N}).
|
gen_server:call(Cache, {heap, N}).
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
%%%
|
||||||
|
%%% basic cache i/o interface
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous cache put
|
%% synchronous cache put
|
||||||
-spec(put/3 :: (cache(), key(), entity()) -> ok).
|
-spec(put/3 :: (cache(), key(), val()) -> ok).
|
||||||
-spec(put/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(put/4 :: (cache(), key(), val(), ttl()) -> ok).
|
||||||
|
|
||||||
put(Cache, Key, Val) ->
|
put(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {put, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
cache:put(Cache, Key, Val, undefined).
|
||||||
|
|
||||||
put(Cache, Key, Val, TTL) ->
|
put(Cache, Key, Val, TTL) ->
|
||||||
gen_server:call(Cache, {put, Key, Val, TTL}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {put, Key, Val, TTL}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous cache put
|
%% asynchronous cache put
|
||||||
-spec(put_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(put_/3 :: (cache(), key(), val()) -> reference()).
|
||||||
-spec(put_/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(put_/4 :: (cache(), key(), val(), ttl()) -> reference()).
|
||||||
|
|
||||||
put_(Cache, Key, Val) ->
|
put_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {put, Key, Val}).
|
cache:put_(Cache, Key, Val, undefined).
|
||||||
|
|
||||||
put_(Cache, Key, Val, TTL) ->
|
put_(Cache, Key, Val, TTL) ->
|
||||||
gen_server:cast(Cache, {put, Key, Val, TTL}).
|
cast(Cache, {put, Key, Val, TTL}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous get cache entry, the operation prolongs entry ttl
|
%% synchronous cache get, the operation prolongs value ttl
|
||||||
-spec(get/2 :: (cache(), key()) -> entity() | undefined).
|
-spec(get/2 :: (cache(), key()) -> val() | undefined).
|
||||||
|
|
||||||
get(Cache, Key) ->
|
get(Cache, Key) ->
|
||||||
gen_server:call(Cache, {get, Key}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {get, Key}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous lookup cache entry, the operation do not prolong entry ttl
|
%% asynchronous cache get, the operation prolongs value ttl
|
||||||
-spec(lookup/2 :: (cache(), key()) -> entity() | undefined).
|
-spec(get_/2 :: (cache(), key()) -> reference()).
|
||||||
|
|
||||||
|
get_(Cache, Key) ->
|
||||||
|
cast(Cache, {get, Key}).
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% synchronous cache lookup, the operation do not prolong entry ttl
|
||||||
|
-spec(lookup/2 :: (cache(), key()) -> val() | undefined).
|
||||||
|
|
||||||
lookup(Cache, Key) ->
|
lookup(Cache, Key) ->
|
||||||
gen_server:call(Cache, {lookup, Key}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {lookup, Key}).
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% asynchronous cache lookup, the operation do not prolong entry ttl
|
||||||
|
-spec(lookup_/2 :: (cache(), key()) -> reference()).
|
||||||
|
|
||||||
|
lookup_(Cache, Key) ->
|
||||||
|
cast(Cache, {lookup, Key}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% check if cache key exists,
|
%% check if cache key exists,
|
||||||
-spec(has/2 :: (cache(), key()) -> true | false).
|
-spec(has/2 :: (cache(), key()) -> true | false).
|
||||||
|
|
||||||
has(Cache, Key) ->
|
has(Cache, Key) ->
|
||||||
gen_server:call(Cache, {has, Key}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {has, Key}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% check entity at cache and return estimated ttl
|
%% check entity at cache and return estimated ttl
|
||||||
-spec(ttl/2 :: (cache(), key()) -> ttl() | false).
|
-spec(ttl/2 :: (cache(), key()) -> ttl() | false).
|
||||||
|
|
||||||
ttl(Cache, Key) ->
|
ttl(Cache, Key) ->
|
||||||
gen_server:call(Cache, {ttl, Key}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {ttl, Key}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous remove entry from cache
|
%% synchronous remove entry from cache
|
||||||
-spec(remove/2 :: (cache(), key()) -> ok).
|
-spec(remove/2 :: (cache(), key()) -> ok).
|
||||||
|
|
||||||
remove(Cache, Key) ->
|
remove(Cache, Key) ->
|
||||||
gen_server:call(Cache, {remove, Key}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {remove, Key}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous remove entry from cache
|
%% asynchronous remove entry from cache
|
||||||
-spec(remove_/2 :: (cache(), key()) -> ok).
|
-spec(remove_/2 :: (cache(), key()) -> ok).
|
||||||
|
|
||||||
remove_(Cache, Key) ->
|
remove_(Cache, Key) ->
|
||||||
gen_server:cast(Cache, {remove, Key}).
|
cast(Cache, {remove, Key}).
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
%%%
|
||||||
|
%%% extended cache i/o interface
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous in-cache accumulator
|
%% synchronous in-cache accumulator
|
||||||
-spec(acc/3 :: (cache(), key(), integer() | [{integer(), integer()}]) -> integer() | undefined).
|
-spec(acc/3 :: (cache(), key(), integer() | [{integer(), integer()}]) -> integer() | undefined).
|
||||||
|
|
||||||
acc(Cache, Key, Val) ->
|
acc(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {acc, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {acc, Key, Val}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous in-cache accumulator
|
%% asynchronous in-cache accumulator
|
||||||
-spec(acc_/3 :: (cache(), key(), integer() | {integer(), integer()}) -> ok).
|
-spec(acc_/3 :: (cache(), key(), integer() | {integer(), integer()}) -> ok).
|
||||||
|
|
||||||
acc_(Cache, Key, Val) ->
|
acc_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {acc, Key, Val}).
|
cast(Cache, {acc, Key, Val}).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% memcached-like interface
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous store key/val
|
%% synchronous store key/val
|
||||||
-spec(set/3 :: (cache(), key(), entity()) -> ok).
|
-spec(set/3 :: (cache(), key(), val()) -> ok).
|
||||||
-spec(set/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(set/4 :: (cache(), key(), val(), ttl()) -> ok).
|
||||||
|
|
||||||
set(Cache, Key, Val) ->
|
set(Cache, Key, Val) ->
|
||||||
cache:put(Cache, Key, Val).
|
cache:put(Cache, Key, Val).
|
||||||
@ -262,8 +274,8 @@ set(Cache, Key, Val, TTL) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous store key/val
|
%% asynchronous store key/val
|
||||||
-spec(set_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(set_/3 :: (cache(), key(), val()) -> ok).
|
||||||
-spec(set_/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(set_/4 :: (cache(), key(), val(), ttl()) -> ok).
|
||||||
|
|
||||||
set_(Cache, Key, Val) ->
|
set_(Cache, Key, Val) ->
|
||||||
cache:put_(Cache, Key, Val).
|
cache:put_(Cache, Key, Val).
|
||||||
@ -271,84 +283,79 @@ set_(Cache, Key, Val) ->
|
|||||||
set_(Cache, Key, Val, TTL) ->
|
set_(Cache, Key, Val, TTL) ->
|
||||||
cache:put_(Cache, Key, Val, TTL).
|
cache:put_(Cache, Key, Val, TTL).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous store key/val only if cache does not already hold data for this key
|
%% synchronous store key/val only if cache does not already hold data for this key
|
||||||
-spec(add/3 :: (cache(), key(), entity()) -> ok | conflict).
|
-spec(add/3 :: (cache(), key(), val()) -> ok | {error, conflict}).
|
||||||
-spec(add/4 :: (cache(), key(), entity(), ttl()) -> ok | conflict).
|
-spec(add/4 :: (cache(), key(), val(), ttl()) -> ok | {error, conflict}).
|
||||||
|
|
||||||
add(Cache, Key, Val) ->
|
add(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {add, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
cache:add(Cache, Key, Val, undefined).
|
||||||
|
|
||||||
add(Cache, Key, Val, TTL) ->
|
add(Cache, Key, Val, TTL) ->
|
||||||
gen_server:call(Cache, {add, Key, Val, TTL}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {add, Key, Val, TTL}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous store key/val only if cache does not already hold data for this key
|
%% asynchronous store key/val only if cache does not already hold data for this key
|
||||||
-spec(add_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(add_/3 :: (cache(), key(), val()) -> reference()).
|
||||||
-spec(add_/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(add_/4 :: (cache(), key(), val(), ttl()) -> reference()).
|
||||||
|
|
||||||
add_(Cache, Key, Val) ->
|
add_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {add, Key, Val}).
|
cache:add_(Cache, Key, Val, undefined).
|
||||||
|
|
||||||
add_(Cache, Key, Val, TTL) ->
|
add_(Cache, Key, Val, TTL) ->
|
||||||
gen_server:cast(Cache, {add, Key, Val, TTL}).
|
cast(Cache, {add, Key, Val, TTL}).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous store key/val only if cache does hold data for this key
|
%% synchronous store key/val only if cache does hold data for this key
|
||||||
-spec(replace/3 :: (cache(), key(), entity()) -> ok | not_found).
|
-spec(replace/3 :: (cache(), key(), val()) -> ok | {error, not_found}).
|
||||||
-spec(replace/4 :: (cache(), key(), entity(), ttl()) -> ok | not_found).
|
-spec(replace/4 :: (cache(), key(), val(), ttl()) -> ok | {error, not_found}).
|
||||||
|
|
||||||
replace(Cache, Key, Val) ->
|
replace(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {replace, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
cache:replace(Cache, Key, Val, undefined).
|
||||||
|
|
||||||
replace(Cache, Key, Val, TTL) ->
|
replace(Cache, Key, Val, TTL) ->
|
||||||
gen_server:call(Cache, {replace, Key, Val, TTL}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {replace, Key, Val, TTL}).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronous store key/val only if cache does hold data for this key
|
%% asynchronous store key/val only if cache does hold data for this key
|
||||||
-spec(replace_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(replace_/3 :: (cache(), key(), val()) -> reference()).
|
||||||
-spec(replace_/4 :: (cache(), key(), entity(), ttl()) -> ok).
|
-spec(replace_/4 :: (cache(), key(), val(), ttl()) -> reference()).
|
||||||
|
|
||||||
replace_(Cache, Key, Val) ->
|
replace_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {replace, Key, Val}).
|
cache:replace_(Cache, Key, Val).
|
||||||
|
|
||||||
replace_(Cache, Key, Val, TTL) ->
|
replace_(Cache, Key, Val, TTL) ->
|
||||||
gen_server:cast(Cache, {replace, Key, Val, TTL}).
|
cast(Cache, {replace, Key, Val, TTL}).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronously add data to existing key after existing data, the operation do not prolong entry ttl
|
%% synchronously add data to existing key after existing data, the operation do not prolong entry ttl
|
||||||
-spec(append/3 :: (cache(), key(), entity()) -> ok | not_found).
|
-spec(append/3 :: (cache(), key(), val()) -> ok | {error, not_found}).
|
||||||
|
|
||||||
append(Cache, Key, Val) ->
|
append(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {append, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {append, Key, Val}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronously add data to existing key after existing data, the operation do not prolong entry ttl
|
%% asynchronously add data to existing key after existing data, the operation do not prolong entry ttl
|
||||||
-spec(append_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(append_/3 :: (cache(), key(), val()) -> reference()).
|
||||||
|
|
||||||
append_(Cache, Key, Val) ->
|
append_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {append, Key, Val}).
|
cast(Cache, {append, Key, Val}).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronously add data to existing key before existing data
|
%% synchronously add data to existing key before existing data
|
||||||
-spec(prepend/3 :: (cache(), key(), entity()) -> ok | not_found).
|
-spec(prepend/3 :: (cache(), key(), val()) -> ok | {error, not_found}).
|
||||||
|
|
||||||
prepend(Cache, Key, Val) ->
|
prepend(Cache, Key, Val) ->
|
||||||
gen_server:call(Cache, {prepend, Key, Val}, ?DEF_CACHE_TIMEOUT).
|
call(Cache, {prepend, Key, Val}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% asynchronously add data to existing key before existing data
|
%% asynchronously add data to existing key before existing data
|
||||||
-spec(prepend_/3 :: (cache(), key(), entity()) -> ok).
|
-spec(prepend_/3 :: (cache(), key(), val()) -> reference()).
|
||||||
|
|
||||||
prepend_(Cache, Key, Val) ->
|
prepend_(Cache, Key, Val) ->
|
||||||
gen_server:cast(Cache, {prepend, Key, Val}).
|
gen_server:cast(Cache, {prepend, Key, Val}).
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% synchronous remove entry from cache
|
%% synchronous remove entry from cache
|
||||||
-spec(delete/2 :: (cache(), key()) -> ok).
|
-spec(delete/2 :: (cache(), key()) -> ok).
|
||||||
@ -363,3 +370,22 @@ delete(Cache, Key) ->
|
|||||||
delete_(Cache, Key) ->
|
delete_(Cache, Key) ->
|
||||||
cache:remove_(Cache, Key).
|
cache:remove_(Cache, Key).
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
%%%
|
||||||
|
%%% private
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% synchronous call to server
|
||||||
|
call(Pid, Req) ->
|
||||||
|
gen_server:call(Pid, Req, ?CONFIG_TIMEOUT).
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% asynchronous call
|
||||||
|
cast(Pid, Req) ->
|
||||||
|
Ref = erlang:make_ref(),
|
||||||
|
erlang:send(Pid, {'$gen_call', {self(), Ref}, Req}, [noconnect]),
|
||||||
|
Ref.
|
||||||
|
|
||||||
|
@ -33,4 +33,4 @@
|
|||||||
-define(DEF_CACHE_CHECK, 20000).
|
-define(DEF_CACHE_CHECK, 20000).
|
||||||
|
|
||||||
%% default cache i/o timeout
|
%% default cache i/o timeout
|
||||||
-define(DEF_CACHE_TIMEOUT, 60000).
|
-define(CONFIG_TIMEOUT, 30000).
|
||||||
|
@ -103,83 +103,64 @@ terminate(_Reason, State) ->
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------------
|
%%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
handle_call({put, Key, Val}, _, S) ->
|
handle_call({put, Key, Val, TTL}, _, State) ->
|
||||||
{reply, ok, cache_put(Key, Val, S)};
|
{reply, ok, cache_put(Key, Val, TTL, State)};
|
||||||
|
|
||||||
handle_call({put, Key, Val, TTL}, _, S) ->
|
handle_call({get, Key}, _, State) ->
|
||||||
{reply, ok, cache_put(Key, Val, cache_util:now() + TTL, S)};
|
{reply, cache_get(Key, State), State};
|
||||||
|
|
||||||
handle_call({get, Key}, _, S) ->
|
handle_call({lookup, Key}, _, State) ->
|
||||||
{reply, cache_get(Key, S), S};
|
{reply, cache_lookup(Key, State), State};
|
||||||
|
|
||||||
handle_call({lookup, Key}, _, S) ->
|
handle_call({has, Key}, _, State) ->
|
||||||
{reply, cache_lookup(Key, S), S};
|
{reply, cache_has(Key, State), State};
|
||||||
|
|
||||||
handle_call({has, Key}, _, S) ->
|
handle_call({ttl, Key}, _, State) ->
|
||||||
{reply, cache_has(Key, S), S};
|
{reply, cache_ttl(Key, State), State};
|
||||||
|
|
||||||
handle_call({ttl, Key}, _, S) ->
|
handle_call({remove, Key}, _, State) ->
|
||||||
{reply, cache_ttl(Key, S), S};
|
{reply, ok, cache_remove(Key, State)};
|
||||||
|
|
||||||
handle_call({remove, Key}, _, S) ->
|
handle_call({acc, Key, Val}, _, State0) ->
|
||||||
{reply, ok, cache_remove(Key, S)};
|
{Result, State1} = cache_acc(Key, Val, State0),
|
||||||
|
{reply, Result, State1};
|
||||||
|
|
||||||
handle_call({acc, Key, Val}, _, S) ->
|
handle_call({add, Key, Val, TTL}, _, State) ->
|
||||||
{Reply, NS} = cache_acc(Key, Val, S),
|
case cache_has(Key, State) of
|
||||||
{reply, Reply, NS};
|
|
||||||
|
|
||||||
handle_call({add, Key, Val}, _, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
true ->
|
||||||
{reply, conflict, S};
|
{reply, {error, conflict}, State};
|
||||||
false ->
|
false ->
|
||||||
{reply, ok, cache_put(Key, Val, S)}
|
{reply, ok, cache_put(Key, Val, TTL, State)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call({add, Key, Val, TTL}, _, S) ->
|
handle_call({replace, Key, Val, TTL}, _, State) ->
|
||||||
case cache_has(Key, S) of
|
case cache_has(Key, State) of
|
||||||
true ->
|
true ->
|
||||||
{reply, conflict, S};
|
{reply, ok, cache_put(Key, Val, TTL, State)};
|
||||||
false ->
|
false ->
|
||||||
{reply, ok, cache_put(Key, Val, cache_util:now() + TTL, S)}
|
{reply, {error, not_found}, State}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call({replace, Key, Val}, _, S) ->
|
handle_call({prepend, Key, Val}, _, State) ->
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{reply, ok, cache_put(Key, Val, S)};
|
|
||||||
false ->
|
|
||||||
{reply, not_found, S}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_call({replace, Key, Val, TTL}, _, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{reply, ok, cache_put(Key, Val, cache_util:now() + TTL, S)};
|
|
||||||
false ->
|
|
||||||
{reply, not_found, S}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_call({prepend, Key, Val}, _, S) ->
|
|
||||||
% @todo: reduce one write
|
% @todo: reduce one write
|
||||||
case cache_get(Key, S) of
|
case cache_get(Key, State) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{reply, ok, cache_put(Key, [Val], S)};
|
{reply, ok, cache_put(Key, [Val], undefined, State)};
|
||||||
X when is_list(X) ->
|
X when is_list(X) ->
|
||||||
{reply, ok, cache_put(Key, [Val|X], S)};
|
{reply, ok, cache_put(Key, [Val|X], undefined, State)};
|
||||||
X ->
|
X ->
|
||||||
{reply, ok, cache_put(Key, [Val,X], S)}
|
{reply, ok, cache_put(Key, [Val,X], undefined, State)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call({append, Key, Val}, _, S) ->
|
handle_call({append, Key, Val}, _, State) ->
|
||||||
% @todo: reduce one write
|
% @todo: reduce one write
|
||||||
case cache_get(Key, S) of
|
case cache_get(Key, State) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{reply, ok, cache_put(Key, [Val], S)};
|
{reply, ok, cache_put(Key, [Val], undefined, State)};
|
||||||
X when is_list(X) ->
|
X when is_list(X) ->
|
||||||
{reply, ok, cache_put(Key, X++[Val], S)};
|
{reply, ok, cache_put(Key, X++[Val], undefined, State)};
|
||||||
X ->
|
X ->
|
||||||
{reply, ok, cache_put(Key, [X, Val], S)}
|
{reply, ok, cache_put(Key, [X, Val], undefined, State)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call(i, _, State) ->
|
handle_call(i, _, State) ->
|
||||||
@ -209,73 +190,6 @@ handle_call(_, _, S) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
%%
|
%%
|
||||||
handle_cast({put, Key, Val}, S) ->
|
|
||||||
{noreply, cache_put(Key, Val, S)};
|
|
||||||
|
|
||||||
handle_cast({put, Key, Val, TTL}, S) ->
|
|
||||||
{noreply, cache_put(Key, Val, cache_util:now() + TTL, S)};
|
|
||||||
|
|
||||||
handle_cast({remove, Key}, S) ->
|
|
||||||
{noreply, cache_remove(Key, S)};
|
|
||||||
|
|
||||||
handle_cast({acc, Key, Val}, S) ->
|
|
||||||
{_, NS} = cache_acc(Key, Val, S),
|
|
||||||
{noreply, NS};
|
|
||||||
|
|
||||||
handle_cast({add, Key, Val}, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{noreply, S};
|
|
||||||
false ->
|
|
||||||
{noreply, cache_put(Key, Val, S)}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast({add, Key, Val, TTL}, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{noreply, S};
|
|
||||||
false ->
|
|
||||||
{noreply, cache_put(Key, Val, cache_util:now() + TTL, S)}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast({replace, Key, Val}, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{noreply, cache_put(Key, Val, S)};
|
|
||||||
false ->
|
|
||||||
{noreply, S}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast({replace, Key, Val, TTL}, S) ->
|
|
||||||
case cache_has(Key, S) of
|
|
||||||
true ->
|
|
||||||
{noreply, cache_put(Key, Val, cache_util:now() + TTL, S)};
|
|
||||||
false ->
|
|
||||||
{noreply, S}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast({prepend, Key, Val}, S) ->
|
|
||||||
% @todo: reduce one write
|
|
||||||
case cache_get(Key, S) of
|
|
||||||
undefined ->
|
|
||||||
{noreply, cache_put(Key, [Val], S)};
|
|
||||||
X when is_list(X) ->
|
|
||||||
{noreply, cache_put(Key, [Val|X], S)};
|
|
||||||
X ->
|
|
||||||
{noreply, cache_put(Key, [Val,X], S)}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast({append, Key, Val}, S) ->
|
|
||||||
% @todo: reduce one write
|
|
||||||
case cache_get(Key, S) of
|
|
||||||
undefined ->
|
|
||||||
{noreply, cache_put(Key, [Val], S)};
|
|
||||||
X when is_list(X) ->
|
|
||||||
{noreply, cache_put(Key, X++[Val], S)};
|
|
||||||
X ->
|
|
||||||
{noreply, cache_put(Key, [X, Val], S)}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_cast(_, S) ->
|
handle_cast(_, S) ->
|
||||||
{noreply, S}.
|
{noreply, S}.
|
||||||
|
|
||||||
@ -309,7 +223,6 @@ handle_info(_, S) ->
|
|||||||
code_change(_Vsn, S, _Extra) ->
|
code_change(_Vsn, S, _Extra) ->
|
||||||
{ok, S}.
|
{ok, S}.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------------
|
%%%----------------------------------------------------------------------------
|
||||||
%%%
|
%%%
|
||||||
%%% private
|
%%% private
|
||||||
@ -318,88 +231,83 @@ code_change(_Vsn, S, _Extra) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
%% insert value to cache
|
%% insert value to cache
|
||||||
cache_put(Key, Val, #cache{heap=Heap}=State) ->
|
cache_put(Key, Val, undefined, #cache{name=_Name, heap=Heap}=State) ->
|
||||||
{_, Head} = cache_heap:head(Heap),
|
{_, Head} = cache_heap:head(Heap),
|
||||||
true = ets:insert(Head, {Key, Val}),
|
true = ets:insert(Head, {Key, Val}),
|
||||||
lists:foreach(
|
ok = heap_remove(Key, cache_heap:tail(Heap)),
|
||||||
fun({_, X}) -> ets:delete(X, Key) end,
|
_ = stats(put, State),
|
||||||
cache_heap:tail(Heap)
|
?DEBUG("cache ~p: put ~p to heap ~p~n", [_Name, Key, Head]),
|
||||||
),
|
State;
|
||||||
cache_util:stats(State#cache.stats, {cache, State#cache.name, put}),
|
|
||||||
?DEBUG("cache ~p: put ~p to heap ~p~n", [State#cache.name, Key, Head]),
|
|
||||||
State.
|
|
||||||
|
|
||||||
cache_put(Key, Val, Expire, #cache{}=State) ->
|
cache_put(Key, Val, TTL, #cache{name=_Name, heap=Heap}=State) ->
|
||||||
Refs = cache_heap:refs(State#cache.heap),
|
Expire = cache_util:now() + TTL,
|
||||||
|
Refs = cache_heap:refs(Heap),
|
||||||
case lists:splitwith(fun({X, _}) -> X > Expire end, Refs) of
|
case lists:splitwith(fun({X, _}) -> X > Expire end, Refs) of
|
||||||
{[], _Tail} ->
|
{[], _Tail} ->
|
||||||
cache_put(Key, Val, State);
|
cache_put(Key, Val, undefined, State);
|
||||||
{Head, Tail} ->
|
{Head, Tail} ->
|
||||||
[{_, Heap} | Rest] = lists:reverse(Head),
|
[{_, Inst} | Rest] = lists:reverse(Head),
|
||||||
true = ets:insert(Heap, {Key, Val}),
|
true = ets:insert(Inst, {Key, Val}),
|
||||||
lists:foreach(
|
ok = heap_remove(Key, Rest ++ Tail),
|
||||||
fun({_, X}) -> ets:delete(X, Key) end,
|
_ = stats(put, State),
|
||||||
Rest ++ Tail
|
?DEBUG("cache ~p: put ~p to heap ~p~n", [_Name, Key, Inst]),
|
||||||
),
|
|
||||||
cache_util:stats(State#cache.stats, {cache, State#cache.name, put}),
|
|
||||||
?DEBUG("cache ~p: put ~p to heap ~p~n", [State#cache.name, Key, Heap]),
|
|
||||||
State
|
State
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% get cache value
|
%% get cache value
|
||||||
cache_get(Key, #cache{policy=mru}=S) ->
|
cache_get(Key, #cache{policy=mru}=State) ->
|
||||||
% cache MRU should not move key anywhere because
|
% cache MRU should not move key anywhere because
|
||||||
% cache always evicts last generation
|
% cache always evicts last generation
|
||||||
% fall-back to cache lookup
|
% fall-back to cache lookup
|
||||||
cache_lookup(Key, S);
|
cache_lookup(Key, State);
|
||||||
|
|
||||||
cache_get(Key, #cache{}=S) ->
|
cache_get(Key, #cache{name=_Name, heap=Heap}=State) ->
|
||||||
{_, Head} = cache_heap:head(S#cache.heap),
|
{_, Head} = cache_heap:head(Heap),
|
||||||
case heap_lookup(Key, cache_heap:refs(S#cache.heap)) of
|
case heap_lookup(Key, cache_heap:refs(Heap)) of
|
||||||
undefined ->
|
undefined ->
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, miss}),
|
stats(miss, State),
|
||||||
undefined;
|
undefined;
|
||||||
{Head, Val} ->
|
{Head, Val} ->
|
||||||
?DEBUG("cache ~p: get ~p at cell ~p~n", [S#cache.name, Key, Head]),
|
?DEBUG("cache ~p: get ~p at cell ~p~n", [_Name, Key, Head]),
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, hit}),
|
stats(hit, State),
|
||||||
Val;
|
Val;
|
||||||
{Heap, Val} ->
|
{Cell, Val} ->
|
||||||
true = ets:insert(Head, {Key, Val}),
|
true = ets:insert(Head, {Key, Val}),
|
||||||
_ = ets:delete(Heap, Key),
|
_ = ets:delete(Cell, Key),
|
||||||
?DEBUG("cache ~p: get ~p at cell ~p~n", [S#cache.name, Key, Heap]),
|
?DEBUG("cache ~p: get ~p at cell ~p~n", [_Name, Key, Cell]),
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, hit}),
|
stats(hit, State),
|
||||||
Val
|
Val
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% lookup cache value
|
%% lookup cache value
|
||||||
cache_lookup(Key, #cache{}=S) ->
|
cache_lookup(Key, #cache{name=_Name, heap=Heap}=State) ->
|
||||||
case heap_lookup(Key, cache_heap:refs(S#cache.heap)) of
|
case heap_lookup(Key, cache_heap:refs(Heap)) of
|
||||||
undefined ->
|
undefined ->
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, miss}),
|
stats(miss, State),
|
||||||
undefined;
|
undefined;
|
||||||
{_Heap, Val} ->
|
{_Cell, Val} ->
|
||||||
?DEBUG("cache ~p: get ~p at cell ~p~n", [S#cache.name, Key, _Heap]),
|
?DEBUG("cache ~p: get ~p at cell ~p~n", [_Name, Key, _Cell]),
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, hit}),
|
stats(hit, State),
|
||||||
Val
|
Val
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% check if key exists
|
%% check if key exists
|
||||||
cache_has(Key, #cache{}=S) ->
|
cache_has(Key, #cache{name=_Name, heap=Heap}) ->
|
||||||
case heap_has(Key, cache_heap:refs(S#cache.heap)) of
|
case heap_has(Key, cache_heap:refs(Heap)) of
|
||||||
false ->
|
false ->
|
||||||
false;
|
false;
|
||||||
_Heap ->
|
_Heap ->
|
||||||
?DEBUG("cache ~p: has ~p at cell ~p~n", [S#cache.name, Key, _Heap]),
|
?DEBUG("cache ~p: has ~p at cell ~p~n", [_Name, Key, _Heap]),
|
||||||
true
|
true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% check key ttl
|
%% check key ttl
|
||||||
cache_ttl(Key, #cache{}=S) ->
|
cache_ttl(Key,#cache{heap=Heap}) ->
|
||||||
case heap_has(Key, cache_heap:refs(S#cache.heap)) of
|
case heap_has(Key, cache_heap:refs(Heap)) of
|
||||||
false ->
|
false ->
|
||||||
undefined;
|
undefined;
|
||||||
{Expire, _} ->
|
{Expire, _} ->
|
||||||
@ -408,35 +316,32 @@ cache_ttl(Key, #cache{}=S) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
%%
|
%%
|
||||||
cache_remove(Key, #cache{}=S) ->
|
cache_remove(Key, #cache{name=_Name, heap=Heap}=State) ->
|
||||||
lists:foreach(
|
ok = heap_remove(Key, cache_heap:refs(Heap)),
|
||||||
fun({_, X}) -> ets:delete(X, Key) end,
|
_ = stats(remove, State),
|
||||||
cache_heap:refs(S#cache.heap)
|
?DEBUG("cache ~p: remove ~p~n", [_Name, Key]),
|
||||||
),
|
State.
|
||||||
cache_util:stats(S#cache.stats, {cache, S#cache.name, remove}),
|
|
||||||
?DEBUG("cache ~p: remove ~p~n", [S#cache.name, Key]),
|
|
||||||
S.
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% @todo: reduce one write
|
%% @todo: reduce one write
|
||||||
cache_acc(Key, Val, S)
|
cache_acc(Key, Val, State)
|
||||||
when is_integer(Val) ->
|
when is_integer(Val) ->
|
||||||
case cache_get(Key, S) of
|
case cache_get(Key, State) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{undefined, cache_put(Key, Val, S)};
|
{undefined, cache_put(Key, Val, undefined, State)};
|
||||||
X when is_integer(X) ->
|
X when is_integer(X) ->
|
||||||
{X, cache_put(Key, X + Val, S)};
|
{X, cache_put(Key, X + Val, undefined, State)};
|
||||||
X when is_tuple(X) ->
|
X when is_tuple(X) ->
|
||||||
{erlang:element(1, X), cache_put(Key, tuple_acc({1, Val}, X), S)};
|
{erlang:element(1, X), cache_put(Key, tuple_acc({1, Val}, X), undefined, State)};
|
||||||
_ ->
|
_ ->
|
||||||
{badarg, S}
|
{badarg, State}
|
||||||
end;
|
end;
|
||||||
cache_acc(Key, Val, S) ->
|
cache_acc(Key, Val, State) ->
|
||||||
case cache_get(Key, S) of
|
case cache_get(Key, State) of
|
||||||
X when is_tuple(X) ->
|
X when is_tuple(X) ->
|
||||||
{X, cache_put(Key, tuple_acc(Val, X), S)};
|
{X, cache_put(Key, tuple_acc(Val, X), undefined, State)};
|
||||||
_ ->
|
_ ->
|
||||||
{badarg, S}
|
{badarg, State}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
tuple_acc({Pos, Val}, X) ->
|
tuple_acc({Pos, Val}, X) ->
|
||||||
@ -450,6 +355,15 @@ tuple_acc(List, X) ->
|
|||||||
List
|
List
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% remove key from heap segments
|
||||||
|
heap_remove(Key, Heap) ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({_, Id}) -> ets:delete(Id, Key) end,
|
||||||
|
Heap
|
||||||
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%%
|
%%
|
||||||
heap_lookup(Key, [{_, Heap} | Tail]) ->
|
heap_lookup(Key, [{_, Heap} | Tail]) ->
|
||||||
@ -472,3 +386,10 @@ heap_has(Key, [{_, Heap}=X | Tail]) ->
|
|||||||
heap_has(_Key, []) ->
|
heap_has(_Key, []) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% update statistic
|
||||||
|
stats(_, #cache{stats = undefined}) ->
|
||||||
|
ok;
|
||||||
|
stats(X, #cache{stats = Stats, name = Name}) ->
|
||||||
|
cache_util:stats(Stats, {cache, Name, X}).
|
||||||
|
|
||||||
|
@ -44,22 +44,6 @@ cache_interface_test_() ->
|
|||||||
]
|
]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
|
||||||
% lru_test_() ->
|
|
||||||
% {
|
|
||||||
% setup,
|
|
||||||
% fun cache_init/0,
|
|
||||||
% fun cache_free/1,
|
|
||||||
% [
|
|
||||||
% {"put", fun cache_put/0}
|
|
||||||
% ,{"has", fun cache_has/0}
|
|
||||||
% ,{"get", fun cache_get/0}
|
|
||||||
% ,{"del", fun cache_del/0}
|
|
||||||
% ,{"acc", fun cache_acc/0}
|
|
||||||
% ,{"lifecycle 1", {timeout, 10000, fun cache_lc1/0}}
|
|
||||||
% ]
|
|
||||||
% }.
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------------
|
%%%----------------------------------------------------------------------------
|
||||||
%%%
|
%%%
|
||||||
%%% factory
|
%%% factory
|
||||||
@ -103,8 +87,8 @@ put(Pid) ->
|
|||||||
[
|
[
|
||||||
?_assertMatch(ok, cache:put(Pid, <<"key-1">>, <<"val-1">>))
|
?_assertMatch(ok, cache:put(Pid, <<"key-1">>, <<"val-1">>))
|
||||||
,?_assertMatch(ok, cache:put(Pid, <<"key-2">>, <<"val-2">>, 5))
|
,?_assertMatch(ok, cache:put(Pid, <<"key-2">>, <<"val-2">>, 5))
|
||||||
,?_assertMatch(ok, cache:put_(Pid, <<"key-3">>, <<"val-3">>))
|
,?_assertMatch(ok, async(cache:put_(Pid, <<"key-3">>, <<"val-3">>)))
|
||||||
,?_assertMatch(ok, cache:put_(Pid, <<"key-4">>, <<"val-4">>, 5))
|
,?_assertMatch(ok, async(cache:put_(Pid, <<"key-4">>, <<"val-4">>, 5)))
|
||||||
].
|
].
|
||||||
|
|
||||||
get(Pid) ->
|
get(Pid) ->
|
||||||
@ -114,7 +98,7 @@ get(Pid) ->
|
|||||||
,?_assertMatch(<<"val-1">>, cache:lookup(Pid, <<"key-1">>))
|
,?_assertMatch(<<"val-1">>, cache:lookup(Pid, <<"key-1">>))
|
||||||
,?_assertMatch(true, cache:has(Pid, <<"key-1">>))
|
,?_assertMatch(true, cache:has(Pid, <<"key-1">>))
|
||||||
|
|
||||||
,?_assertMatch(ok, cache:put_(Pid, <<"key-3">>, <<"val-3">>))
|
,?_assertMatch(ok, async(cache:put_(Pid, <<"key-3">>, <<"val-3">>)))
|
||||||
,?_assertMatch(<<"val-3">>, cache:get(Pid, <<"key-3">>))
|
,?_assertMatch(<<"val-3">>, cache:get(Pid, <<"key-3">>))
|
||||||
,?_assertMatch(<<"val-3">>, cache:lookup(Pid, <<"key-3">>))
|
,?_assertMatch(<<"val-3">>, cache:lookup(Pid, <<"key-3">>))
|
||||||
,?_assertMatch(true, cache:has(Pid, <<"key-3">>))
|
,?_assertMatch(true, cache:has(Pid, <<"key-3">>))
|
||||||
@ -124,166 +108,8 @@ get(Pid) ->
|
|||||||
,?_assertMatch(false, cache:has(Pid, <<"key-5">>))
|
,?_assertMatch(false, cache:has(Pid, <<"key-5">>))
|
||||||
].
|
].
|
||||||
|
|
||||||
|
async(Ref) ->
|
||||||
%% @todo - fix ttl and segment expire time
|
receive
|
||||||
|
{Ref, X} ->
|
||||||
|
X
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
cache_init() ->
|
|
||||||
cache:start_link(test, ?CACHE).
|
|
||||||
|
|
||||||
cache_free({ok, Pid}) ->
|
|
||||||
erlang:unlink(Pid),
|
|
||||||
cache:drop(test).
|
|
||||||
|
|
||||||
cache_put() ->
|
|
||||||
ok = cache:put(test, <<"key">>, <<"val">>).
|
|
||||||
|
|
||||||
cache_has() ->
|
|
||||||
true = cache:has(test, <<"key">>),
|
|
||||||
false = cache:has(test, <<"yek">>).
|
|
||||||
|
|
||||||
cache_get() ->
|
|
||||||
<<"val">> = cache:get(test, <<"key">>),
|
|
||||||
undefined = cache:get(test, <<"yek">>).
|
|
||||||
|
|
||||||
cache_del() ->
|
|
||||||
ok = cache:remove(test, <<"key">>),
|
|
||||||
ok = cache:remove(test, <<"yek">>).
|
|
||||||
|
|
||||||
cache_acc() ->
|
|
||||||
undefined = cache:acc(test, <<"acc">>, 10),
|
|
||||||
10 = cache:acc(test, <<"acc">>, 10),
|
|
||||||
20 = cache:get(test, <<"acc">>),
|
|
||||||
badarg = cache:acc(test, <<"acc">>, [{1, 10}]),
|
|
||||||
badarg = cache:acc(test, <<"acc1">>,[{1, 10}]),
|
|
||||||
ok = cache:put(test, <<"acc1">>, {10,20,30,40}),
|
|
||||||
{10, 20, 30, 40} = cache:acc(test, <<"acc1">>,[{1, 10}, {2, 10}]),
|
|
||||||
{20, 30, 30, 40} = cache:get(test, <<"acc1">>).
|
|
||||||
|
|
||||||
cache_lc1() ->
|
|
||||||
ok = cache:put(test, key, val),
|
|
||||||
timer:sleep(1200),
|
|
||||||
val = cache:get(test, key),
|
|
||||||
timer:sleep(1200),
|
|
||||||
val = cache:get(test, key),
|
|
||||||
timer:sleep(1200),
|
|
||||||
val = cache:get(test, key),
|
|
||||||
timer:sleep(3200),
|
|
||||||
undefined = cache:get(test, key).
|
|
||||||
|
|
||||||
|
|
||||||
% lifecyle_2_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {ttl, 10},
|
|
||||||
% {evict, 100}
|
|
||||||
% ]),
|
|
||||||
% ok = cache:put(test, key, val),
|
|
||||||
% timer:sleep(6),
|
|
||||||
% {ok, val} = cache:get(test, key),
|
|
||||||
% timer:sleep(6),
|
|
||||||
% {ok, val} = cache:get(test, key),
|
|
||||||
% timer:sleep(20),
|
|
||||||
% none = cache:get(test, key),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
|
||||||
% lifecyle_3_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {ttl, 10},
|
|
||||||
% {evict, 5}
|
|
||||||
% ]),
|
|
||||||
% ok = cache:put(test, key1, val1),
|
|
||||||
% timer:sleep(5),
|
|
||||||
% ok = cache:put(test, key2, val2),
|
|
||||||
% timer:sleep(5),
|
|
||||||
% ok = cache:put(test, key3, val3),
|
|
||||||
% timer:sleep(5),
|
|
||||||
% ok = cache:put(test, key4, val4),
|
|
||||||
|
|
||||||
% none = cache:get(test, key1),
|
|
||||||
% none = cache:get(test, key2),
|
|
||||||
% {ok, val3} = cache:get(test, key3),
|
|
||||||
% {ok, val4} = cache:get(test, key4),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
|
||||||
% evict_lru_1_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {policy, lru},
|
|
||||||
% {ttl, 100},
|
|
||||||
% {evict, 5},
|
|
||||||
% {size, 10},
|
|
||||||
% {chunk, 2}
|
|
||||||
% ]),
|
|
||||||
% lists:foreach(
|
|
||||||
% fun(X) -> cache:put(test, X, X) end,
|
|
||||||
% lists:seq(1, 10)
|
|
||||||
% ),
|
|
||||||
% timer:sleep(10),
|
|
||||||
% {ok, 1} = cache:get(test, 1),
|
|
||||||
% cache:put(test, key, val),
|
|
||||||
% timer:sleep(10),
|
|
||||||
% none = cache:get(test, 2),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
|
||||||
% evict_lru_2_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {policy, lru},
|
|
||||||
% {ttl, 100},
|
|
||||||
% {evict, 100},
|
|
||||||
% {size, 10},
|
|
||||||
% {chunk, 2}
|
|
||||||
% ]),
|
|
||||||
% lists:foreach(
|
|
||||||
% fun(X) -> cache:put(test, X, X) end,
|
|
||||||
% lists:seq(1, 10)
|
|
||||||
% ),
|
|
||||||
% {ok, 1} = cache:get(test, 1),
|
|
||||||
% cache:put(test, key, val),
|
|
||||||
% cache:evict(test),
|
|
||||||
% none = cache:get(test, 2),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
|
||||||
% evict_mru_1_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {policy, mru},
|
|
||||||
% {ttl, 100},
|
|
||||||
% {evict, 5},
|
|
||||||
% {size, 10},
|
|
||||||
% {chunk, 2}
|
|
||||||
% ]),
|
|
||||||
% lists:foreach(
|
|
||||||
% fun(X) -> cache:put(test, X, X) end,
|
|
||||||
% lists:seq(1, 10)
|
|
||||||
% ),
|
|
||||||
% timer:sleep(10),
|
|
||||||
% {ok, 1} = cache:get(test, 1),
|
|
||||||
% cache:put(test, key, val),
|
|
||||||
% timer:sleep(10),
|
|
||||||
% none = cache:get(test, key),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
|
||||||
% evict_mru_2_test() ->
|
|
||||||
% cache:start(),
|
|
||||||
% {ok, _} = cache:start_link(test, [
|
|
||||||
% {policy, mru},
|
|
||||||
% {ttl, 100},
|
|
||||||
% {evict, 100},
|
|
||||||
% {size, 10},
|
|
||||||
% {chunk, 2}
|
|
||||||
% ]),
|
|
||||||
% lists:foreach(
|
|
||||||
% fun(X) -> cache:put(test, X, X) end,
|
|
||||||
% lists:seq(1, 10)
|
|
||||||
% ),
|
|
||||||
% {ok, 1} = cache:get(test, 1),
|
|
||||||
% cache:put(test, key, val),
|
|
||||||
% cache:evict(test),
|
|
||||||
% none = cache:get(test, key),
|
|
||||||
% cache:stop(test).
|
|
||||||
|
Loading…
Reference in New Issue
Block a user