Complete API reference for all public modules in rpc_load_balancer.
RpcLoadBalancer
Top-level module providing RPC wrappers around :erpc.
Types
@spec call(node(), module(), atom(), [any()], timeout: timeout()) :: ErrorMessage.t_res(any())
@spec cast(node(), module(), atom(), [term()]) :: :ok | {:error, ErrorMessage.t()}Functions
call(node, module, fun, args, opts \\ [timeout: :timer.seconds(10)])
Executes a synchronous RPC call on the given node. Wraps :erpc.call/5.
Returns:
{:ok, result}on success{:error, %ErrorMessage{code: :request_timeout}}on timeout{:error, %ErrorMessage{code: :service_unavailable}}on connection failure{:error, %ErrorMessage{code: :bad_request}}on bad arguments
Options:
:timeout— call timeout in milliseconds (default:10_000)
cast(node, module, fun, args)
Executes an asynchronous RPC cast on the given node. Wraps :erpc.cast/4.
Returns:
:okon success{:error, %ErrorMessage{}}on failure
RpcLoadBalancer.LoadBalancer
GenServer-based load balancer that registers nodes via :pg and selects them using a pluggable algorithm.
Types
@type name :: atom() | module()
@type node_match_list :: [String.t() | Regex.t()] | :all
@type option ::
{:node_match_list, node_match_list()}
| {:selection_algorithm, module()}
| {:algorithm_opts, keyword()}
@type opts :: [GenServer.option() | option()]Functions
start_link(opts \\ [])
Starts a load balancer GenServer.
Options:
:name— registered name for the balancer (auto-generated if omitted):selection_algorithm— module implementingSelectionAlgorithm(default:SelectionAlgorithm.Random):algorithm_opts— keyword list forwarded to the algorithm'sinit/2callback (default:[]):node_match_list— controls which nodes join the:pggroup (default::all):all— every node joins[String.t() | Regex.t()]— only nodes matching at least one entry join
Returns: GenServer.on_start()
select_node(load_balancer_name, opts \\ [])
Selects a node from the balancer's registered members using the configured algorithm.
Options: forwarded to the algorithm's choose_from_nodes/3 (e.g., key: "user:123" for HashRing)
Returns:
{:ok, node()}on success{:error, %ErrorMessage{code: :service_unavailable}}when no nodes are registered
release_node(load_balancer_name, node)
Decrements the connection counter for the given node. Only meaningful for connection-tracking algorithms (LeastConnections, PowerOfTwo). No-op for other algorithms.
Returns: :ok
call(load_balancer_name, module, fun, args, opts \\ [])
Selects a node, executes a synchronous RPC call, then releases the node.
Options:
:key— forwarded to the selection algorithm (used by HashRing):timeout— forwarded toRpcLoadBalancer.call/5
Returns: ErrorMessage.t_res(any())
cast(load_balancer_name, module, fun, args, opts \\ [])
Selects a node and executes an asynchronous RPC cast.
Options:
:key— forwarded to the selection algorithm
Returns: :ok | {:error, ErrorMessage.t()}
select_nodes(load_balancer_name, count, opts \\ [])
Selects multiple nodes from the balancer's registered members. Algorithms that implement choose_nodes/4 (e.g., HashRing) provide consistent multi-node selection. Others fall back to randomly shuffled nodes.
Options: forwarded to the algorithm's choose_nodes/4 (e.g., key: "user:123" for HashRing)
Returns:
{:ok, [node()]}— up tocountdistinct nodes{:error, %ErrorMessage{code: :service_unavailable}}when no nodes are registered
get_members(load_balancer_name)
Returns the deduplicated list of nodes registered in the :pg group for this balancer.
Returns:
{:ok, [node()]}when members exist{:error, %ErrorMessage{code: :service_unavailable}}when the group is empty
pg_group_name()
Returns the :pg scope atom used by all load balancers: :rpc_load_balancer.
RpcLoadBalancer.LoadBalancer.SelectionAlgorithm
Behaviour definition and dispatch layer for selection algorithms.
Callbacks
Required
@callback choose_from_nodes(load_balancer_name(), [node()], opts :: keyword()) :: node()Called to pick one node from the available list. Receives the balancer name, the current node list, and any caller-provided options.
Optional
@callback init(load_balancer_name(), opts :: keyword()) :: :okCalled once during balancer startup. Receives algorithm_opts from start_link/1.
@callback choose_nodes(load_balancer_name(), [node()], pos_integer(), opts :: keyword()) :: [node()]Called by LoadBalancer.select_nodes/3 to pick multiple distinct nodes for a given key. Used for replica selection. Algorithms that don't implement this fall back to returning randomly shuffled nodes.
@callback on_node_change(load_balancer_name(), {:joined | :left, [node()]}) :: :okCalled when the :pg group membership changes.
@callback release_node(load_balancer_name(), node()) :: :okCalled after an RPC call completes to clean up per-node state.
Built-in Algorithms
All algorithms live under RpcLoadBalancer.LoadBalancer.SelectionAlgorithm.*.
Random
Picks a random node using Enum.random/1. No state, no configuration.
RoundRobin
Cycles through nodes using an atomic ETS counter (CounterCache). The counter auto-resets after 10,000,000 to prevent overflow.
LeastConnections
Tracks active connections per node with ETS counters. Always picks the node with the lowest count. Increments on selection, decrements on release_node/2.
Implements: init/2, choose_from_nodes/3, on_node_change/2, release_node/2
PowerOfTwo
Samples two random nodes and picks the one with fewer active connections. Same counter infrastructure as LeastConnections but with O(1) selection cost instead of O(n).
Implements: init/2, choose_from_nodes/3, on_node_change/2, release_node/2
HashRing
Consistent hash ring powered by libring. Each physical node is sharded into weight points (default: 128) distributed across a 2^32 continuum using SHA-256. Key lookup finds the next highest shard on the ring via gb_tree. Falls back to random selection when no key is given. The ring is stored in ETS and lazily rebuilt when topology changes.
Supports replica selection via choose_nodes/4 using HashRing.key_to_nodes/3 — returns multiple distinct nodes for a given key, walking the ring from the primary shard.
Algorithm options:
:weight— number of shards per physical node (default:128)
Implements: init/2, choose_from_nodes/3, choose_nodes/4, on_node_change/2
WeightedRoundRobin
Expands the node list by duplicating each node according to its weight, then cycles through with an atomic counter. Weights are passed via algorithm_opts: [weights: %{node => integer}]. Nodes without an explicit weight default to 1.
Implements: init/2, choose_from_nodes/3
Internal Modules
These modules are not part of the public API but are documented here for contributors.
RpcLoadBalancer.LoadBalancer.Pg
Starts and wraps the :pg scope (:rpc_load_balancer). Started as a child of the application supervisor.
RpcLoadBalancer.LoadBalancer.AlgorithmCache
ETS cache (via elixir_cache) that maps load_balancer_name -> algorithm_module. Configured with read_concurrency: true and write_concurrency: true.
RpcLoadBalancer.LoadBalancer.CounterCache
ETS cache (via elixir_cache) used for atomic counters (round robin indices, connection counts) and weight storage. Configured with read_concurrency: true and write_concurrency: true.