Database-backed, cached, cluster-aware system configuration for Phoenix.
Usage
Define a configuration module in your application:
defmodule MyApp.SystemConfig do
use PhoenixFlags,
otp_app: :my_app,
repo: MyApp.Repo
def benefits_enabled?, do: get("enable_benefits", false)
endAdd it to your supervision tree:
children = [
MyApp.Repo,
MyApp.SystemConfig,
# ...
]Read configuration values:
MyApp.SystemConfig.get("enable_benefits") #=> true
MyApp.SystemConfig.get("max_retries", 3) #=> 3
MyApp.SystemConfig.benefits_enabled?() #=> trueTesting
In test environment, a Test submodule is generated with process-scoped overrides:
MyApp.SystemConfig.Test.stub("enable_benefits", true)
MyApp.SystemConfig.Test.insert_entry("enable_benefits", true)Architecture
- Storage:
system_flagstable in PostgreSQL (source of truth) - Cache: Single
:persistent_termkey holding a pre-built%{key => value}map - Reads:
:persistent_term.get+Map.get— zero-copy, no process calls - Writes: GenServer updates DB, reloads local cache, notifies peer nodes
- Cluster: After a write, the GenServer sends
:reloadto its counterpart on all connected nodes — no PubSub dependency. A periodic jittered cache refresh (refresh_interval, default 60s) heals nodes that missed a notification.
Summary
Functions
Clears the current process's targeting context.
Returns the current process's targeting context.
Declares a flag. Validated at compile time.
Merges attributes into the current process's targeting context.
Sets the targeting context for the current process.
Functions
Clears the current process's targeting context.
Returns the current process's targeting context.
Declares a flag. Validated at compile time.
flag "enable_benefits",
type: :boolean,
default: "false",
category: "integrations",
label: "Enable Benefits",
description: "When enabled, the Benefits integration is active."
Merges attributes into the current process's targeting context.
Sets the targeting context for the current process.
Shorthand for PhoenixFlags.Context.put/1. Call it once where you already
have the current user — a plug, or a LiveView on_mount hook — and every read
in that process can be targeted without threading a context through:
PhoenixFlags.put_context(user_id: user.id, company_id: user.company_id)Not inherited by spawned processes; see PhoenixFlags.Context.