ExHashRing.Ring (ex_hash_ring v7.0.0)

View Source

A pure Elixir consistent hash ring.

Ring data is stored in an ETS table owned by the ExHashRing.Ring GenServer. This module provides functions for managing and querying a consistent hash ring quickly and efficiently.

Summary

Types

Rings maintain a history, the history is limited to depth number of generations to retain.

Generations act as a grouping mechanism to associate many records together as one logical and atomic group.

Any hashable key can be looked up in the ring to find the nodes that own that key.

Rings are named with a unique atom.

Union type that represents all valid options

Option that controls the number of generations to retain for lookup.

Option that controls the name to register this process under, Rings that are registered can use their name in place of their pid.

Option that controls the initial nodes for the Ring.

Option that controls the initial overrides for the Ring.

Option that controls the number of replicas to use for nodes that do not define replicas.

List of options that can be provided when starting a Ring, see the option/0 type and its associated types for more information.

Overrides allow the Ring to always resolve a given key to a set list of nodes.

Several functions accept either a name for a named Ring or a pid for an anonymous Ring

Ring size is a memoized count of the number of logical nodes in a ring

t()

Functions

Adds a node to the existing set of nodes in the ring.

Adds multiple nodes to the existing set of nodes in the ring.

Returns a specification to start this module under a supervisor.

Finds the specified number of nodes responsible for the given key in the specified ring's history, going back back number of generations.

Finds the node responsible for the given key in the specified ring.

Finds the specified number of nodes responsible for the given key in the specified ring's current generation.

Finds the specificed number of nodes responsible for the given key by looking at each generation in the ring's configured depth. See find_stable_nodes/4 for more information.

Finds the specified number of nodes responsible for the given key in the specified ring's current generation and in the history of the ring. This means that this function returns up to back * num; where num = number of nodes requested, and back = the number of generations to consider.

Forces a garbage collection of a specific generation, all generations pending garbage collection if :all_pending is specified. If a specific generation is specified, the it must be pending or else {:error, :not_pending} is returned.

Get the current ring generation

Retrieves the current set of node names from the ring.

Retrieves the current set of nodes as tuples of {name, replicas} from the ring.

Retrieves the current set of overrides from the ring.

Retrieves a list of pending gc generations.

Callback implementation for GenServer.init/1.

Removes a node from the ring by its name.

Atomically remove multiple nodes from the ring by name

Replaces the nodes in the ring with a new set of nodes.

Replaces the overrides in the ring with new overrides.

Start and link a Ring with the given name.

Stops the GenServer holding the Ring.

Types

depth()

@type depth() :: pos_integer()

Rings maintain a history, the history is limited to depth number of generations to retain.

generation()

@type generation() :: integer()

Generations act as a grouping mechanism to associate many records together as one logical and atomic group.

key()

@type key() :: ExHashRing.Hash.hashable()

Any hashable key can be looked up in the ring to find the nodes that own that key.

name()

@type name() :: atom()

Rings are named with a unique atom.

option()

Union type that represents all valid options

option_depth()

@type option_depth() :: {:depth, depth()}

Option that controls the number of generations to retain for lookup.

Defaults to 1

option_name()

@type option_name() :: {:name, name()}

Option that controls the name to register this process under, Rings that are registered can use their name in place of their pid.

Defaults behavior is to not register the Ring process.

option_nodes()

@type option_nodes() :: {:nodes, [ExHashRing.Node.definition()]}

Option that controls the initial nodes for the Ring.

Defaults to []

option_overrides()

@type option_overrides() :: {:overrides, overrides()}

Option that controls the initial overrides for the Ring.

Defaults to %{}

option_replicas()

@type option_replicas() :: {:replicas, ExHashRing.Node.replicas()}

Option that controls the number of replicas to use for nodes that do not define replicas.

Defaults to 512

options()

@type options() :: [option()]

List of options that can be provided when starting a Ring, see the option/0 type and its associated types for more information.

overrides()

@type overrides() :: %{required(key()) => [ExHashRing.Node.name()]}

Overrides allow the Ring to always resolve a given key to a set list of nodes.

ring()

@type ring() :: name() | pid()

Several functions accept either a name for a named Ring or a pid for an anonymous Ring

size()

@type size() :: non_neg_integer()

Ring size is a memoized count of the number of logical nodes in a ring

t()

@type t() :: %ExHashRing.Ring{
  depth: depth(),
  generation: generation(),
  nodes: [ExHashRing.Node.t()],
  overrides: overrides(),
  pending_gcs: %{required(generation()) => reference()},
  replicas: ExHashRing.Node.replicas(),
  sizes: [size()],
  table: :ets.tid()
}

Functions

add_node(ring, node_name, num_replicas \\ nil, timeout \\ 5000)

@spec add_node(
  ring(),
  ExHashRing.Node.name(),
  ExHashRing.Node.replicas() | nil,
  timeout :: timeout()
) ::
  {:ok, [ExHashRing.Node.t()]} | {:error, :node_exists}

Adds a node to the existing set of nodes in the ring.

add_nodes(ring, nodes, timeout \\ 5000)

@spec add_nodes(ring(), nodes :: [ExHashRing.Node.definition()], timeout :: timeout()) ::
  {:ok, [ExHashRing.Node.t()]} | {:error, :node_exists}

Adds multiple nodes to the existing set of nodes in the ring.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

do_find_stable_nodes(key, hash, num, back, info)

@spec do_find_stable_nodes(
  key(),
  hash :: ExHashRing.Hash.t(),
  num :: non_neg_integer(),
  back :: non_neg_integer(),
  info :: ExHashRing.Info.entry()
) :: {:ok, [ExHashRing.Node.name()]} | {:error, atom()}

find_historical_node(ring, key, back)

@spec find_historical_node(ring(), key(), back :: non_neg_integer()) ::
  {:ok, ExHashRing.Node.name()} | {:error, atom()}

find_historical_nodes(ring, key, num, back)

@spec find_historical_nodes(
  ring(),
  key(),
  num :: non_neg_integer(),
  back :: non_neg_integer()
) ::
  {:ok, [ExHashRing.Node.name()]} | {:error, atom()}

Finds the specified number of nodes responsible for the given key in the specified ring's history, going back back number of generations.

find_node(ring, key)

@spec find_node(ring(), key()) :: {:ok, ExHashRing.Node.name()} | {:error, atom()}

Finds the node responsible for the given key in the specified ring.

find_nodes(ring, key, num)

@spec find_nodes(ring(), key(), num :: non_neg_integer()) ::
  {:ok, [ExHashRing.Node.name()]} | {:error, reason :: atom()}

Finds the specified number of nodes responsible for the given key in the specified ring's current generation.

find_stable_nodes(ring, key, num)

@spec find_stable_nodes(ring(), key(), num :: non_neg_integer()) ::
  {:ok, [ExHashRing.Node.name()]} | {:error, atom()}

Finds the specificed number of nodes responsible for the given key by looking at each generation in the ring's configured depth. See find_stable_nodes/4 for more information.

find_stable_nodes(ring, key, num, back)

@spec find_stable_nodes(
  ring(),
  key(),
  num :: non_neg_integer(),
  back :: pos_integer()
) ::
  {:ok, [ExHashRing.Node.name()]} | {:error, atom()}

Finds the specified number of nodes responsible for the given key in the specified ring's current generation and in the history of the ring. This means that this function returns up to back * num; where num = number of nodes requested, and back = the number of generations to consider.

force_gc(ring, generation \\ :all_pending, timeout \\ 5000)

@spec force_gc(ring(), generation() | :all_pending, timeout :: timeout()) ::
  :ok | {:error, :not_pending}

Forces a garbage collection of a specific generation, all generations pending garbage collection if :all_pending is specified. If a specific generation is specified, the it must be pending or else {:error, :not_pending} is returned.

get_generation(ring)

@spec get_generation(ring()) :: {:ok, generation()} | :error

Get the current ring generation

get_nodes(ring, timeout \\ 5000)

@spec get_nodes(ring(), timeout :: timeout()) :: {:ok, [ExHashRing.Node.name()]}

Retrieves the current set of node names from the ring.

get_nodes_with_replicas(ring, timeout \\ 5000)

@spec get_nodes_with_replicas(ring(), timeout :: timeout()) ::
  {:ok, [ExHashRing.Node.t()]}

Retrieves the current set of nodes as tuples of {name, replicas} from the ring.

get_overrides(ring, timeout \\ 5000)

@spec get_overrides(ring(), timeout :: timeout()) :: {:ok, overrides()}

Retrieves the current set of overrides from the ring.

get_pending_gcs(ring, timeout \\ 5000)

@spec get_pending_gcs(ring(), timeout :: timeout()) :: {:ok, [generation()]}

Retrieves a list of pending gc generations.

init(options)

@spec init(options()) :: {:ok, t()}

Callback implementation for GenServer.init/1.

remove_node(ring, name, timeout \\ 5000)

@spec remove_node(ring(), name :: ExHashRing.Node.name(), timeout :: timeout()) ::
  {:ok, [ExHashRing.Node.t()]} | {:error, :node_not_exists}

Removes a node from the ring by its name.

remove_nodes(ring, names, timeout \\ 5000)

Atomically remove multiple nodes from the ring by name

set_nodes(ring, nodes, timeout \\ 5000)

@spec set_nodes(ring(), nodes :: [ExHashRing.Node.definition()], timeout :: timeout()) ::
  {:ok, [ExHashRing.Node.t()]}

Replaces the nodes in the ring with a new set of nodes.

set_overrides(ring, overrides, timeout \\ 5000)

@spec set_overrides(ring(), overrides(), timeout :: timeout()) :: {:ok, overrides()}

Replaces the overrides in the ring with new overrides.

start_link(options \\ [])

@spec start_link(options()) :: GenServer.on_start()

Start and link a Ring with the given name.

Ring supports various options see options/0 for more information.

stop(name)

@spec stop(ring()) :: :ok

Stops the GenServer holding the Ring.