defmodule Phoenix.SessionProcess.Helpers do @moduledoc """ Helper functions for common session management tasks. This module provides convenient functions for batch operations, session health checks, and common patterns. """ alias Phoenix.SessionProcess @doc """ Start sessions for multiple session IDs in parallel. ## Examples iex> Phoenix.SessionProcess.Helpers.start_sessions(["session1", "session2"]) [{"session1", {:ok, #PID<0.123.0>}}, {"session2", {:ok, #PID<0.124.0>}}] """ @spec start_sessions([binary()]) :: [{binary(), {:ok, pid()} | {:error, term()}}] def start_sessions(session_ids) when is_list(session_ids) do session_ids |> Enum.map(fn session_id -> Task.async(fn -> {session_id, SessionProcess.start(session_id)} end) end) |> Task.await_many(5_000) end @doc """ Terminate multiple sessions in parallel. ## Examples iex> Phoenix.SessionProcess.Helpers.terminate_sessions(["session1", "session2"]) [{"session1", :ok}, {"session2", {:error, :not_found}}] """ @spec terminate_sessions([binary()]) :: [{binary(), :ok | {:error, term()}}] def terminate_sessions(session_ids) when is_list(session_ids) do session_ids |> Enum.map(fn session_id -> Task.async(fn -> {session_id, SessionProcess.terminate(session_id)} end) end) |> Task.await_many(5_000) end @doc """ Broadcast a message to all active sessions. ## Examples iex> Phoenix.SessionProcess.Helpers.broadcast_all({:system_message, "Maintenance in 5 minutes"}) :ok """ @spec broadcast_all(any()) :: :ok def broadcast_all(message) do SessionProcess.list_session() |> Enum.each(fn {session_id, _pid} -> SessionProcess.cast(session_id, message) end) :ok end @doc """ Get health status for all sessions. ## Examples iex> Phoenix.SessionProcess.Helpers.session_health() %{healthy: 10, crashed: 0, total: 10} """ @spec session_health() :: %{healthy: integer(), crashed: integer(), total: integer()} def session_health() do sessions = SessionProcess.list_session() {healthy, crashed} = sessions |> Enum.reduce({0, 0}, fn {_session_id, pid}, {h, c} -> case Process.alive?(pid) do true -> {h + 1, c} false -> {h, c + 1} end end) %{ healthy: healthy, crashed: crashed, total: length(sessions) } end @doc """ Find sessions by pattern matching on session ID. ## Examples iex> Phoenix.SessionProcess.Helpers.find_sessions_by_pattern(~r/user_.*/) ["user_123", "user_456"] """ @spec find_sessions_by_pattern(Regex.t()) :: [binary()] def find_sessions_by_pattern(pattern) when is_struct(pattern, Regex) do SessionProcess.list_session() |> Enum.map(fn {session_id, _pid} -> session_id end) |> Enum.filter(fn session_id -> String.match?(session_id, pattern) end) end @doc """ Safely call a session with automatic retry on timeout. ## Examples iex> Phoenix.SessionProcess.Helpers.safe_call("session_123", :get_user, 3) {:ok, %User{}} """ @spec safe_call(binary(), any(), integer(), non_neg_integer()) :: {:ok, any()} | {:error, term()} def safe_call(session_id, request, retries \\ 3, timeout \\ 5_000) do do_safe_call(session_id, request, retries, timeout) end defp do_safe_call(_session_id, _request, 0, _timeout) do {:error, :max_retries_exceeded} end defp do_safe_call(session_id, request, retries, timeout) do case SessionProcess.call(session_id, request, timeout) do {:ok, result} -> {:ok, result} {:error, {:timeout, _}} -> Process.sleep(100) do_safe_call(session_id, request, retries - 1, timeout) {:error, reason} -> {:error, reason} end end @doc """ Create a session with automatic retry on rate limit. ## Examples iex> Phoenix.SessionProcess.Helpers.create_session_with_retry("session_123") {:ok, #PID<0.123.0>} """ @spec create_session_with_retry(binary(), module() | nil, any() | nil, integer()) :: {:ok, pid()} | {:error, term()} def create_session_with_retry(session_id, module \\ nil, arg \\ nil, retries \\ 5) do do_create_session_with_retry(session_id, module, arg, retries) end defp do_create_session_with_retry(_session_id, _module, _arg, 0) do {:error, :max_retries_exceeded} end defp do_create_session_with_retry(session_id, module, arg, retries) do result = if module do if arg do SessionProcess.start(session_id, module, arg) else SessionProcess.start(session_id, module) end else SessionProcess.start(session_id) end case result do {:ok, pid} -> {:ok, pid} {:error, {:session_limit_reached, _max}} -> Process.sleep(200) do_create_session_with_retry(session_id, module, arg, retries - 1) {:error, reason} -> {:error, reason} end end end