Overview

The hash ring behaviour is controlled through options passed to make/2 and make_from_keys/2. All options are optional.

# All defaults
ring = ExRingRing.make(nodes)

# Custom configuration
ring = ExRingRing.make(nodes,
  virtual_node_count: 512,
  max_hash_byte_size: 8,
  hash_algorithm: :sha256,
  implementation: :dynamic
)

Options Reference

OptionTypeDefaultDescription
:virtual_node_countpos_integer256Virtual nodes per physical node
:max_hash_byte_sizepos_integer4Significant bytes of hash value
:hash_algorithmatom:md5Hash function to use
:implementation:static | :dynamic:staticRing data structure

Hash Algorithms

AlgorithmByte SizePerformanceUse Case
:md516FastGeneral purpose (default)
:sha20ModerateWhen larger hash space needed
:sha25632SlowestMaximum hash quality
:crc324FastestHigh throughput, small hash ok
:phash24FastestErlang native, no binary conversion

The max_hash_byte_size option controls how many bytes of the hash are used. For example, with :md5 (16 bytes) and max_hash_byte_size: 4, only the first 4 bytes are used. This is clamped to the algorithm's maximum.

# Uses all 32 bytes of SHA256
ring = ExRingRing.make(nodes, hash_algorithm: :sha256, max_hash_byte_size: 32)

# Fast CRC32 with minimal hash space
ring = ExRingRing.make(nodes, hash_algorithm: :crc32)

Virtual Nodes

Each physical node is represented by multiple virtual nodes spread around the ring. More virtual nodes = better distribution at the cost of memory.

# 128 virtual nodes per physical node (less memory, slightly less balanced)
ring = ExRingRing.make(nodes, virtual_node_count: 128)

# 1024 virtual nodes (more balanced, more memory)
ring = ExRingRing.make(nodes, virtual_node_count: 1024)

Node Weights

Node weights scale the number of virtual nodes assigned to a node, giving it proportionally more hash space.

# 3-argument form: key, data, weight
node = ExRingRing.Domain.Entities.Node.make(:big, "10x capacity", 10)

# With keyword options: key, data, [weight: n]
node = ExRingRing.Domain.Entities.Node.make(:small, "1x capacity", weight: 1)

# Using list_to_nodes
nodes = ExRingRing.list_to_nodes([
  {:server_a, "10gb_ram", [weight: 2]},
  {:server_b, "10gb_ram", [weight: 2]},
  {:server_c, "4gb_ram",  [weight: 1]}
])

Phantom Nodes (Weight 0)

A node with weight 0 exists in the ring but is invisible to find_node/2 and collect_nodes/3. Useful for standby/failover members.

nodes = [
  ExRingRing.Domain.Entities.Node.make(:active, "primary", weight: 1),
  ExRingRing.Domain.Entities.Node.make(:standby, "failover", weight: 0)
]

ring = ExRingRing.make(nodes)

ExRingRing.get_nodes(ring)        # => [%Node{key: :active}, %Node{key: :standby}]
ExRingRing.get_node_count(ring)    # => 2
ExRingRing.find_node(ring, "key")  # => {:ok, %Node{key: :active}}  (never :standby)

To promote a phantom to an active node, simply add it with a positive weight:

ring = ExRingRing.add_node(ring, ExRingRing.Domain.Entities.Node.make(:standby, "now_active", weight: 1))