Current section
Files
Jump to
Current section
Files
lib/phoenix/session_process/error.ex
defmodule Phoenix.SessionProcess.Error do
@moduledoc """
Comprehensive error handling for Phoenix.SessionProcess operations.
This module provides structured error types, helper functions for creating errors,
and utilities for converting errors to human-readable messages. All public functions
in the Phoenix.SessionProcess library return standardized error tuples that can be
handled using this module.
## Error Types
The library returns errors in the format `{:error, error_details}` where `error_details`
follows these patterns:
### Session Management Errors
- `{:invalid_session_id, session_id}` - Session ID format is invalid
- `{:session_limit_reached, max_sessions}` - Maximum concurrent sessions exceeded
- `{:session_not_found, session_id}` - Session doesn't exist or has expired
### Process Communication Errors
- `{:process_not_found, session_id}` - Session process not found in registry
- `{:timeout, timeout}` - Operation timed out
- `{:call_failed, {module, function, args, reason}}` - GenServer call failed
- `{:cast_failed, {module, function, args, reason}}` - GenServer cast failed
## Usage Examples
### Handling Errors
Use pattern matching to handle different error types returned by Phoenix.SessionProcess functions:
case Phoenix.SessionProcess.start("session_123") do
{:ok, _pid} ->
# Success case
{:error, {:invalid_session_id, _id}} ->
# Handle invalid session ID
{:error, :invalid_session}
{:error, {:session_limit_reached, _max}} ->
# Handle session limit exceeded
{:error, :too_many_sessions}
{:error, _reason} ->
# Handle other errors
{:error, :session_failed}
end
### Creating Custom Errors
Use the helper functions to create standardized errors:
Phoenix.SessionProcess.Error.invalid_session_id("invalid@id")
Phoenix.SessionProcess.Error.session_limit_reached(1000)
### Converting to Human-Readable Messages
Use the `message/1` function to convert errors to human-readable strings:
error = Phoenix.SessionProcess.Error.invalid_session_id("invalid@id")
Phoenix.SessionProcess.Error.message(error)
# Returns: "Invalid session ID format: \"invalid@id\""
## Error Recovery Strategies
### Session Expiration
When `{:session_not_found, session_id}` is encountered, it typically means the
session has expired due to TTL. The appropriate response is to redirect the user
to login or create a new session.
### Rate Limiting
When `{:session_limit_reached, max_sessions}` occurs, implement backoff strategies
and consider increasing the `:max_sessions` configuration if appropriate.
### Timeout Handling
Timeout errors may indicate system overload. Consider implementing retry logic
with exponential backoff for transient failures.
"""
@typedoc """
Custom error types returned by Phoenix.SessionProcess functions.
"""
@type t ::
{:error,
{:invalid_session_id, String.t()}
| {:session_limit_reached, non_neg_integer()}
| {:session_not_found, String.t()}
| {:process_not_found, String.t()}
| {:timeout, timeout()}
| {:call_failed, {module(), atom(), any(), any()}}
| {:cast_failed, {module(), atom(), any(), any()}}}
@doc """
Creates an invalid session ID error.
## Examples
iex> Phoenix.SessionProcess.Error.invalid_session_id("invalid@id")
{:error, {:invalid_session_id, "invalid@id"}}
"""
@spec invalid_session_id(String.t()) :: {:error, {:invalid_session_id, String.t()}}
def invalid_session_id(session_id) do
{:error, {:invalid_session_id, session_id}}
end
@doc """
Creates a session limit reached error.
"""
@spec session_limit_reached(non_neg_integer()) ::
{:error, {:session_limit_reached, non_neg_integer()}}
def session_limit_reached(max_sessions) do
{:error, {:session_limit_reached, max_sessions}}
end
@doc """
Creates a session not found error.
"""
@spec session_not_found(String.t()) :: {:error, {:session_not_found, String.t()}}
def session_not_found(session_id) do
{:error, {:session_not_found, session_id}}
end
@doc """
Creates a process not found error.
"""
@spec process_not_found(String.t()) :: {:error, {:process_not_found, String.t()}}
def process_not_found(session_id) do
{:error, {:process_not_found, session_id}}
end
@doc """
Creates a timeout error.
"""
@spec timeout(timeout()) :: {:error, {:timeout, timeout()}}
def timeout(timeout) do
{:error, {:timeout, timeout}}
end
@doc """
Creates a call failed error.
"""
@spec call_failed(module(), atom(), any(), any()) :: {:error, {:call_failed, tuple()}}
def call_failed(module, function, args, reason) do
{:error, {:call_failed, {module, function, args, reason}}}
end
@doc """
Creates a cast failed error.
"""
@spec cast_failed(module(), atom(), any(), any()) :: {:error, {:cast_failed, tuple()}}
def cast_failed(module, function, args, reason) do
{:error, {:cast_failed, {module, function, args, reason}}}
end
@doc """
Returns a human-readable error message for the given error.
"""
@spec message(t()) :: String.t()
def message({:error, {:invalid_session_id, session_id}}) do
"Invalid session ID format: #{inspect(session_id)}"
end
def message({:error, {:session_limit_reached, max_sessions}}) do
"Maximum concurrent sessions limit (#{max_sessions}) reached"
end
def message({:error, {:session_not_found, session_id}}) do
"Session not found: #{session_id}"
end
def message({:error, {:process_not_found, session_id}}) do
"Process not found for session: #{session_id}"
end
def message({:error, {:timeout, timeout}}) do
"Operation timed out after #{timeout}ms"
end
def message({:error, {:call_failed, {module, function, args, reason}}}) do
"Call failed: #{inspect(module)}.#{function}/#{tuple_size(args) + 1} with reason: #{inspect(reason)}"
end
def message({:error, {:cast_failed, {module, function, args, reason}}}) do
"Cast failed: #{inspect(module)}.#{function}/#{tuple_size(args) + 1} with reason: #{inspect(reason)}"
end
@doc """
Converts a generic error to a SessionProcess error when possible.
"""
@spec normalize_error(any()) :: {:error, any()}
def normalize_error({:error, _} = error), do: error
def normalize_error(:not_found), do: {:error, {:session_not_found, "unknown"}}
def normalize_error(:timeout), do: {:error, {:timeout, 5000}}
def normalize_error(error), do: {:error, error}
end