Quiescence Batching - Real Actors

Generated by GenServerVirtualTime โ€ข Mermaid Flowchart

๐Ÿ“ˆ Simulation Summary

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

๐Ÿ“Š Actor Topology

flowchart LR receiver["receiver
๐Ÿ“ค Sent: 1
๐Ÿ“ฅ Recv: 10"] sender["sender
๐Ÿ“ค Sent: 10
๐Ÿ“ฅ Recv: 1"] receiver -->|:batch| sender sender -->|:msg| receiver style receiver fill:#e8f5e9,stroke:#388e3c style sender fill:#e8f5e9,stroke:#388e3c

Node Shapes (Actor Type)

flowchart TD legend_processor("Processor
(send & receive)") style legend_processor fill:#ffffff,stroke:#666666,stroke-width:2px

Node Colors (Activity Level)

flowchart LR activity_0["๐ŸŸข Medium Activity (10-50 msgs)"] style activity_0 fill:#e8f5e9,stroke:#388e3c

๐Ÿ“‰ Detailed Statistics

Actor Sent Received Send Rate Receive Rate Activity
receiver 1 10 55.56 msg/s 555.56 msg/s 11 total
sender 10 1 555.56 msg/s 55.56 msg/s 11 total
Summary: Total messages: 22 โ€ข Duration: 18ms โ€ข 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
  )