Quiescence Batching - Real Actors

Generated by GenServerVirtualTime โ€ข Mermaid Flowchart

๐Ÿ“ˆ Simulation Summary

Virtual Time
0ms
Real Time
0ms
Speedup
โˆžx
Termination
โœ“ Quiescence

๐Ÿ“Š Actor Topology

flowchart LR receiver["receiver
๐Ÿ“ค Sent: 0
๐Ÿ“ฅ Recv: 0"] sender["sender
๐Ÿ“ค Sent: 0
๐Ÿ“ฅ Recv: 0"] receiver -->|:batch| sender sender -->|:msg| receiver style receiver fill:#f5f5f5,stroke:#9e9e9e style sender fill:#f5f5f5,stroke:#9e9e9e

Node Shapes (Actor Type)

flowchart TD legend_sink["Sink
(receives only)"] style legend_sink fill:#ffffff,stroke:#666666,stroke-width:2px

Node Colors (Activity Level)

flowchart LR activity_0["โšช Inactive (0 msgs)"] style activity_0 fill:#f5f5f5,stroke:#9e9e9e

๐Ÿ“‰ Detailed Statistics

Actor Sent Received Send Rate Receive Rate Activity
receiver 0 0 0.0 msg/s 0.0 msg/s 0 total
sender 0 0 0.0 msg/s 0.0 msg/s 0 total
Summary: Total messages: 0 โ€ข Duration: 0ms โ€ข Actors: 2

๐Ÿ’ป Model Source Code

This is the Elixir code that defines the actor simulation model:

# Quiescence example: sender emits 10 messages, receiver batches and replies once
# Termination: :quiescence (no timers left), with long timeout (60 seconds)
# Minimal delays (1ms each) to advance virtual time and show message flow

defmodule BatchReceiver do
  use VirtualTimeGenServer

  # args: {sender_name, expected_count}
  def init({sender, expected}), do: {:ok, %{sender: sender, expected: expected, received: []}}

  def handle_cast({:msg, i}, state) do
    new_received = [i | state.received]
    if length(new_received) == state.expected do
      VirtualTimeGenServer.cast(state.sender, {:batch, Enum.reverse(new_received)})
      {:noreply, %{state | received: []}}
    else
      {:noreply, %{state | received: new_received}}
    end
  end
end

defmodule BatchSender do
  use VirtualTimeGenServer
  def init(receiver), do: {:ok, %{receiver: receiver, batch: nil}}

  def handle_info(:start, state) do
    # Schedule messages with tiny delays (1ms each) to advance virtual time
    Enum.each(1..10, fn i ->
      VirtualTimeGenServer.send_after(self(), {:send_msg, i}, i)
    end)
    {:noreply, state}
  end

  def handle_info({:send_msg, i}, state) do
    VirtualTimeGenServer.cast(state.receiver, {:msg, i})
    {:noreply, state}
  end

  def handle_cast({:batch, list}, state), do: {:noreply, %{state | batch: list}}
end

simulation =
  ActorSimulation.new(trace: true)
  |> ActorSimulation.add_process(:receiver, module: BatchReceiver, args: {:sender, 10})
  |> ActorSimulation.add_process(:sender, module: BatchSender, args: :receiver)

# Kick off sending with minimal delay to advance virtual time
VirtualTimeGenServer.send_after(simulation.actors[:sender].pid, :start, 1)

# Run until quiescence (no timers left), up to 60s max virtual time
simulation =
  ActorSimulation.run(simulation,
    max_duration: 60_000,
    terminate_when: :quiescence
  )