ActorSimulation (GenServerVirtualTime v0.1.0)

View Source

A DSL for simulating actor systems with message rates and statistics.

This module provides a way to define actors, their message sending patterns, and simulate their interactions using virtual time.

Example

simulation =
  ActorSimulation.new()
  |> ActorSimulation.add_actor(:producer,
      send_pattern: {:periodic, 100, {:data, :id}},
      targets: [:consumer])
  |> ActorSimulation.add_actor(:consumer,
      on_receive: fn msg, state ->
        # Process message and maybe send response
        {:ok, state}
      end)
  |> ActorSimulation.run(duration: 5000)

stats = ActorSimulation.get_stats(simulation)
IO.inspect(stats)

Summary

Functions

Adds an actor to the simulation.

Adds a real GenServerVirtualTime process to the simulation ("Process in the Loop").

Enables message tracing for the simulation.

Gets statistics from the simulation.

Gets the message trace from the simulation. Returns a list of trace events for building sequence diagrams.

Creates a new actor simulation.

Runs the simulation for the specified duration (in milliseconds).

Stops the simulation and cleans up resources.

Formats the trace as a Mermaid sequence diagram with enhanced styling.

Formats the trace as a PlantUML sequence diagram.

Functions

add_actor(simulation, name, opts \\ [])

Adds an actor to the simulation.

Options:

  • :send_pattern - How this actor sends messages:
    • {:periodic, interval, message} - Send message every interval ms
    • {:rate, messages_per_second, message} - Send at a specific rate
    • {:burst, count, interval, message} - Send count messages every interval
  • :targets - List of actor names to send messages to
  • :on_receive - Function called when receiving a message: fn msg, state -> {:ok, new_state} | {:send, msgs, new_state} end

  • :on_match - Pattern matching responses: [{pattern, response_fn}]
  • :initial_state - Initial state for the actor (default: %{})

add_process(simulation, name, opts \\ [])

Adds a real GenServerVirtualTime process to the simulation ("Process in the Loop").

This allows you to test real GenServer implementations alongside simulated actors.

Options:

  • :module - The GenServer module to start (required)
  • :args - Arguments to pass to the module's init/1
  • :targets - List of actor names this process can send to (optional)

Example

defmodule MyRealServer do
  use VirtualTimeGenServer

  def init(args), do: {:ok, args}
  def handle_call(:ping, _from, state), do: {:reply, :pong, state}
end

simulation =
  ActorSimulation.new()
  |> ActorSimulation.add_process(:my_server, module: MyRealServer, args: %{})
  |> ActorSimulation.add_actor(:pinger,
      send_pattern: {:periodic, 100, {:call, :my_server, :ping}})

enable_trace(simulation)

Enables message tracing for the simulation.

get_stats(simulation)

Gets statistics from the simulation.

get_trace(simulation)

Gets the message trace from the simulation. Returns a list of trace events for building sequence diagrams.

Each event is a map with:

  • :timestamp - Virtual time when message was sent
  • :from - Sender actor name
  • :to - Receiver actor name
  • :message - The message sent
  • :type - :cast, :call, or :send

new(opts \\ [])

Creates a new actor simulation.

Options:

  • :trace - Enable message tracing for sequence diagrams (default: false)

Example

iex> simulation = ActorSimulation.new()
iex> is_pid(simulation.clock)
true
iex> simulation.actors
%{}

run(simulation, opts \\ [])

Runs the simulation for the specified duration (in milliseconds).

stop(simulation)

Stops the simulation and cleans up resources.

trace_to_mermaid(simulation, opts \\ [])

Formats the trace as a Mermaid sequence diagram with enhanced styling.

Mermaid is widely supported in GitHub, GitLab, and many markdown viewers. Uses features from Mermaid sequence diagrams:

  • Different arrow types for call/cast/send
  • Activation boxes for processing
  • Notes with timestamps
  • Background highlighting for grouped interactions

Example

iex> simulation = %ActorSimulation{trace: [
...>   %{from: :alice, to: :bob, message: :hello, type: :send, timestamp: 100},
...>   %{from: :bob, to: :alice, message: :hi, type: :send, timestamp: 200}
...> ]}
iex> mermaid = ActorSimulation.trace_to_mermaid(simulation)
iex> String.contains?(mermaid, "sequenceDiagram")
true
iex> String.contains?(mermaid, "alice->>bob")
true

trace_to_plantuml(simulation)

Formats the trace as a PlantUML sequence diagram.

Example

simulation = ActorSimulation.new(trace: true)
  |> add_actor(:client, send_pattern: {:periodic, 100, :ping}, targets: [:server])
  |> add_actor(:server)
  |> run(duration: 200)

plantuml = ActorSimulation.trace_to_plantuml(simulation)
File.write!("sequence.puml", plantuml)