Skip to content

Commit e285828

Browse files
committed
feat: support runtime configuration via supervisor start options
Add support for passing configuration options when starting the Phoenix.SessionProcess supervisor, allowing runtime config to override application environment settings. Features: - Pass config options when starting supervisor - Runtime config stored in ETS for fast, concurrent access - Config module checks ETS first, then app env, then defaults - Supports all config keys: session_process, max_sessions, session_ttl, rate_limit - Runtime config takes precedence over application environment Implementation: - Modified Supervisor.init/1 to create ETS table with runtime config - Updated Config module to check ETS before falling back to app env - Added private helpers get_config/2 and lookup_runtime_config/1 - Updated documentation with examples Example usage: # Application supervision tree {Phoenix.SessionProcess, [ session_process: MyApp.SessionProcess, max_sessions: 20_000, session_ttl: :timer.hours(2), rate_limit: 150 ]} All tests pass (148/148), credo passes.
1 parent 435bb61 commit e285828

File tree

2 files changed

+108
-8
lines changed

2 files changed

+108
-8
lines changed

lib/phoenix/session_process/config.ex

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,32 @@ defmodule Phoenix.SessionProcess.Config do
4242
4343
## Runtime Configuration
4444
45-
Configuration values are read at runtime, allowing for dynamic updates:
45+
Configuration can be provided in two ways:
4646
47-
Application.put_env(:phoenix_session_process, :max_sessions, 20_000)
47+
### 1. Application Environment (config files)
48+
49+
# config/config.exs
50+
config :phoenix_session_process,
51+
session_process: MyApp.SessionProcess,
52+
max_sessions: 10_000
53+
54+
### 2. Supervisor Start Options (runtime)
55+
56+
# lib/my_app/application.ex
57+
def start(_type, _args) do
58+
children = [
59+
{Phoenix.SessionProcess, [
60+
session_process: MyApp.SessionProcess,
61+
max_sessions: 20_000,
62+
session_ttl: :timer.hours(2),
63+
rate_limit: 150
64+
]}
65+
]
66+
67+
Supervisor.start_link(children, strategy: :one_for_one)
68+
end
69+
70+
**Priority**: Runtime options (passed to supervisor) take precedence over application environment.
4871
4972
## Environment-specific Configuration
5073
@@ -89,7 +112,7 @@ defmodule Phoenix.SessionProcess.Config do
89112
"""
90113
@spec session_process :: module()
91114
def session_process do
92-
Application.get_env(:phoenix_session_process, :session_process, @default_session_process)
115+
get_config(:session_process, @default_session_process)
93116
end
94117

95118
@doc """
@@ -116,7 +139,7 @@ defmodule Phoenix.SessionProcess.Config do
116139
"""
117140
@spec max_sessions :: integer()
118141
def max_sessions do
119-
Application.get_env(:phoenix_session_process, :max_sessions, @default_max_sessions)
142+
get_config(:max_sessions, @default_max_sessions)
120143
end
121144

122145
@doc """
@@ -147,7 +170,7 @@ defmodule Phoenix.SessionProcess.Config do
147170
"""
148171
@spec session_ttl :: integer()
149172
def session_ttl do
150-
Application.get_env(:phoenix_session_process, :session_ttl, @default_session_ttl)
173+
get_config(:session_ttl, @default_session_ttl)
151174
end
152175

153176
@doc """
@@ -178,7 +201,7 @@ defmodule Phoenix.SessionProcess.Config do
178201
"""
179202
@spec rate_limit :: integer()
180203
def rate_limit do
181-
Application.get_env(:phoenix_session_process, :rate_limit, @default_rate_limit)
204+
get_config(:rate_limit, @default_rate_limit)
182205
end
183206

184207
@doc """
@@ -220,4 +243,32 @@ defmodule Phoenix.SessionProcess.Config do
220243
byte_size(session_id) <= 64 and
221244
String.match?(session_id, ~r/^[A-Za-z0-9_-]+$/)
222245
end
246+
247+
# Private helper to get config value with proper precedence:
248+
# 1. Runtime config (ETS table from supervisor start options)
249+
# 2. Application environment (config files)
250+
# 3. Default value
251+
defp get_config(key, default) do
252+
case lookup_runtime_config(key) do
253+
{:ok, value} ->
254+
value
255+
256+
:not_found ->
257+
Application.get_env(:phoenix_session_process, key, default)
258+
end
259+
end
260+
261+
# Lookup value from runtime config ETS table
262+
defp lookup_runtime_config(key) do
263+
table = :phoenix_session_process_runtime_config
264+
265+
if :ets.whereis(table) != :undefined do
266+
case :ets.lookup(table, key) do
267+
[{^key, value}] -> {:ok, value}
268+
[] -> :not_found
269+
end
270+
else
271+
:not_found
272+
end
273+
end
223274
end

lib/phoenix/session_process/superviser.ex

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,30 @@ defmodule Phoenix.SessionProcess.Supervisor do
3232
def start(_type, _args) do
3333
children = [
3434
# ... other children ...
35-
{Phoenix.SessionProcess.Supervisor, []}
35+
{Phoenix.SessionProcess, []}
36+
# Or with runtime configuration:
37+
{Phoenix.SessionProcess, [
38+
session_process: MyApp.CustomProcess,
39+
max_sessions: 20_000,
40+
session_ttl: :timer.hours(2),
41+
rate_limit: 150
42+
]}
3643
]
3744
3845
Supervisor.start_link(children, strategy: :one_for_one)
3946
end
4047
48+
## Runtime Configuration
49+
50+
Configuration can be provided at supervisor startup, overriding application environment settings:
51+
52+
- `:session_process` - Default session module to use
53+
- `:max_sessions` - Maximum concurrent sessions allowed
54+
- `:session_ttl` - Session time-to-live in milliseconds
55+
- `:rate_limit` - Maximum session creations per minute
56+
57+
Runtime configuration takes precedence over application environment configuration.
58+
4159
## Process Lifecycle
4260
4361
1. **Application Start**: Top-level supervisor starts all children
@@ -74,7 +92,26 @@ defmodule Phoenix.SessionProcess.Supervisor do
7492
end
7593

7694
@impl true
77-
def init(_init_arg) do
95+
def init(init_arg) do
96+
# Store runtime configuration in ETS for fast access
97+
# This allows configuration to be passed at startup instead of only via application env
98+
runtime_config = normalize_config(init_arg)
99+
100+
if runtime_config != [] do
101+
# Create ETS table for runtime config (public, readable by all processes)
102+
:ets.new(:phoenix_session_process_runtime_config, [
103+
:set,
104+
:public,
105+
:named_table,
106+
read_concurrency: true
107+
])
108+
109+
# Store each config option in ETS
110+
Enum.each(runtime_config, fn {key, value} ->
111+
:ets.insert(:phoenix_session_process_runtime_config, {key, value})
112+
end)
113+
end
114+
78115
children = [
79116
{Registry, keys: :unique, name: Phoenix.SessionProcess.Registry},
80117
{Phoenix.SessionProcess.ProcessSupervisor, []},
@@ -84,4 +121,16 @@ defmodule Phoenix.SessionProcess.Supervisor do
84121

85122
Supervisor.init(children, strategy: :one_for_one)
86123
end
124+
125+
# Normalize and validate configuration options
126+
defp normalize_config(opts) when is_list(opts) do
127+
valid_keys = [:session_process, :max_sessions, :session_ttl, :rate_limit]
128+
129+
opts
130+
|> Enum.filter(fn {key, _value} -> key in valid_keys end)
131+
|> Enum.into(%{})
132+
|> Map.to_list()
133+
end
134+
135+
defp normalize_config(_), do: []
87136
end

0 commit comments

Comments
 (0)