用 Erlang 的 gb_trees 实现了一个 memcached

最近蛋疼在用 Erlang 写 web 程序,这货是存 session 用的
定时器部分是拍脑袋拍的,不知道正规的实现应该是什么样子,想到这样,就这么写了..

-module(cache).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-export([set/2, set/3, get/1, del/1]).

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

set(K, V, T) ->
    gen_server:call(?SERVER, {set, K, V, T}).

set(K, V) ->
    gen_server:call(?SERVER, {set, K, V}).

get(K) ->
    gen_server:call(?SERVER, {get, K}).

del(K) ->
    gen_server:call(?SERVER, {del, K}).

init([]) ->
    {ok, gb_trees:empty()}.

handle_call({set, K, V, T}, _From, Tree) ->
    case gb_trees:lookup(K, Tree) of
        none ->
            none;
        {value, {V, forever}} ->
            none;
        {value, {V, OldTRef}} ->
            timer:cancel(OldTRef)
    end,
    {ok, TRef} = timer:apply_after(T * 1000, ?MODULE, del, [K]),
    Tree2 = gb_trees:enter(K, {V, TRef}, Tree),
    {reply, {ok, K, V}, Tree2};

handle_call({set, K, V}, _From, Tree) ->
    case gb_trees:lookup(K, Tree) of
        none ->
            none;
        {value, {V, forever}} ->
            none;
        {value, {V, OldTRef}} ->
            timer:cancel(OldTRef)
    end,
    Tree2 = gb_trees:enter(K, {V, forever}, Tree),
    {reply, {ok, K, V}, Tree2};

handle_call({get, K}, _From, Tree) ->
    Reply = case gb_trees:lookup(K, Tree) of
                none ->
                    none;
                {value, {Value, _}} ->
                    {ok, Value}
            end,
    {reply, Reply, Tree};

handle_call({del, K}, _From, Tree) ->
    Tree2 = gb_trees:delete_any(K, Tree),
    {reply, {ok, K}, Tree2}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.
terminate(_Reason, _State) ->
    ok.
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.