### Starting Phoenix Sessions Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Examples of how to start a new session using Phoenix.SessionProcess. It shows starting with a default module, a custom module, and with custom arguments. ```elixir # Start with default module {:ok, pid} = Phoenix.SessionProcess.start_session("session_123") # Start with custom module {:ok, pid} = Phoenix.SessionProcess.start_session("session_123", module: MyApp.CustomProcess) # Start with custom module and arguments {:ok, pid} = Phoenix.SessionProcess.start_session("session_123", module: MyApp.CustomProcess, args: %{user_id: 456}) # Start with default module but custom arguments {:ok, pid} = Phoenix.SessionProcess.start_session("session_123", args: %{debug: true}) ``` -------------------------------- ### Start Session on User Login Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Initiates a user session after successful authentication. This example shows how to start the session process and dispatch initial user data to the session state upon successful login. ```elixir defmodule MyAppWeb.AuthController do use MyAppWeb, :controller alias Phoenix.SessionProcess def login(conn, %{"user" => user_params}) do with {:ok, user} <- MyApp.Accounts.authenticate(user_params), session_id <- get_session(conn, :session_id), {:ok, _pid} <- SessionProcess.start_session(session_id) do # Initialize user state :ok = SessionProcess.dispatch(session_id, "user.set", user) conn |> put_flash(:info, "Logged in successfully") |> redirect(to: "/dashboard") else {:error, _} -> conn |> put_flash(:error, "Invalid credentials") |> render("login.html") end end end ``` -------------------------------- ### Install Dependencies with Mix Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This command fetches and installs all the project's dependencies as defined in the `mix.exs` file. It is a standard step in setting up the development environment. ```bash mix deps.get ``` -------------------------------- ### Phoenix Session Process: Login Integration Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Shows how to initiate a session process as part of a user login flow. This example demonstrates authenticating a user and then starting a session, dispatching user data to it upon successful login. ```elixir def login(conn, %{"user" => params}) do with {:ok, user} <- authenticate(params), session_id <- get_session(conn, :session_id), {:ok, _pid} <- SessionProcess.start_session(session_id) do :ok = SessionProcess.dispatch(session_id, "user.set", user) redirect(conn, to: "/dashboard") end end ``` -------------------------------- ### Initial Setup - Configuration Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Details on configuring `phoenix_session_process` in your application's configuration files. ```APIDOC ## Initial Setup - Configuration ### Description This section outlines the configuration options available for `phoenix_session_process`, which should be set in your `config/config.exs` file. Options include `session_process`, `max_sessions`, `session_ttl`, `rate_limit`, and `unmatched_action_handler`. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A **Configure (config/config.exs):** ```elixir config :phoenix_session_process, session_process: MyApp.SessionProcess, max_sessions: 10_000, session_ttl: 3_600_000, # 1 hour in milliseconds rate_limit: 100, unmatched_action_handler: :log # :log | :warn | :silent | function ``` ``` -------------------------------- ### Initial Setup - Dependencies and Supervision Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Instructions for adding `phoenix_session_process` to your project's dependencies and supervision tree. ```APIDOC ## Initial Setup - Dependencies and Supervision ### Description This section details the steps required to integrate `phoenix_session_process` into a Phoenix application, including adding the dependency to `mix.exs` and including it in the application's supervision tree. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A **Add to mix.exs:** ```elixir def deps do [ {:phoenix_session_process, "~> 1.0"} ] end ``` **Add to supervision tree (lib/my_app/application.ex):** ```elixir def start(_type, _args) do children = [ # ... other children {Phoenix.SessionProcess, []}, ] Supervisor.start_link(children, strategy: :one_for_one) end ``` ``` -------------------------------- ### Reducer Usage Example Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Example of how to compose and reuse defined reducers in different session types. ```APIDOC ## Reducer Usage Example ### Description This example shows how a defined reducer (e.g., `MyApp.CartReducer`) can be reused across different session types like `MyApp.UserSession` and `MyApp.GuestSession`. ### Method N/A (Code Definition) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ```elixir defmodule MyApp.CartReducer do use Phoenix.SessionProcess, :reducer @name :cart # ... cart logic end defmodule MyApp.UserSession do def combined_reducers, do: [MyApp.CartReducer, MyApp.UserReducer] end defmodule MyApp.GuestSession do def combined_reducers, do: [MyApp.CartReducer] # Reuse CartReducer end ``` ``` -------------------------------- ### Initial Setup - Router Plug Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Instructions for adding the `Phoenix.SessionProcess.SessionId` plug to your Phoenix router pipeline. ```APIDOC ## Initial Setup - Router Plug ### Description This guide explains how to add the `Phoenix.SessionProcess.SessionId` plug to your Phoenix router's pipeline. It's crucial that this plug is placed after `:fetch_session`. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A **Add plug to router (lib/my_app_web/router.ex):** ```elixir pipeline :browser do plug :fetch_session plug Phoenix.SessionProcess.SessionId # MUST be after :fetch_session # ... other plugs end ``` ``` -------------------------------- ### Phoenix Session Process Telemetry Setup Example Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This Elixir code demonstrates how to attach telemetry handlers to monitor Phoenix.SessionProcess events. It shows how to log session lifecycle events and how to monitor session call durations to identify performance bottlenecks. ```elixir # Attach telemetry handlers :telemetry.attach_many("session-handler", [ [:phoenix, :session_process, :start], [:phoenix, :session_process, :stop] ], fn event, measurements, meta, _ -> Logger.info("Session event: #{inspect(event)} #{inspect(meta)}") end, nil) # Monitor session performance :telemetry.attach("session-performance", [:phoenix, :session_process, :call], fn _event, %{duration: duration}, %{session_id: session_id}, _metadata -> if duration > 1_000_000 do # > 1ms Logger.warn("Slow session call for #{session_id}: #{duration}µs") end end, nil) ``` -------------------------------- ### Installation and Configuration Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Instructions for adding Phoenix SessionProcess to your project dependencies and configuring its runtime options. ```APIDOC ## Installation Add to your `mix.exs` dependencies and configure in your application. ```elixir # mix.exs def deps do [ {:phoenix_session_process, "~> 1.0"} ] end # config/config.exs config :phoenix_session_process, session_process: MyApp.SessionProcess, # Default session module max_sessions: 10_000, # Maximum concurrent sessions session_ttl: 3_600_000, # Session TTL in milliseconds (1 hour) rate_limit: 100, # Sessions per minute limit unmatched_action_handler: :log # How to handle unmatched actions ``` ## Adding to Supervision Tree Add the Phoenix.SessionProcess supervisor to your application's supervision tree. ```elixir # lib/my_app/application.ex defmodule MyApp.Application do use Application def start(_type, _args) do children = [ MyAppWeb.Endpoint, # Add Phoenix.SessionProcess to supervision tree {Phoenix.SessionProcess, []} # Or with runtime options: # {Phoenix.SessionProcess, [ # max_sessions: 20_000, # session_ttl: :timer.hours(2), # rate_limit: 150 # ]} ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end ``` ``` -------------------------------- ### Integrate and Run Benchmarks in Elixir Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/bench/README.md Demonstrates how to start the supervisor and execute session benchmarks within an Elixir test environment. ```elixir Application.put_env(:my_app, :session_process, MyApp.SessionProcess) {:ok, _} = Phoenix.SessionProcess.Supervisor.start_link([]) # Run benchmarks bench_results = BenchHelper.run_session_benchmark() ``` -------------------------------- ### Configure Phoenix.SessionProcess Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Covers the installation steps including adding the dependency to mix.exs, registering the process in the supervision tree, and configuring the router and application settings. ```elixir # mix.exs def deps do [{:phoenix_session_process, "~> 1.0"}] end # lib/my_app/application.ex children = [{Phoenix.SessionProcess, []}] # lib/my_app_web/router.ex pipeline :browser do plug :fetch_session plug Phoenix.SessionProcess.SessionId end # config/config.exs config :phoenix_session_process, session_process: MyApp.SessionProcess, max_sessions: 10_000, session_ttl: 3_600_000 ``` -------------------------------- ### Phoenix Session Process: Controller Integration Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Demonstrates how to use the Phoenix Session Process within a controller to start sessions, dispatch actions, and retrieve state. It highlights the use of binary types for dispatching and provides examples for synchronous and asynchronous operations. ```elixir defmodule MyAppWeb.PageController do use MyAppWeb, :controller alias Phoenix.SessionProcess def index(conn, _params) do session_id = get_session(conn, :session_id) # Start session {:ok, _pid} = SessionProcess.start_session(session_id) # Dispatch actions (MUST use binary types) :ok = SessionProcess.dispatch(session_id, "counter.increment") :ok = SessionProcess.dispatch(session_id, "user.set", %{id: 1, name: "Alice"}) # Async dispatch (convenience - automatically adds async: true) :ok = SessionProcess.dispatch_async(session_id, "user.fetch", 123) # Equivalent to: # :ok = SessionProcess.dispatch(session_id, "user.fetch", 123, async: true) # Get state state = SessionProcess.get_state(session_id) # => %{counter: %{count: 1}, user: %{current_user: %{id: 1, name: "Alice"}}} render(conn, "index.html", state: state) end end ``` -------------------------------- ### Reducer Definition Example Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Example of how to define a reusable reducer for managing counter state. ```APIDOC ## Reducer Definition Example ### Description This example demonstrates how to define a reusable reducer using `Phoenix.SessionProcess, :reducer`. It includes state initialization, handling synchronous actions, and an example of handling asynchronous actions. ### Method N/A (Code Definition) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ```elixir defmodule MyApp.CounterReducer do use Phoenix.SessionProcess, :reducer @name :counter @action_prefix "counter" @impl true def init_state do %{count: 0} end @impl true def handle_action(action, state) do alias Phoenix.SessionProcess.Action case action do %Action{type: "increment"} -> %{state | count: state.count + 1} %Action{type: "decrement"} -> %{state | count: state.count - 1} %Action{type: "set", payload: value} -> %{state | count: value} _ -> handle_unmatched_action(action, state) end end @impl true def handle_async(action, dispatch, state) do alias Phoenix.SessionProcess.Action case action do %Action{type: "fetch_remote", payload: url} -> task = Task.async(fn -> value = HTTPClient.get(url) dispatch.("set", value) end) fn -> Task.shutdown(task, :brutal_kill) end _ -> handle_unmatched_async(action, dispatch, state) end end end ``` ``` -------------------------------- ### Phoenix Session Process Error Handling Example Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This Elixir code provides an example of handling errors when starting a session with Phoenix.SessionProcess. It demonstrates how to use a case statement to match against various error tuples, such as invalid session IDs or session limits being reached, and log appropriate messages. ```elixir case Phoenix.SessionProcess.start_session(session_id) do {:ok, pid} -> # Session started successfully {:ok, pid} {:error, {:invalid_session_id, id}} -> Logger.error("Invalid session ID: #{id}") {:error, :invalid_session} {:error, {:session_limit_reached, max}} -> Logger.warn("Session limit reached: #{max}") {:error, :too_many_sessions} {:error, reason} -> Logger.error("Failed to start session: #{inspect(reason)}") {:error, :session_start_failed} end ``` -------------------------------- ### Define a Basic Session Process Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Provides an example of creating a custom session process module to handle state initialization and message passing. ```elixir defmodule MyApp.SessionProcess do use Phoenix.SessionProcess, :process @impl true def init(_init_arg) do {:ok, %{user_id: nil, preferences: %{}}} end @impl true def handle_call(:get_user, _from, state) do {:reply, state.user_id, state} end @impl true def handle_cast({:set_user, user_id}, state) do {:noreply, %{state | user_id: user_id}} end end ``` -------------------------------- ### Dispatch Actions to Session Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Examples of how to dispatch actions to a session process, including standard and asynchronous dispatch calls. ```elixir dispatch(session_id, "increment") dispatch(session_id, "set_user", %{id: 1}) dispatch(session_id, "fetch", nil, async: true) ``` -------------------------------- ### Dispatching Actions to SessionProcess Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Demonstrates how to dispatch synchronous and asynchronous actions to a session process. Includes examples of passing payloads, metadata, and targeting specific reducers. ```elixir :ok = SessionProcess.dispatch(session_id, "cart.add_item", %{id: "prod_001", name: "Elixir Book", price: 4999}) :ok = SessionProcess.dispatch(session_id, "user.login", %{id: 123, name: "Alice", email: "alice@example.com"}, priority: :high) :ok = SessionProcess.dispatch_async(session_id, "data.fetch", %{page: 1}) :ok = SessionProcess.dispatch(session_id, "reload", nil, reducers: [:user, :cart]) state = SessionProcess.get_state(session_id) ``` -------------------------------- ### Dispatching Actions Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Illustrates how to start a session and dispatch actions to it using the SessionProcess API. Actions are identified by a binary string prefix matching the reducer's configuration. ```elixir alias Phoenix.SessionProcess session_id = "user_abc123" {:ok, _} = SessionProcess.start_session(session_id, module: MyApp.UserSessionProcess) :ok = SessionProcess.dispatch(session_id, "counter.increment") ``` -------------------------------- ### Run Project Tests with Mix Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This command executes the comprehensive test suite included with the library using the Mix build tool. Ensure you have the project dependencies installed before running. ```bash mix test ``` -------------------------------- ### Initializing Sessions in Request Lifecycle Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Best practices for starting sessions within router plugs or login controllers, avoiding initialization inside LiveView mount. ```elixir pipeline :browser do plug :fetch_session plug Phoenix.SessionProcess.SessionId plug :ensure_session_process end def login(conn, params) do with {:ok, user} <- authenticate(params), session_id <- get_session(conn, :session_id), {:ok, _pid} <- SessionProcess.start_session(session_id) do end end ``` -------------------------------- ### Start Sessions in Router Hook Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Configures the router pipeline to ensure a session process is started for each incoming request. It uses a custom plug `ensure_session_process` to handle session startup, including error handling for cases where the session cannot be started. ```elixir # lib/my_app_web/router.ex defmodule MyAppWeb.Router do use MyAppWeb, :router alias Phoenix.SessionProcess pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug Phoenix.SessionProcess.SessionId plug :ensure_session_process # ... other plugs end defp ensure_session_process(conn, _opts) do session_id = get_session(conn, :session_id) case SessionProcess.start_session(session_id) do {:ok, _pid} -> conn {:error, {:already_started, _pid}} -> conn {:error, reason} -> conn |> put_flash(:error, "Session unavailable") |> redirect(to: "/error") |> halt() end end end ``` -------------------------------- ### Router Hook for Session Initialization Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Sets up a router pipeline to ensure a Phoenix.SessionProcess is started or verified for each incoming connection, handling session initialization and potential errors. ```APIDOC ## Router Hook for Session Initialization ### Description Start session processes in a router plug for consistent session management. This ensures that a session process is either started or its Time-To-Live (TTL) is refreshed upon each request. ### Method N/A (Router Pipeline Plug) ### Endpoint N/A (Applies to routes using the `:browser` pipeline) ### Parameters N/A ### Request Example N/A ### Response N/A ### Code Example (Elixir) ```elixir # lib/my_app_web/router.ex defmodule MyAppWeb.Router do use MyAppWeb, :router alias Phoenix.SessionProcess pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug Phoenix.SessionProcess.SessionId plug :ensure_session_process # Start session process here plug :fetch_live_flash plug :put_root_layout, {MyAppWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers end # Start or verify session process exists defp ensure_session_process(conn, _opts) do session_id = get_session(conn, :session_id) case SessionProcess.start_session(session_id, module: MyApp.UserSessionProcess) do {:ok, _pid} -> conn {:error, {:already_started, _pid}} -> SessionProcess.touch(session_id) conn {:error, {:session_limit_reached, max}} -> conn |> put_flash(:error, "Service busy, please try again later") |> redirect(to: "/maintenance") |> halt() {:error, reason} -> conn |> put_flash(:error, "Session error: #{inspect(reason)}") |> redirect(to: "/error") |> halt() end end scope "/", MyAppWeb do pipe_through :browser get "/", PageController, :index live "/dashboard", DashboardLive live "/cart", CartLive end end ``` ``` -------------------------------- ### Managing Session Lifecycle Errors Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Demonstrates robust error handling when starting sessions, including checking for existing sessions or service limits. ```elixir case SessionProcess.start_session(session_id) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> :ok {:error, {:session_limit_reached, max}} -> {:error, :service_busy} {:error, reason} -> {:error, :session_unavailable} end ``` -------------------------------- ### Initialize SessionProcess in Router Pipeline Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Shows how to implement a custom plug in the Phoenix router to ensure a session process is started or refreshed for every request. It handles session limits and error states gracefully. ```elixir defp ensure_session_process(conn, _opts) do session_id = get_session(conn, :session_id) case SessionProcess.start_session(session_id, module: MyApp.UserSessionProcess) do {:ok, _pid} -> conn {:error, {:already_started, _pid}} -> SessionProcess.touch(session_id) conn {:error, {:session_limit_reached, max}} -> conn |> put_flash(:error, "Service busy") |> redirect(to: "/maintenance") |> halt() {:error, reason} -> conn |> put_flash(:error, "Session error") |> redirect(to: "/error") |> halt() end end ``` -------------------------------- ### Manage Session Processes in Controllers Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Demonstrates how to start a session process and store user data within a Phoenix controller using the SessionProcess API. ```elixir defmodule MyAppWeb.PageController do use MyAppWeb, :controller def index(conn, _params) do session_id = get_session(conn, :session_id) # Start session process {:ok, _pid} = Phoenix.SessionProcess.start_session(session_id) # Store user data Phoenix.SessionProcess.cast(session_id, {:put, :user_id, conn.assigns.current_user.id}) Phoenix.SessionProcess.cast(session_id, {:put, :last_seen, DateTime.utc_now()}) render(conn, "index.html") end end ``` -------------------------------- ### Communicating with Phoenix Session Processes Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Provides examples for interacting with active session processes. This includes checking if a session exists, calling functions, sending casts, terminating sessions, and listing all active sessions. ```elixir # Check if session exists Phoenix.SessionProcess.started?("session_123") # Call the session process {:ok, user} = Phoenix.SessionProcess.call("session_123", :get_user) # Cast to the session process :ok = Phoenix.SessionProcess.cast("session_123", {:set_user, user}) # Terminate session :ok = Phoenix.SessionProcess.terminate("session_123") # Reset session TTL (extend session lifetime) :ok = Phoenix.SessionProcess.touch("session_123") # List all sessions sessions = Phoenix.SessionProcess.list_session() # Get memory and performance statistics stats = Phoenix.SessionProcess.session_stats() # Returns: %{total_memory: ..., session_count: ..., process_info: [...]} ``` -------------------------------- ### Use Session Process in Controllers Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Demonstrates how to use Phoenix.SessionProcess within a controller to start, dispatch actions to, and retrieve state from a user session. It covers synchronous and asynchronous dispatching, as well as state retrieval with and without selectors. ```elixir defmodule MyAppWeb.PageController do use MyAppWeb, :controller alias Phoenix.SessionProcess def index(conn, _params) do session_id = get_session(conn, :session_id) # Start session {:ok, _pid} = SessionProcess.start_session(session_id) # Dispatch actions (MUST use binary types) :ok = SessionProcess.dispatch(session_id, "counter.increment") :ok = SessionProcess.dispatch(session_id, "user.set", %{id: 1, name: "Alice"}) # Async dispatch (convenience) :ok = SessionProcess.dispatch_async(session_id, "counter.fetch_remote", "http://api.example.com/count") # Get state state = SessionProcess.get_state(session_id) # => %{counter: %{count: 1}, user: %{id: 1, name: "Alice"}} # Get state with selector (client-side) count = SessionProcess.get_state(session_id, fn s -> s.counter.count end) # Server-side selection (more efficient for large states) count = SessionProcess.select_state(session_id, fn s -> s.counter.count end) render(conn, "index.html", state: state, count: count) end end ``` -------------------------------- ### Get Human-Readable Error Messages in Elixir Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This snippet demonstrates how to use `Phoenix.SessionProcess.Error.message/1` to retrieve user-friendly error messages from session process errors. It shows an example of starting a session with invalid input and then converting the resulting error tuple into a readable string. ```elixir error_tuple = {:error, "some_error_code"} Phoenix.SessionProcess.Error.message(elem(error_tuple, 1)) ``` -------------------------------- ### Phoenix Session Process Redux Store API Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Examples of using the Redux Store API for state management within Phoenix.SessionProcess. Covers dispatching actions, getting state using selectors, and subscribing to state changes. ```elixir # Dispatch actions (MUST use binary types) :ok = Phoenix.SessionProcess.dispatch(session_id, "counter.increment") # Async dispatch (convenience alias) :ok = Phoenix.SessionProcess.dispatch_async( session_id, "user.set", %{id: 123} ) # Get current state after dispatch state = Phoenix.SessionProcess.get_state(session_id) # Get state with selector (client-side) user = Phoenix.SessionProcess.get_state(session_id, fn state -> state.user end) # Server-side selection (more efficient for large states) user = Phoenix.SessionProcess.select_state(session_id, fn state -> state.user end) # Subscribe to state changes (with selector) {:ok, sub_id} = Phoenix.SessionProcess.subscribe( session_id, fn state -> state.user end, # Only notified when user changes :user_changed, # Event name for messages self() # Subscriber PID ) # Receive notifications receive do {:user_changed, user} -> IO.inspect(user, label: "User changed") end # Unsubscribe :ok = Phoenix.SessionProcess.unsubscribe(session_id, sub_id) ``` -------------------------------- ### Run Quick Benchmark with Mix Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This command executes a quick benchmark for the library, typically taking 5-10 seconds to complete. It uses a specific benchmark script located in the `bench/` directory. ```bash mix run bench/simple_bench.exs ``` -------------------------------- ### Dispatching Actions and Handling Reducers Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Demonstrates how to dispatch actions with prefixes and implement the handle_action callback to process state updates. ```elixir dispatch(session_id, "user.set", user_data) dispatch(session_id, "user.update", changes) def handle_action(action, state) do case action do %Action{type: "set"} -> # Matches "user.set" %Action{type: "update"} -> # Matches "user.update" end end ``` -------------------------------- ### Session Management API Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Core functions for starting, interacting with, and querying session state. ```APIDOC ## SessionProcess API ### Description Provides methods to manage the lifecycle and state of user sessions within a Phoenix application. ### Methods - **start_session(session_id)**: Initializes a new session process. - **dispatch(session_id, action, payload)**: Synchronously updates session state via reducers. - **dispatch_async(session_id, action, payload)**: Asynchronously updates session state. - **get_state(session_id, selector?)**: Retrieves the current state, optionally filtered by a selector function. - **subscribe(session_id, selector, event_name, pid)**: Subscribes to state changes for a specific slice of data. ### Request Example SessionProcess.dispatch(session_id, "user.set", %{id: 1, name: "Alice"}) ### Response - **:ok** (atom) - Indicates successful operation. - **{:ok, pid}** (tuple) - Returned by start_session upon successful process creation. ``` -------------------------------- ### Session Management API Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Core functions for starting, checking, finding, and terminating session processes. ```APIDOC ## Starting and Managing Sessions Create, check, and terminate session processes using the main API. ```elixir alias Phoenix.SessionProcess # Start session with default module {:ok, pid} = SessionProcess.start_session("user_abc123") # Start session with custom module {:ok, pid} = SessionProcess.start_session("user_abc123", module: MyApp.SessionProcess) # Start session with custom module and initialization arguments {:ok, pid} = SessionProcess.start_session("user_abc123", module: MyApp.SessionProcess, args: %{user_id: 456, role: :admin} ) # Check if session exists true = SessionProcess.started?("user_abc123") false = SessionProcess.started?("nonexistent") # Find session and get its PID {:ok, pid} = SessionProcess.find_session("user_abc123") {:error, :not_found} = SessionProcess.find_session("nonexistent") # Extend session TTL (reset expiration timer) :ok = SessionProcess.touch("user_abc123") # Terminate session :ok = SessionProcess.terminate("user_abc123") {:error, :not_found} = SessionProcess.terminate("user_abc123") # List all active sessions sessions = SessionProcess.list_session() # => [{"user_abc123", #PID<0.123.0>}, {"user_xyz789", #PID<0.124.0>}] # Get session statistics stats = SessionProcess.session_stats() # => %{total_sessions: 2, memory_usage: 20480, avg_memory_per_session: 10240} # Get session info info = SessionProcess.session_info() # => %{count: 2, modules: [MyApp.SessionProcess]} ``` ``` -------------------------------- ### Run Comprehensive Benchmark with Mix Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This command executes a more comprehensive benchmark for the library, which may take 30-60 seconds. It utilizes a detailed benchmark script found in the `bench/` directory. ```bash mix run bench/session_benchmark.exs ``` -------------------------------- ### Session Management API Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Endpoints for controlling the lifecycle of a session, including starting, terminating, and monitoring session statistics. ```APIDOC ## POST /session/start ### Description Initializes a new session process with an optional custom module and arguments. ### Method POST ### Parameters #### Request Body - **session_id** (string) - Required - Unique identifier for the session. - **module** (atom) - Optional - Custom module to handle session logic. - **args** (term) - Optional - Initial arguments for the session. ### Response #### Success Response (200) - **status** (atom) - Returns :ok if session started successfully. ## POST /session/terminate ### Description Stops an active session and cleans up resources. ### Method POST ### Parameters #### Request Body - **session_id** (string) - Required - The ID of the session to terminate. ``` -------------------------------- ### Implement a State Reducer with Async Support Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Shows how to implement a full reducer with initial state, synchronous action handling, and asynchronous side-effect management using the handle_async callback. ```elixir defmodule MyApp.CounterReducer do use Phoenix.SessionProcess, :reducer @name :counter @action_prefix "counter" @impl true def init_state, do: %{count: 0} @impl true def handle_action(action, state) do case action.type do "increment" -> %{state | count: state.count + 1} _ -> handle_unmatched_action(action, state) end end @impl true def handle_async(action, dispatch, state) do case action.type do "fetch_remote" -> task = Task.async(fn -> dispatch.("set", 10) end) fn -> Task.shutdown(task) end _ -> handle_unmatched_async(action, dispatch, state) end end end ``` -------------------------------- ### Unsubscribe from Session Process Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Example of how to unsubscribe from session process updates using the session ID and subscription ID. ```elixir :ok = Phoenix.SessionProcess.unsubscribe(session_id, sub_id) ``` -------------------------------- ### Add Benchmark Dependencies Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/bench/README.md Configuration for adding Benchee and the session process library to the project's mix.exs file. ```elixir defp deps do [ {:benchee, "~> 1.0", only: :dev}, {:phoenix_session_process, "~> 1.0"} ] end ``` -------------------------------- ### Monitor Session Lifecycle with Telemetry Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/bench/README.md Attaches telemetry handlers to track session start, stop, and call events for production monitoring. ```elixir :telemetry.attach_many("session-metrics", [ [:phoenix, :session_process, :start], [:phoenix, :session_process, :stop], [:phoenix, :session_process, :call] ], fn event, measurements, _meta, _config -> IO.inspect({event, measurements}) end, nil) ``` -------------------------------- ### Manage Session Lifecycle and Statistics Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Use the SessionProcess API to start, check, terminate, and retrieve statistics for user sessions within your application. ```elixir alias Phoenix.SessionProcess # Start session {:ok, pid} = SessionProcess.start_session("user_abc123") # Check existence true = SessionProcess.started?("user_abc123") # Extend TTL :ok = SessionProcess.touch("user_abc123") # Terminate :ok = SessionProcess.terminate("user_abc123") # Get stats stats = SessionProcess.session_stats() ``` -------------------------------- ### Define a Session Process Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Demonstrates how to define a session process module using the Phoenix.SessionProcess macro. This includes setting the initial state and defining combined reducers. ```elixir defmodule MySessionProcess do use Phoenix.SessionProcess, :process @impl true def init_state(_args) do %{count: 0, user: nil} end @impl true def combined_reducers do [MyApp.CounterReducer, MyApp.UserReducer] end end ``` -------------------------------- ### Create Custom Session Benchmarks Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/bench/README.md Shows how to define a custom module to measure session creation rates using Elixir's timer module. ```elixir defmodule MyApp.Bench do def custom_bench do sessions = Enum.map(1..1000, &"user_#{&1}") {time, _} = :timer.tc(fn -> Phoenix.SessionProcess.Helpers.start_sessions(sessions) end) rate = 1000 / (time / 1_000_000) IO.puts("Custom rate: #{rate} sessions/sec") end end ``` -------------------------------- ### Managing State Subscriptions Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Shows how to subscribe to state changes using a selector function and handle incoming updates. It also demonstrates the manual unsubscribe process, though automatic cleanup is handled by process monitoring. ```elixir # Subscribe to state changes with selector {:ok, sub_id} = SessionProcess.subscribe( session_id, fn state -> state.counter.count end, :count_changed, self() ) # Receive updates when selected value changes receive do {:count_changed, new_count} -> IO.puts("Count is now: #{new_count}") end # Unsubscribe (optional, automatic cleanup via monitoring) :ok = SessionProcess.unsubscribe(session_id, sub_id) ``` -------------------------------- ### Define and Compose Reusable Reducers Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CODE_PROMPT.md Demonstrates how to define a reducer using the Phoenix.SessionProcess macro and compose it into different session types for modular state management. ```elixir defmodule MyApp.CartReducer do use Phoenix.SessionProcess, :reducer @name :cart end defmodule MyApp.UserSession do def combined_reducers, do: [MyApp.CartReducer, MyApp.UserReducer] end defmodule MyApp.GuestSession do def combined_reducers, do: [MyApp.CartReducer] end ``` -------------------------------- ### Configure phoenix_session_process Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Sets up the configuration for the phoenix_session_process library, including the session process module, session limits, and TTL. It also defines how to handle unmatched actions. ```elixir config :phoenix_session_process, session_process: MyApp.SessionProcess, max_sessions: 10_000, session_ttl: 3_600_000, unmatched_action_handler: :log ``` -------------------------------- ### Phoenix Session Process: LiveView Integration Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Illustrates how to integrate the Phoenix Session Process with Phoenix LiveView. This includes subscribing to state changes, fetching initial state, dispatching events to update state, and handling subscription cleanup. ```elixir defmodule MyAppWeb.DashboardLive do use Phoenix.LiveView alias Phoenix.SessionProcess def mount(_params, %{"session_id" => session_id}, socket) do # Session should already exist (started in router hook or login) if SessionProcess.started?(session_id) do # Subscribe to state changes with a selector {:ok, sub_id} = SessionProcess.subscribe( session_id, fn state -> state.counter.count end, # Selector function :count_changed, # Event name for messages self() # Subscriber pid (defaults to self()) ) # Get initial state initial_count = SessionProcess.get_state(session_id, fn s -> s.counter.count end) {:ok, assign(socket, session_id: session_id, subscription_id: sub_id, count: initial_count)} else {:ok, push_redirect(socket, to: "/login")} end end # Handle state change messages from subscription def handle_info({:count_changed, new_count}, socket) do {:noreply, assign(socket, count: new_count)} end # Dispatch actions to update state (MUST use binary types) def handle_event("increment", _params, socket) do :ok = SessionProcess.dispatch(socket.assigns.session_id, "counter.increment") {:noreply, socket} end def handle_event("decrement", _params, socket) do # Can also dispatch async :ok = SessionProcess.dispatch_async(socket.assigns.session_id, "counter.decrement") {:noreply, socket} end def handle_event("set_value", %{"value" => value}, socket) do # Dispatch with payload :ok = SessionProcess.dispatch(socket.assigns.session_id, "counter.set", String.to_integer(value)) {:noreply, socket} end def terminate(_reason, socket) do # Cleanup is automatic via process monitoring, but can explicitly unsubscribe if needed if sub_id = socket.assigns[:subscription_id] do SessionProcess.unsubscribe(socket.assigns.session_id, sub_id) end :ok end end ``` -------------------------------- ### Phoenix LiveView Integration with Session Process Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md Demonstrates how to integrate Phoenix LiveView with the SessionProcess for managing user state. It covers subscribing to state changes, handling updates, dispatching actions, and initial mounting. ```elixir defmodule MyAppWeb.DashboardLive do use Phoenix.LiveView alias Phoenix.SessionProcess def mount(_params, %{"session_id" => session_id}, socket) do # Subscribe to user state {:ok, _sub_id} = SessionProcess.subscribe( session_id, fn state -> state.user end, :user_changed, self() ) # Get initial state state = SessionProcess.get_state(session_id) {:ok, assign(socket, session_id: session_id, state: state)} end # Receive state updates def handle_info({:user_changed, user}, socket) do {:noreply, assign(socket, user: user)} end # Dispatch actions def handle_event("increment", _params, socket) do SessionProcess.dispatch(socket.assigns.session_id, "increment", nil, async: true) {:noreply, socket} end def terminate(_reason, socket) do # Subscriptions are automatically cleaned up via process monitoring :ok end end ``` -------------------------------- ### Integrate SessionProcess into Application Supervision Tree Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Add the Phoenix.SessionProcess supervisor to your application's supervision tree to ensure the session management process is started alongside your application. ```elixir defmodule MyApp.Application do use Application def start(_type, _args) do children = [ MyAppWeb.Endpoint, {Phoenix.SessionProcess, []} ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end ``` -------------------------------- ### Standard GenServer State Management in Phoenix.SessionProcess Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/README.md This Elixir code demonstrates using standard GenServer callbacks within a Phoenix.SessionProcess. It shows how to initialize a basic state containing a user ID, arbitrary data, and timestamps, and how to handle calls and casts to retrieve and update this state. ```elixir defmodule MyApp.BasicSessionProcess do use Phoenix.SessionProcess, :process @impl true def init(_init_arg) do {:ok, %{user_id: nil, data: %{}, timestamps: []}} end @impl true def handle_call(:get_user, _from, state) do {:reply, state.user_id, state} end @impl true def handle_cast({:set_user, user_id}, state) do {:noreply, %{state | user_id: user_id}} end @impl true def handle_cast({:add_data, key, value}, state) do new_data = Map.put(state.data, key, value) {:noreply, %{state | data: new_data}} end end ``` -------------------------------- ### Add Session Process Supervisor to Application Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Integrates the Phoenix Session Process supervisor into the main application's supervision tree. This ensures the session process is started and managed correctly. ```elixir def start(_type, _args) do children = [ {Phoenix.SessionProcess, []}, # Or: Phoenix.SessionProcess.Supervisor, # ... other children ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end ``` -------------------------------- ### Handling Unmatched Actions in Elixir Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md Describes the default behavior for unmatched actions in Elixir, which is to log a debug message. Provides examples of how to customize this behavior by overriding `handle_unmatched_action/2` or configuring it globally. ```elixir # Default behavior: logs debug message for unmatched actions def handle_action(action, state) do case action do %Action{type: "known"} -> # handle action _ -> handle_unmatched_action(action, state) # Logs debug message end end # Override to customize behavior def handle_unmatched_action(action, state) do # Custom logic, e.g., track unmatched actions MyApp.Metrics.track_unmatched(action) state end # Or configure globally config :phoenix_session_process, unmatched_action_handler: :warn # :log | :warn | :silent | custom function ``` -------------------------------- ### Configure Phoenix SessionProcess Dependencies and Settings Source: https://context7.com/gsmlg-dev/phoenix_session_process/llms.txt Add the library to your mix.exs dependencies and define global configuration settings in config.exs to manage session limits and TTL. ```elixir # mix.exs def deps do [ {:phoenix_session_process, "~> 1.0"} ] end # config/config.exs config :phoenix_session_process, session_process: MyApp.SessionProcess, max_sessions: 10_000, session_ttl: 3_600_000, rate_limit: 100, unmatched_action_handler: :log ``` -------------------------------- ### Define Reusable Reducers in Elixir Source: https://github.com/gsmlg-dev/phoenix_session_process/blob/main/CLAUDE.md This Elixir code defines reusable reducers for session state management using Phoenix.SessionProcess. It includes examples for a CartReducer and a UserReducer, demonstrating how to initialize state and handle actions. ```elixir defmodule MyApp.CartReducer do use Phoenix.SessionProcess, :reducer @name :cart @action_prefix "cart" @impl true def init_state, do: %{items: [], total: 0} @impl true def handle_action(action, state) do case action do %Action{type: "add_item", payload: item} -> %{state | items: [item | state.items], total: state.total + item.price} %Action{type: "clear"} -> %{items: [], total: 0} _ -> state end end end defmodule MyApp.UserReducer do use Phoenix.SessionProcess, :reducer @name :user @action_prefix "user" @impl true def init_state, do: %{current_user: nil, authenticated: false} @impl true def handle_action(action, state) do case action do %Action{type: "login", payload: user} -> %{state | current_user: user, authenticated: true} %Action{type: "logout"} -> %{current_user: nil, authenticated: false} _ -> state end end end ```