Jido.Persist (Jido v2.0.0-rc.4)

View Source

Coordinates hibernate/thaw operations for agents with thread support.

This module is the invariant enforcer - it ensures:

  1. Journal is flushed before checkpoint
  2. Checkpoint never contains full Thread, only a pointer
  3. Thread is rehydrated on thaw

API

The primary API accepts a storage configuration tuple:

Jido.Persist.hibernate({adapter, opts}, agent)
Jido.Persist.thaw({adapter, opts}, agent_module, key)

Or a Jido instance with embedded storage config:

Jido.Persist.hibernate(jido_instance, agent)
Jido.Persist.thaw(jido_instance, agent_module, key)

hibernate/2 Flow

  1. Extract thread from agent.state[:__thread__]
  2. If thread exists with entries, flush journal via adapter.append_thread/3
  3. Call agent_module.checkpoint/2 if implemented, else use default
  4. Enforce invariant: Remove :__thread__ from state, store only thread pointer
  5. Call adapter.put_checkpoint/3

thaw/3 Flow

  1. Call adapter.get_checkpoint/2
  2. If :not_found, return :not_found
  3. Call agent_module.restore/2 if implemented, else use default
  4. If checkpoint has thread pointer, load and attach thread
  5. Verify loaded thread.rev matches checkpoint pointer rev

Agent Callbacks

Agents may optionally implement:

  • checkpoint(agent, ctx) - Returns {:ok, checkpoint_data} for custom serialization
  • restore(checkpoint_data, ctx) - Returns {:ok, agent} for custom deserialization

If not implemented, default serialization is used.

Examples

# Using storage config tuple
storage = {Jido.Storage.ETS, table: :my_storage}

# Hibernate an agent
:ok = Jido.Persist.hibernate(storage, agent)

# Thaw an agent
case Jido.Persist.thaw(storage, MyAgent, "agent-123") do
  {:ok, agent} -> agent
  :not_found -> start_fresh()
  {:error, :missing_thread} -> handle_missing_thread()
  {:error, :thread_mismatch} -> handle_mismatch()
end

Summary

Functions

Persists an agent to storage, flushing any pending thread entries first.

Restores an agent from storage, rehydrating thread if present.

Types

agent()

@type agent() :: struct()

agent_module()

@type agent_module() :: module()

checkpoint()

@type checkpoint() :: %{
  version: pos_integer(),
  agent_module: agent_module(),
  id: term(),
  state: map(),
  thread: thread_pointer() | nil
}

checkpoint_key()

@type checkpoint_key() :: {agent_module(), term()}

key()

@type key() :: term()

storage_config()

@type storage_config() :: {module(), keyword()}

thread_pointer()

@type thread_pointer() :: %{id: String.t(), rev: non_neg_integer()}

Functions

hibernate(storage_or_instance, agent)

@spec hibernate(storage_config() | module() | struct(), agent()) ::
  :ok | {:error, term()}

Persists an agent to storage, flushing any pending thread entries first.

Accepts either a {adapter, opts} tuple or a struct with :storage field.

Examples

storage = {Jido.Storage.ETS, table: :agents}
:ok = Jido.Persist.hibernate(storage, my_agent)

Returns

  • :ok - Successfully hibernated
  • {:error, reason} - Failed to hibernate

thaw(storage_or_instance, agent_module, key)

@spec thaw(storage_config() | module() | struct(), agent_module(), key()) ::
  {:ok, agent()} | :not_found | {:error, term()}

Restores an agent from storage, rehydrating thread if present.

Accepts either a {adapter, opts} tuple or a struct with :storage field.

Examples

storage = {Jido.Storage.ETS, table: :agents}
{:ok, agent} = Jido.Persist.thaw(storage, MyAgent, "agent-123")

Returns

  • {:ok, agent} - Successfully thawed
  • :not_found - No checkpoint exists for this key
  • {:error, :missing_thread} - Checkpoint references thread that doesn't exist
  • {:error, :thread_mismatch} - Loaded thread.rev != checkpoint thread.rev
  • {:error, reason} - Other errors