0.3.6
Fixes
Widen the
node_filterargument ofRpcLoadBalancer.call_on_random_node/5andRpcLoadBalancer.cast_on_random_node/5fromString.t()toString.t() | Regex.t(). Both already route node matching throughRpcLoadBalancer.NodeFilter.matches?/2, which has accepted aRegex.t()(including theexcluded_node_patternscarve-out, read viaRegex.source/1) since 0.3.4 — only the specs still said string, so a regex filter worked at runtime but failed dialyzer for every consumer. An anchored filter such as~r/^my_service@/is now spec-valid, which matters where a plain substring filter would also match a differently-suffixed node of the same family (e.g.my_service_scratch@…) andEnum.random/1would route to it.
0.3.5
Fixes
- Rebuild the
HashRingring whenever the node list it is asked to select from no longer matches the ring's own node set. Previouslyget_or_build_ring/2returned the cached ring and ignorednode_listentirely, relying solely on the asynchronouson_node_change/2callback for invalidation — soselect_node/2could return a node that had already left the cluster, and a dropped or out-of-order:pgmonitor message left the stale ring in place indefinitely. Observed in production: an autoscaling instance refresh replaced a node, and every routed call kept selecting the terminated one, returningservice_unavailable "noconnection"until the calling node was restarted. Membership is now the source of truth rather than message delivery.
Docs
- Overhaul all guides for 0.3.x:
use RpcLoadBalancernamed modules,LeastCpu, telemetry/metrics, no-route retry,excluded_node_patterns, and the current cache/storage layout. New how-tos: named load balancer module, least CPU, retry behaviour, telemetry and metrics. Reference and architecture docs rewritten to match the code; stale moduledocs (select_nodes/3, ETS counters,RpcLoadBalancer.LoadBalancer.start_link) corrected. - Rewrite
AGENTS.mdfor this repository and fill in theelixir_skillsconsumer skill. - Remove
docs/superpowers/design/plan scratch files (they were shipped in the Hex package viafiles: ~w(... docs)).
0.3.4
Features
- Add
use RpcLoadBalancerto define a named load balancer module exposing the fullRpcLoadBalancerinterface bound to a fixed configuration, withchild_spec/1/start_link/1for direct supervision. (#8) - Retry no-route conditions on the load-balancer-routed
call/5andcast/5paths (theload_balancer:option), backing off per:retry?/:retry_count/:retry_sleepinstead of failing immediately; non-selection errors pass through un-retried. (#8) - Support
retry_count: :infinityinRpcLoadBalancer.Retry.with_retry/2(previously raisedArithmeticError). (#8) - Add configurable, filter-relative node exclusion via
config :rpc_load_balancer, excluded_node_patterns: [...](default[]). A node whose short name carries an excluded pattern is dropped from any filter that does not itself carry it, while a filter carrying the pattern still reaches it. All node matching routes throughRpcLoadBalancer.NodeFilter.matches?/2,3. (#10)
Fixes
- Raise a clear, actionable error from
IndexRegistry.get_or_register/2when the cache's index counter was never initialized, naming the missing cache/node instead of an opaque:persistent_termArgumentError. (#9) - Bypass telemetry on hot reads and use single-pass algorithm selection. (#2)
0.3.2
Features
- Extend
RpcLoadBalancer.Metricsduration histogram buckets to30_000and60_000ms. Previously the top bucket was10_000ms — calls exceeding 10s landed only in the+Infbucket sohistogram_quantile/2lost precision above 10s. Long-running RPCs (heavy reports, large data transfers, or any caller with a:timeoutopt above the default 10s) now have meaningful p95/p99 measurements up to 60s.
0.3.1
Features
- Emit
[:rpc_load_balancer, :node_selected]telemetry after every successful pick bySelectionAlgorithm.choose_from_nodes/4. Measurements:count: 1, members_count: pool_size. Metadata::algorithm(the algorithm module atom — e.g.RpcLoadBalancer.LoadBalancer.SelectionAlgorithm.RoundRobin),:load_balancer,:node. Reveals selection skew, per-algorithm call mix, and cluster pool size over time. - Emit
[:rpc_load_balancer, :node_selected, :empty]when the algorithm is invoked with an empty member list. Telemetry fires first, then the algorithm raises whatever it would have raised (e.g.Enum.EmptyErrorfromRandom) — original contract preserved. - Add
rpc_load_balancer.node.selected.count,rpc_load_balancer.node.selected.empty.count, andrpc_load_balancer.node.pool_sizetoRpcLoadBalancer.Metrics.metrics/0. - Widen the duration histogram buckets to cover sub-millisecond cluster RPC:
0.1, 0.5, 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000ms (was1, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000— local-cluster calls hit the floor at 1ms so quantiles capped at 5ms in dashboards).
0.3.0
Features
- Wrap
RpcLoadBalancer.call/5andRpcLoadBalancer.cast/5in:telemetry.span/3under the prefix[:rpc_load_balancer, :rpc]. Emits:start,:stop(with:durationmeasurement), and:exceptionevents. Metadata::type(:callor:cast),:node,:module(inspect/1'd),:function,:load_balancer(nullable), and on:stopa:statusderived from the result tuple (:ok,ErrorMessage.code, or:error). - Add
RpcLoadBalancer.Metricswithmetrics/0returning ready-to-registerTelemetry.Metricsdefinitions forrpc_load_balancer.rpc.request.start.count,rpc_load_balancer.rpc.request.stop.count, andrpc_load_balancer.rpc.duration.milliseconds. Drop into anyPrometheusTelemetrysupervisor. - Declare
:telemetryand:telemetry_metricsas direct dependencies (previously transitive via:elixir_cache).
0.2.2
Features
- Add
LeastCpuselection algorithm that routes calls to the node with the lowest CPU utilization, sampled via:cpu_supand cached in:persistent_term - Add optional
child_specs/2callback to theSelectionAlgorithmbehaviour so algorithms can contribute their own supervised children
Fixes
- Ensure the configured selection algorithm module is loaded before checking for
child_specs/2, preventing startup races - Use
Cache.PersistentTermforNodeCpuCacheand fix the:cpu_supapply call to stabilize CPU sampling
0.2.1
Features
- Add
CallDirectselection algorithm for local-node execution without:erpcoverhead - Add
Drainermodule for graceful connection draining with in-flight call tracking - Add
IndexRegistryfor lock-free index allocation using:atomicsand:persistent_term - Add
Retrymodule with configurable retry logic for RPC operations - Add
Configmodule for centralized application configuration defaults - Add
ValueCachebacked byCache.PersistentTerm - Add
call_directly?option tocall/5,cast/5,call_on_random_node/5, andcast_on_random_node/5 - Add
select_node/2to the public API
Refactors
- Consolidate cache initialization into
RpcLoadBalancersupervisor - Extract retry logic from
LoadBalancerinto dedicatedRetrymodule - Simplify selection algorithms (
LeastConnections,PowerOfTwo,RoundRobin,WeightedRoundRobin) by leveraging shared caches - Promote
RpcLoadBalancerto aSupervisormanaging caches and the load balancer GenServer
Docs
- Add
testing-with-call-directhow-to guide - Update architecture docs, reference docs, and getting started tutorial for new API
0.1.0
- Initial Release