Erlang で Consistent Hashing 実装

自作 memcached-client に必要だったので、ConsistentHashing - コンシステント・ハッシュ法 の解説を参考に Erlang で Consisten Hashing を実装した。
環状の配置は ETS の orderd_set を利用している。


レプリカを挿入しているところと get_node で next, first を使っているあたりが肝。

-module(chash).

%% API
-export([new/1, delete/1, add_node/3, remove_node/2, get_node/2]).

-define(NUMBER_OF_REPLICAS, 100).

%%====================================================================
%% API
%%====================================================================

new(Name) ->
    ets:new(Name, [ordered_set, protected]).

add_node(CHash, NodeKey, Node) ->
    add_node(0, CHash, NodeKey, Node).

add_node(N, _CHash, _NodeKey, _Node) when N =:= ?NUMBER_OF_REPLICAS ->
    ok;
add_node(N, CHash, NodeKey, Node) ->
    Hash = hash([N | NodeKey]),
    true = ets:insert(CHash, {Hash, Node}),
    add_node(N + 1, CHash, NodeKey, Node).

remove_node(CHash, NodeKey) ->
    remove_node(0, CHash, NodeKey).

remove_node(N, _CHash, _NodeKey) when N =:= ?NUMBER_OF_REPLICAS ->
    ok;
remove_node(N, CHash, NodeKey) ->
    Hash = hash([N | NodeKey]),
    true = ets:delete(CHash, Hash),
    remove_node(N + 1, CHash, NodeKey).

get_node(CHash, Key) ->
    Hash = hash(Key),
    case ets_lookup(CHash, Hash) of
        [] ->
            case ets:next(CHash, Hash) of
                '$end_of_table' ->
                    case ets:first(CHash) of
                        '$end_of_table' ->
                            {error, no_entry};
                        FirstKey ->
                            ets_lookup(CHash, FirstKey)
                    end;
                NextKey ->
                    ets_lookup(CHash, NextKey)
            end;
        Value ->
            Value
    end.

delete(CHash) ->
    ets:delete(CHash).

%%====================================================================
%% Internal functions
%%====================================================================
hash(Key) ->
    erlang:md5(Key).

ets_lookup(CHash, Key) ->
    case ets:lookup(CHash, Key) of
        [{_Key, Value}] ->
             Value;
        Other -> Other
    end.