Skip to content

ohhi-vn/easy_rpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Docs Hex.pm

EasyRpc

A library that makes it easy to wrap a remote procedure call (RPC) as a local function. EasyRpc uses Erlang's :erpc module under the hood and adds retry, timeout, and error-handling support on top.

Each function can carry its own options, or inherit global options declared at the module level. EasyRpc works seamlessly with ClusterHelper for dynamic Elixir clusters.

Note: Collab between human & AI.

Installation

Add easy_rpc to your dependencies in mix.exs:

def deps do
  [
    {:easy_rpc, "~> 0.7.0"}
  ]
end

Two Usage Approaches

DefRpc RpcWrapper
Functions declared In module via defrpc macro In config file
Node config loaded At runtime per call At compile time
Cluster topology changes ✅ Picked up automatically Requires recompile
Best for Explicit control, dynamic clusters All-in-config, stable topology

Usage — DefRpc (declarative macros)

1. Add node config

# config/config.exs  (or runtime.exs for runtime topology)
config :my_app, :remote_defrpc,
  nodes: [:"remote@127.0.0.1"],
  # or: nodes: {ClusterHelper, :get_nodes, [:remote_api]},
  select_mode: :round_robin,
  sticky_node: true

:round_robin and :sticky_node are tracked per process.

2. Declare functions

defmodule MyApp.Remote do
  use EasyRpc.DefRpc,
    otp_app: :my_app,
    config_name: :remote_defrpc,
    module: RemoteNode.Interface,
    timeout: 1_000

  defrpc :get_data
  defrpc :put_data, args: 1
  defrpc :clear, args: 2, as: :clear_data, private: true
  defrpc :put_data, args: [:name], as: :put_with_retry, retry: 3, sleep_before_retry: 200, timeout: 1_000
end

defrpc options:

Option Description
:args Arity as integer, [] (zero), or list of named atoms
:as / :new_name Override the generated function name
:private Generate as defp (default: false)
:retry Override global retry count
:sleep_before_retry Milliseconds to wait before each retry (default: 0)
:timeout Override global timeout (ms or :infinity)
:error_handling Override global error-handling flag

Usage — RpcWrapper (config-driven)

All function and node information is declared in config. Functions are generated at compile time.

1. Add config

# config/config.exs
config :my_app, :data_wrapper,
  nodes: [:"node1@host", :"node2@host"],
  # or: nodes: {ClusterHelper, :get_nodes, [:data]},
  error_handling: true,
  select_mode: :random,
  module: TargetApp.Interface.Api,
  timeout: 5_000,
  retry: 3,
  sleep_before_retry: 500,       # wait 500 ms between retries
  functions: [
    # {function_name, arity}
    # {function_name, arity, options}
    {:get_data, 1},
    {:put_data, 1, [error_handling: false]},
    {:clear, 2, [new_name: :clear_data, retry: 3, sleep_before_retry: 100]},
    {:clear_all, 0, [new_name: :reset, private: true]}
  ]

2. Use in your module

defmodule MyApp.DataHelper do
  use EasyRpc.RpcWrapper,
    otp_app: :my_app,
    config_name: :data_wrapper

  def process_remote() do
    case get_data("key") do
      {:ok, data}     -> data
      {:error, reason} -> {:error, reason}
    end
  end
end

# Or call directly:
{:ok, result} = MyApp.DataHelper.get_data("my_key")

Node Selection Strategies

Configure via select_mode: in your config:

Strategy Description
:random Randomly picks a node on each call (default)
:round_robin Circular distribution, tracked per process
:hash Consistent hashing on args — same args always hit the same node

Sticky Nodes

config :my_app, :api,
  nodes: [:node1@host, :node2@host],
  select_mode: :random,
  sticky_node: true   # process pins to first selected node

Dynamic Node Discovery

config :my_app, :api,
  nodes: {ClusterHelper, :get_nodes, [:backend]},
  select_mode: :round_robin

Error Handling

Without error handling (default — raises on error)

user = MyApi.get_user(123)

With error handling (returns tagged tuples)

case MyApi.get_user(123) do
  {:ok, user}                    -> process(user)
  {:error, %EasyRpc.Error{} = e} -> Logger.error(EasyRpc.Error.format(e))
end

Enable globally in config or per function:

config :my_app, :api, error_handling: true

# or per defrpc:
defrpc :get_user, args: 1, error_handling: true

Retry Logic

# Global retry
config :my_app, :api, retry: 3

# Per-function
defrpc :critical_op, args: 1, retry: 5

When retry > 0, error_handling is automatically enabled — retried calls always return {:ok, result} | {:error, %EasyRpc.Error{}}.


Sleep Before Retry

By default EasyRpc retries immediately after a failure. Use sleep_before_retry to add a fixed delay (in milliseconds) between attempts. This is useful for giving a remote node time to recover, or for reducing thundering-herd pressure on a flapping service.

# Global — all retries in this config wait 500 ms
config :my_app, :api,
  retry: 3,
  sleep_before_retry: 500

# Per-function override
defrpc :critical_op, args: 1, retry: 5, sleep_before_retry: 200

The sleep happens between attempts — there is no delay before the first call, and no delay after the final failure.

attempt 1 → fails → sleep 500 ms
attempt 2 → fails → sleep 500 ms
attempt 3 → fails → sleep 500 ms
attempt 4 → fails → return {:error, ...}

sleep_before_retry requires a non-negative integer. The default is 0 (no sleep). Setting it without also setting retry has no effect.


Timeout Configuration

# Global
config :my_app, :api, timeout: 5_000

# Per-function
defrpc :long_op,      args: 1, timeout: 30_000
defrpc :health_check,          timeout: 500
defrpc :no_limit,              timeout: :infinity

Examples

See the lib_examples repository for complete, runnable examples.


AI Agents & MCP Support

Sync usage rules from deps into your repo for AI agent support:

mix usage_rules.sync AGENTS.md --all \
  --link-to-folder deps \
  --inline usage_rules:all

Start the MCP server:

mix tidewave

Configure your agent to connect to http://localhost:4113/tidewave/mcp (change port in mix.exs if needed). See Tidewave docs for details.

About

Help dev can work easily with rpc in Elixir cluster

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages