···337337 end
338338 end
339339340340+ @spec start_node(atom, module, term) :: :ok
341341+ def start_node(name, app_module, args) when is_atom(name) and is_atom(app_module) do
342342+ if not simulated?(), do: raise "start_node/3 can only be called in simulation"
343343+ scheduler_pid = fetch_scheduler_pid!()
344344+345345+ Scheduler.start_node(scheduler_pid, name, app_module, args)
346346+ end
347347+340348 @spec monitor(pid) :: reference
341349 def monitor(monitor_pid) do
342350 case get_scheduler_pid() do
+18
lib/construct/sim_supervisor.ex
···11+defmodule Hobbes.Construct.SimSupervisor do
22+ use Hobbes.Construct.SimServer
33+44+ def start_link(children, opts) do
55+ SimServer.start_link(__MODULE__, {children, opts})
66+ end
77+88+ def init({children, _opts}) do
99+ # TODO: trap exits?
1010+ Enum.map(children, &spawn_child/1)
1111+ {:ok, nil}
1212+ end
1313+1414+ defp spawn_child({module, arg} = _child_spec) do
1515+ %{start: {m, f, a}} = module.child_spec(arg)
1616+ apply(m, f, a)
1717+ end
1818+end
+70-17
lib/hobbes.ex
···11defmodule Hobbes do
22- alias Hobbes.Servers.{Coordinator, ServerSupervisor, Manager}
22+ alias Hobbes.Construct.SimServer
33+ alias Hobbes.ClusterNode
44+ alias Hobbes.Servers.{Coordinator, Manager}
35 alias Hobbes.Structs.Cluster
4677+ defmodule AppShim do
88+ use Application
99+1010+ alias Hobbes.Construct.SimSupervisor
1111+1212+ def start(_type, [config]) do
1313+ children = [
1414+ {Hobbes.ClusterNode, config}
1515+ ]
1616+ SimSupervisor.start_link(children, max_restarts: 0)
1717+ end
1818+ end
1919+2020+ # Sim-only, relies on simulated nodes
2121+ defp init_distributed_cluster(num_coordinators) do
2222+ coordinators =
2323+ Enum.map(0..(num_coordinators - 1), fn i ->
2424+ {String.to_atom("node#{i}"), String.to_atom("coordinator-#{i}")}
2525+ end)
2626+2727+ coordinators
2828+ |> Enum.with_index()
2929+ |> Enum.each(fn {{node, _name}, i} ->
3030+ config = [
3131+ cluster: "cluster",
3232+ coordinators: coordinators,
3333+ coordinator_id: i,
3434+ slots: [
3535+ ],
3636+ ]
3737+3838+ SimServer.start_node(node, Hobbes.AppShim, [config])
3939+ end)
4040+4141+ coordinators
4242+ end
4343+4444+ defp init_local_cluster(num_coordinators) do
4545+ coordinators =
4646+ Enum.map(0..(num_coordinators - 1), fn i ->
4747+ String.to_atom("coordinator-#{i}")
4848+ end)
4949+5050+ coordinators
5151+ |> Enum.with_index()
5252+ |> Enum.each(fn {_name, i} ->
5353+ config = [
5454+ cluster: "cluster",
5555+ coordinators: coordinators,
5656+ coordinator_id: i,
5757+ slots: [
5858+ ],
5959+ ]
6060+6161+ ClusterNode.start_link(config)
6262+ end)
6363+6464+ coordinators
6565+ end
6666+567 defp default_opts do
668 [
769 num_coordinators: 3,
···3496 def start_cluster(opts) do
3597 num_coordinators = Keyword.get(opts, :num_coordinators, 3)
36983737- coordinator_names =
3838- Enum.map(0..(num_coordinators - 1), fn i ->
3939- {i, String.to_atom("coordinator-#{i}")}
4040- end)
4141-4299 coordinators =
4343- Enum.map(coordinator_names, fn {id, name} ->
4444- {:ok, pid} = Coordinator.start_link(id, coordinator_names, name: name)
4545- %{id: id, name: name, pid: pid}
4646- end)
100100+ case SimServer.simulated?() do
101101+ true -> init_distributed_cluster(num_coordinators)
102102+ false -> init_local_cluster(num_coordinators)
103103+ end
471044848- coordinator_pids = Enum.map(coordinators, fn %{pid: pid} -> pid end)
105105+ SimServer.sleep(1000)
4910650107 config = config_pairs(opts)
5151- Coordinator.write(hd(coordinator_pids), config)
5252-5353- for _i <- 1..Keyword.get(opts, :num_supervisors, 3) do
5454- {:ok, _pid} = ServerSupervisor.start_link(coordinators: coordinator_pids)
5555- end
108108+ {:ok, :ok} = Coordinator.write(hd(coordinators), config)
5610957110 {
58111 :ok,
5959- coordinator_pids,
112112+ coordinators,
60113 }
61114 end
62115