# Configuration

## Overview

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

```elixir
# 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

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `:virtual_node_count` | `pos_integer` | `256` | Virtual nodes per physical node |
| `:max_hash_byte_size` | `pos_integer` | `4` | Significant bytes of hash value |
| `:hash_algorithm` | `atom` | `:md5` | Hash function to use |
| `:implementation` | `:static \| :dynamic` | `:static` | Ring data structure |

## Hash Algorithms

| Algorithm | Byte Size | Performance | Use Case |
|-----------|-----------|-------------|----------|
| `:md5` | 16 | Fast | General purpose (default) |
| `:sha` | 20 | Moderate | When larger hash space needed |
| `:sha256` | 32 | Slowest | Maximum hash quality |
| `:crc32` | 4 | Fastest | High throughput, small hash ok |
| `:phash2` | 4 | Fastest | Erlang 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.

```elixir
# 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.

```elixir
# 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.

```elixir
# 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.

```elixir
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:
```elixir
ring = ExRingRing.add_node(ring, ExRingRing.Domain.Entities.Node.make(:standby, "now_active", weight: 1))
```
