Current section

Files

Jump to
ex_esdb lib ex_esdb store_system.ex
Raw

lib/ex_esdb/store_system.ex

defmodule ExESDB.StoreSystem do
@moduledoc """
Supervisor for store-related components.
This supervisor manages the store lifecycle and clustering components.
Uses :rest_for_one strategy to ensure proper startup order.
Startup order (critical for distributed coordination):
1. Store: Core store GenServer - must be fully operational first
2. StoreCluster: Clustering coordination - depends on Store being ready
3. StoreRegistry: Distributed store registry - starts after Store system is stable
This order ensures StoreRegistry only announces a store that is actually ready
to handle requests, preventing race conditions in distributed environments.
"""
use Supervisor
require Logger
alias ExESDB.Themes, as: Themes
alias ExESDB.StoreNaming
@impl true
def init(opts) do
Logger.info("[STORE_SYSTEM] Initializing StoreSystem supervisor with opts: #{inspect(opts)}")
Logger.info("[STORE_SYSTEM] StoreSystem PID: #{inspect(self())}, Node: #{inspect(node())}")
# Extract store_id for logging
store_id = StoreNaming.extract_store_id(opts)
Logger.info("[STORE_SYSTEM] Store ID: #{inspect(store_id)}")
Logger.info("[STORE_SYSTEM] Starting store components in critical dependency order...")
children = [
# Store must start first as other components depend on it
{ExESDB.Store, opts},
# StoreCluster handles clustering coordination and depends on Store
{ExESDB.StoreCluster, opts},
# StoreRegistry comes last - only starts after Store system is fully operational
{ExESDB.StoreRegistry, opts}
]
Logger.info("[STORE_SYSTEM] Configured #{length(children)} store subsystems:")
children |> Enum.with_index(1) |> Enum.each(fn {{module, _opts}, index} ->
Logger.info("[STORE_SYSTEM] #{index}. #{inspect(module)} - #{get_component_description(module)}")
end)
Logger.info("[STORE_SYSTEM] Using :rest_for_one strategy - each component depends on previous ones")
Logger.info("[STORE_SYSTEM] Max restarts: 5, Max seconds: 30")
IO.puts("#{Themes.system(self(), "StoreSystem is UP")}")
# Use :rest_for_one because each component depends on the previous ones
# StoreCluster depends on Store, StoreRegistry depends on both
result = Supervisor.init(children,
strategy: :rest_for_one,
max_restarts: 5,
max_seconds: 30
)
Logger.info("[STORE_SYSTEM] StoreSystem supervisor initialization complete")
result
end
defp get_component_description(ExESDB.Store), do: "Core Khepri store GenServer"
defp get_component_description(ExESDB.StoreCluster), do: "Clustering coordination and leadership"
defp get_component_description(ExESDB.StoreRegistry), do: "Distributed store registry and discovery"
defp get_component_description(_), do: "Store component"
def start_link(opts) do
store_id = StoreNaming.extract_store_id(opts)
name = StoreNaming.genserver_name(__MODULE__, store_id)
Supervisor.start_link(__MODULE__, opts, name: name)
end
def child_spec(opts) do
store_id = StoreNaming.extract_store_id(opts)
%{
id: StoreNaming.child_spec_id(__MODULE__, store_id),
start: {__MODULE__, :start_link, [opts]},
restart: :permanent,
shutdown: :infinity,
type: :supervisor
}
end
end