Skip to content

Commit fbde248

Browse files
GSMLG-BOTclaude
andcommitted
docs: Enhance Phoenix.SessionProcess module documentation
Significantly improved the main module documentation with comprehensive @moduledoc and @doc comments for all public functions. ## Changes ### Module Documentation (@moduledoc) - Complete rewrite with detailed feature overview - Quick start guide with code examples - Configuration instructions - Custom session process examples - API overview grouped by category - Error handling documentation - Performance metrics ### Function Documentation - Added comprehensive @doc for all delegated functions: - start/1, start/2, start/3 - with parameters, returns, examples - started?/1 - with clear boolean return documentation - terminate/1 - with graceful shutdown details - call/2, call/3 - with timeout and error handling - cast/2 - with async messaging details - Enhanced existing @doc comments: - list_session/0 - comprehensive examples - session_info/0 - return value documentation - find_session/1 - comparison with started?/1 - session_stats/0 - detailed stats breakdown - list_sessions_by_module/1 - filter documentation ### Documentation Improvements - Clear parameter descriptions - Comprehensive return value documentation - Multiple usage examples for each function - Error case handling examples - Performance expectations - Integration patterns ## Impact +261 lines of documentation All public API functions now have detailed @doc comments Improved developer experience and API discoverability Co-Authored-By: Claude <[email protected]>
1 parent 1231bcd commit fbde248

File tree

1 file changed

+261
-30
lines changed

1 file changed

+261
-30
lines changed

lib/phoenix/session_process.ex

Lines changed: 261 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,196 @@
11
defmodule Phoenix.SessionProcess do
22
@moduledoc """
3-
Documentation for `Phoenix.SessionProcess`.
3+
Main API for managing isolated session processes in Phoenix applications.
44
5-
Add superviser to process tree
5+
This module provides a high-level interface for creating, managing, and communicating
6+
with dedicated GenServer processes for each user session. Each session runs in its own
7+
isolated process, enabling real-time session state management without external dependencies.
68
7-
[
8-
...
9-
{Phoenix.SessionProcess.Supervisor, []}
10-
]
9+
## Features
1110
12-
Add this after the `:fetch_session` plug to generate a unique session ID.
11+
- **Session Isolation**: Each user session runs in a dedicated GenServer process
12+
- **Automatic Cleanup**: TTL-based session expiration and garbage collection
13+
- **LiveView Integration**: Built-in support for monitoring LiveView processes
14+
- **High Performance**: 10,000+ sessions/second creation rate
15+
- **Zero Dependencies**: No Redis, databases, or external services required
16+
- **Comprehensive Telemetry**: Built-in observability for all operations
1317
14-
plug :fetch_session
15-
plug Phoenix.SessionProcess.SessionId
18+
## Quick Start
1619
17-
Start a session process with a session ID.
20+
### 1. Add to Supervision Tree
1821
19-
Phoenix.SessionProcess.start("session_id")
22+
Add the supervisor to your application's supervision tree in `lib/my_app/application.ex`:
2023
21-
This will start a session process using the module defined with
24+
def start(_type, _args) do
25+
children = [
26+
# ... other children ...
27+
{Phoenix.SessionProcess.Supervisor, []}
28+
]
2229
23-
config :phoenix_session_process, session_process: MySessionProcess
30+
Supervisor.start_link(children, strategy: :one_for_one)
31+
end
32+
33+
### 2. Configure Session ID Generation
34+
35+
Add the SessionId plug after `:fetch_session` in your router:
36+
37+
pipeline :browser do
38+
plug :accepts, ["html"]
39+
plug :fetch_session
40+
plug Phoenix.SessionProcess.SessionId # Add this
41+
# ... other plugs ...
42+
end
43+
44+
### 3. Use in Controllers and LiveViews
45+
46+
defmodule MyAppWeb.PageController do
47+
use MyAppWeb, :controller
48+
49+
def index(conn, _params) do
50+
session_id = conn.assigns.session_id
51+
52+
# Start session process
53+
{:ok, _pid} = Phoenix.SessionProcess.start(session_id)
54+
55+
# Store data
56+
Phoenix.SessionProcess.cast(session_id, {:put, :user_id, 123})
57+
58+
# Retrieve data
59+
{:ok, state} = Phoenix.SessionProcess.call(session_id, :get_state)
60+
61+
render(conn, "index.html", state: state)
62+
end
63+
end
64+
65+
## Configuration
66+
67+
Configure the library in `config/config.exs`:
68+
69+
config :phoenix_session_process,
70+
session_process: MyApp.SessionProcess, # Default session module
71+
max_sessions: 10_000, # Maximum concurrent sessions
72+
session_ttl: 3_600_000, # Session TTL (1 hour)
73+
rate_limit: 100 # Sessions per minute
74+
75+
## Creating Custom Session Processes
2476
25-
Or you can start a session process with a specific module.
77+
### Basic Session Process
2678
27-
Phoenix.SessionProcess.start("session_id", MySessionProcess)
28-
# or
29-
Phoenix.SessionProcess.start("session_id", MySessionProcess, arg)
79+
defmodule MyApp.SessionProcess do
80+
use Phoenix.SessionProcess, :process
3081
31-
Check if a session process is started.
82+
@impl true
83+
def init(_init_arg) do
84+
{:ok, %{user_id: nil, cart: [], preferences: %{}}}
85+
end
3286
33-
Phoenix.SessionProcess.started?("session_id")
87+
@impl true
88+
def handle_call(:get_user, _from, state) do
89+
{:reply, state.user_id, state}
90+
end
3491
35-
Terminate a session process.
92+
@impl true
93+
def handle_cast({:set_user, user_id}, state) do
94+
{:noreply, %{state | user_id: user_id}}
95+
end
96+
end
3697
37-
Phoenix.SessionProcess.terminate("session_id")
98+
### With LiveView Integration
3899
39-
Genserver call on a session process.
100+
defmodule MyApp.SessionProcessWithLiveView do
101+
use Phoenix.SessionProcess, :process_link
40102
41-
Phoenix.SessionProcess.call("session_id", request)
103+
@impl true
104+
def init(_init_arg) do
105+
{:ok, %{user: nil, live_views: []}}
106+
end
42107
43-
Genserver cast on a session process.
108+
# Automatically monitors LiveView processes
109+
# Sends :session_expired message when session terminates
110+
end
44111
45-
Phoenix.SessionProcess.cast("session_id", request)
112+
## API Overview
46113
47-
List all session processes.
114+
### Session Management
115+
- `start/1`, `start/2`, `start/3` - Start session processes
116+
- `started?/1` - Check if session exists
117+
- `terminate/1` - Stop session process
118+
- `find_session/1` - Find session by ID
48119
49-
Phoenix.SessionProcess.list_session()
120+
### Communication
121+
- `call/2`, `call/3` - Synchronous requests
122+
- `cast/2` - Asynchronous messages
123+
124+
### Inspection
125+
- `list_session/0` - List all sessions
126+
- `session_info/0` - Get session count and modules
127+
- `session_stats/0` - Get memory and performance stats
128+
- `list_sessions_by_module/1` - Filter sessions by module
129+
130+
## Error Handling
131+
132+
All operations return structured error tuples:
133+
134+
{:error, {:invalid_session_id, session_id}}
135+
{:error, {:session_limit_reached, max_sessions}}
136+
{:error, {:session_not_found, session_id}}
137+
{:error, {:timeout, timeout}}
138+
139+
Use `Phoenix.SessionProcess.Error.message/1` for human-readable errors.
140+
141+
## Performance
142+
143+
Expected performance metrics:
144+
- Session Creation: 10,000+ sessions/sec
145+
- Memory Usage: ~10KB per session
146+
- Registry Lookups: 100,000+ lookups/sec
147+
148+
See the benchmarking guide at `bench/README.md` for details.
50149
"""
51150

151+
@doc """
152+
Starts a session process using the default configured module.
153+
154+
The session process is registered in the Registry and scheduled for automatic
155+
cleanup based on the configured TTL.
156+
157+
## Parameters
158+
- `session_id` - Unique binary identifier for the session
159+
160+
## Returns
161+
- `{:ok, pid}` - Session process started successfully
162+
- `{:error, {:already_started, pid}}` - Session already exists
163+
- `{:error, {:invalid_session_id, id}}` - Invalid session ID format
164+
- `{:error, {:session_limit_reached, max}}` - Maximum sessions exceeded
165+
166+
## Examples
167+
168+
{:ok, pid} = Phoenix.SessionProcess.start("user_123")
169+
{:error, {:already_started, pid}} = Phoenix.SessionProcess.start("user_123")
170+
"""
52171
@spec start(binary()) :: {:ok, pid()} | {:error, term()}
53172
defdelegate start(session_id), to: Phoenix.SessionProcess.ProcessSupervisor, as: :start_session
54173

55174
@doc """
56-
Start a session process with a specific module.
175+
Starts a session process using a custom module.
176+
177+
This allows you to use a specific session process implementation instead of
178+
the default configured module.
179+
180+
## Parameters
181+
- `session_id` - Unique binary identifier for the session
182+
- `module` - Module implementing the session process behavior
183+
184+
## Returns
185+
- `{:ok, pid}` - Session process started successfully
186+
- `{:error, {:already_started, pid}}` - Session already exists
187+
- `{:error, {:invalid_session_id, id}}` - Invalid session ID format
188+
- `{:error, {:session_limit_reached, max}}` - Maximum sessions exceeded
57189
58190
## Examples
59191
192+
{:ok, pid} = Phoenix.SessionProcess.start("user_123", MyApp.CustomSessionProcess)
193+
60194
iex> result = Phoenix.SessionProcess.start("valid_session", Phoenix.SessionProcess.DefaultSessionProcess)
61195
iex> match?({:ok, _pid}, result) or match?({:error, {:already_started, _pid}}, result)
62196
true
@@ -70,10 +204,30 @@ defmodule Phoenix.SessionProcess do
70204
as: :start_session
71205

72206
@doc """
73-
Start a session process with a specific module and initialization arguments.
207+
Starts a session process with a custom module and initialization arguments.
208+
209+
The initialization arguments are passed to the module's `init/1` callback,
210+
allowing you to set up initial state or configuration.
211+
212+
## Parameters
213+
- `session_id` - Unique binary identifier for the session
214+
- `module` - Module implementing the session process behavior
215+
- `arg` - Initialization argument(s) passed to `init/1`
216+
217+
## Returns
218+
- `{:ok, pid}` - Session process started successfully
219+
- `{:error, {:already_started, pid}}` - Session already exists
220+
- `{:error, {:invalid_session_id, id}}` - Invalid session ID format
221+
- `{:error, {:session_limit_reached, max}}` - Maximum sessions exceeded
74222
75223
## Examples
76224
225+
# With map argument
226+
{:ok, pid} = Phoenix.SessionProcess.start("user_123", MyApp.SessionProcess, %{user_id: 123})
227+
228+
# With keyword list
229+
{:ok, pid} = Phoenix.SessionProcess.start("user_456", MyApp.SessionProcess, [debug: true])
230+
77231
iex> result = Phoenix.SessionProcess.start("valid_session_with_args", Phoenix.SessionProcess.DefaultSessionProcess, %{user_id: 123})
78232
iex> match?({:ok, _pid}, result) or match?({:error, {:already_started, _pid}}, result)
79233
true
@@ -87,21 +241,98 @@ defmodule Phoenix.SessionProcess do
87241
to: Phoenix.SessionProcess.ProcessSupervisor,
88242
as: :start_session
89243

244+
@doc """
245+
Checks if a session process is currently running.
246+
247+
## Parameters
248+
- `session_id` - Unique binary identifier for the session
249+
250+
## Returns
251+
- `true` - Session process exists and is running
252+
- `false` - Session process does not exist
253+
254+
## Examples
255+
256+
{:ok, _pid} = Phoenix.SessionProcess.start("user_123")
257+
true = Phoenix.SessionProcess.started?("user_123")
258+
false = Phoenix.SessionProcess.started?("nonexistent")
259+
"""
90260
@spec started?(binary()) :: boolean()
91261
defdelegate started?(session_id),
92262
to: Phoenix.SessionProcess.ProcessSupervisor,
93263
as: :session_process_started?
94264

265+
@doc """
266+
Terminates a session process.
267+
268+
This gracefully shuts down the session process and removes it from the Registry.
269+
Emits telemetry events for session stop.
270+
271+
## Parameters
272+
- `session_id` - Unique binary identifier for the session
273+
274+
## Returns
275+
- `:ok` - Session terminated successfully
276+
- `{:error, :not_found}` - Session does not exist
277+
278+
## Examples
279+
280+
{:ok, _pid} = Phoenix.SessionProcess.start("user_123")
281+
:ok = Phoenix.SessionProcess.terminate("user_123")
282+
{:error, :not_found} = Phoenix.SessionProcess.terminate("user_123")
283+
"""
95284
@spec terminate(binary()) :: :ok | {:error, :not_found}
96285
defdelegate terminate(session_id),
97286
to: Phoenix.SessionProcess.ProcessSupervisor,
98287
as: :terminate_session
99288

100-
@spec call(binary(), any(), :infinity | non_neg_integer()) :: {:ok, any()} | {:error, term()}
289+
@doc """
290+
Makes a synchronous call to a session process.
291+
292+
Sends a synchronous request to the session process and waits for a response.
293+
The request is handled by the session process's `handle_call/3` callback.
294+
295+
## Parameters
296+
- `session_id` - Unique binary identifier for the session
297+
- `request` - The request message to send
298+
- `timeout` - Maximum time to wait for response in milliseconds (default: 15,000)
299+
300+
## Returns
301+
- Response from the session process's `handle_call/3` callback
302+
- `{:error, {:session_not_found, id}}` - Session does not exist
303+
- `{:error, {:timeout, timeout}}` - Request timed out
304+
305+
## Examples
306+
307+
{:ok, _pid} = Phoenix.SessionProcess.start("user_123")
308+
{:ok, state} = Phoenix.SessionProcess.call("user_123", :get_state)
309+
{:ok, :pong} = Phoenix.SessionProcess.call("user_123", :ping, 5_000)
310+
"""
311+
@spec call(binary(), any(), :infinity | non_neg_integer()) :: any()
101312
defdelegate call(session_id, request, timeout \\ 15_000),
102313
to: Phoenix.SessionProcess.ProcessSupervisor,
103314
as: :call_on_session
104315

316+
@doc """
317+
Sends an asynchronous message to a session process.
318+
319+
Sends a fire-and-forget message to the session process. The message is handled
320+
by the session process's `handle_cast/2` callback. Does not wait for a response.
321+
322+
## Parameters
323+
- `session_id` - Unique binary identifier for the session
324+
- `request` - The message to send
325+
326+
## Returns
327+
- `:ok` - Message sent successfully
328+
- `{:error, {:session_not_found, id}}` - Session does not exist
329+
330+
## Examples
331+
332+
{:ok, _pid} = Phoenix.SessionProcess.start("user_123")
333+
:ok = Phoenix.SessionProcess.cast("user_123", {:put, :user_id, 123})
334+
:ok = Phoenix.SessionProcess.cast("user_123", {:delete, :old_key})
335+
"""
105336
@spec cast(binary(), any()) :: :ok | {:error, term()}
106337
defdelegate cast(session_id, request),
107338
to: Phoenix.SessionProcess.ProcessSupervisor,

0 commit comments

Comments
 (0)