Start a Jido Instance
This tutorial starts a Jido instance from a script, moves it into a supervision tree, and runs an Agent inside it. A Jido instance is the named supervisor that owns your running Agents.
Actions and Flows run through Jido.Exec.run/4 without an instance. You need one when an Agent runs as a process.
Install Jido
Mix.install([
{:jido, github: "mikehostetler/new_jido", branch: "main"}
])
Define an Agent to run
This is the invoice Agent from Getting Started, with an inline Action to keep the tutorial short.
defmodule StartInstance.InvoiceAgent do
use Jido.Agent, name: "start_instance_invoice"
state do
schema Jido.Joi.object(%{paid_cents: Jido.Joi.integer() |> Jido.Joi.default(0)})
end
routes do
signal_source "/billing/invoice"
route "invoice.payment.apply", as: :apply_payment do
action %{amount_cents: amount},
schema: Jido.Joi.object(%{amount_cents: Jido.Joi.integer()}),
context: context do
{:ok, %{context.agent_state | paid_cents: context.agent_state.paid_cents + amount}}
end
end
end
end
Start the default instance
Jido.start/1 suits scripts and Livebook. With no :name, it starts the default instance, whose name is the module-style atom Jido.Default. Calling it again returns the running instance's PID and ignores any new options.
{:ok, default_pid} = Jido.start()
{:ok, ^default_pid} = Jido.start(max_tasks: 2_000)
Process.whereis(Jido.Default) == default_pid
The cell returns true.
Pass the instance name to every instance function.
{:ok, server} = Jido.start_agent(Jido.Default, StartInstance.InvoiceAgent, id: "inv-1001")
:ok = Jido.AgentServer.await_ready(server)
{Jido.agent_count(Jido.Default), Jido.whereis_agent(Jido.Default, "inv-1001") == server}
The cell returns {1, true}.
The Agent links to the instance's supervisor, not to the process that started it. Stopping the instance stops its Agents.
:ok = Jido.stop()
{Process.whereis(Jido.Default), Process.alive?(server)}
The cell returns {nil, false}.
Define an instance module
An application defines a module with use Jido. :otp_app is required, and it names the application whose config the module reads.
defmodule StartInstance.Jido do
use Jido,
otp_app: :start_instance,
namespace: "start-instance/primary"
end
The :namespace option is a nonempty string that gives this instance a stable identity. You need it for stable Agent Refs and for persistent Agents. One namespace can bind to one live instance per node.
Add the instance to a supervision tree
In an application, put the module in the children list in lib/my_app/application.ex. This cell starts the same tree directly so that you can see it run.
children = [StartInstance.Jido]
{:ok, tree} = Supervisor.start_link(children, strategy: :one_for_one)
Supervisor.which_children(tree) |> Enum.map(&elem(&1, 0))
The cell returns [StartInstance.Jido]. The instance module's name is the instance's name.
{:ok, server} = Jido.start_agent(StartInstance.Jido, StartInstance.InvoiceAgent, id: "inv-2001")
:ok = Jido.AgentServer.await_ready(server)
signal = StartInstance.InvoiceAgent.apply_payment_signal!(%{amount_cents: 700})
{:ok, agent, []} = Jido.AgentServer.call(server, signal)
agent.state
The cell returns %{paid_cents: 700}.
Configure the instance
The module reads config :my_app, MyApp.Jido and merges it with any options in the child spec. config/0 shows the result. This cell uses Application.put_env/3 in place of a config file.
Application.put_env(:start_instance, StartInstance.Jido, max_tasks: 2_000)
StartInstance.Jido.config()
The cell returns [max_tasks: 2000]. Jido reads this config when the instance starts, so it applies to the next start.
| Option | Meaning | Default |
|---|---|---|
:name |
The instance name, a non-nil atom. Required by Jido.start_link/1. A use Jido module uses its own name. |
The default instance under Jido.start/1 |
:namespace |
Stable identity for Refs and persistent Agents. | nil |
:max_tasks |
Child limit for the instance's Task supervisor, a nonnegative integer or :infinity. |
1_000 |
:persistence |
Adapter, as a module or {module, options}, for Jido.thaw/4, Jido.activate_agent/4, and persistence: :inherit. Jido.start_agent/3 ignores it unless you pass persistence: :inherit. |
nil |
:agent_server |
Default AgentServer runtime options for this instance. | [] |
:debug |
Log detail for this instance: false, true, or :verbose. |
nil |
An unknown option returns an error.
{:error, %Jido.Error.ValidationError{details: %{keys: keys}}} =
Jido.start_link(name: StartInstance.Other, max_agents: 10)
keys
The cell returns [:max_agents].
Run two instances
Two instances have separate registries, so the same Agent ID can run in each. Use this to keep tenants or test runs apart.
{:ok, _second} = Jido.start(name: StartInstance.Second)
{:ok, other_server} = Jido.start_agent(StartInstance.Second, StartInstance.InvoiceAgent, id: "inv-2001")
{
other_server != server,
Jido.whereis_agent(StartInstance.Jido, "inv-2001") == server,
Jido.whereis_agent(StartInstance.Second, "inv-2001") == other_server
}
The cell returns {true, true, true}.
:ok = Jido.stop(StartInstance.Second)
:ok = Supervisor.stop(tree)
Process.whereis(StartInstance.Jido)
The cell returns nil.
Next steps
- Build Your First Action writes the work that an Agent runs.
- Start and Address Agents covers partitions, Refs, and OTP names.
- Instances and Tenants covers tenant isolation.