defmodule HRW do @moduledoc """ HRW (Highest Random Weight), also known as rendezvous hashing, maps a key to a node out of a set in a way that stays stable when nodes are added or removed. This module is stateless. For O(log n) lookups over large node sets, see `HRW.Skeleton`. """ @doc """ Returns the node responsible for `key`. Each node is hashed together with the key; the highest-scoring node wins. ## Options * `:hash_fn` - a function `term -> integer`. Defaults to `&:erlang.phash2/1`. ## Examples iex> HRW.owner("192.168.0.1", ["server1", "server2", "server3"]) "server2" """ @spec owner(term(), [term()], keyword()) :: term() def owner(key, nodes, opts \\ []) do hash_fn = Keyword.get(opts, :hash_fn, &:erlang.phash2/1) Enum.max_by(nodes, fn node -> hash_fn.({key, node}) end) end @doc """ Returns the top `count` nodes responsible for `key`, in descending weight order. ## Options * `:hash_fn` - a function `term -> integer`. Defaults to `&:erlang.phash2/1`. ## Examples iex> HRW.owners("192.168.0.1", ["server1", "server2", "server3"], 2) ["server2", "server3"] """ @spec owners(term(), [term()], non_neg_integer(), keyword()) :: [term()] def owners(key, nodes, count, opts \\ []) do hash_fn = Keyword.get(opts, :hash_fn, &:erlang.phash2/1) nodes |> Enum.sort_by(fn node -> hash_fn.({key, node}) end, :desc) |> Enum.take(count) end end