Session-backed Backpex.Preferences adapter.
Stores all preferences as a single nested map under one Phoenix session key
("backpex_preferences"). Exact storage characteristics depend on the host
app's Plug.Session backend (cookie, ETS, Redis, ...). The default cookie
store has a ~4KB limit; prefer a database-backed adapter when you expect
bulky per-user data.
Reference implementation for the Backpex.Preferences.Adapter behavior — a
reasonable template when writing your own adapter.
Size limit
With the default :cookie session store the whole session — Backpex's
preferences plus everything the host app keeps in it, such as
phx.gen.auth's user token — travels in one Set-Cookie header, and
Plug.Conn refuses to send a header longer than 4096 bytes: it raises
Plug.Conn.CookieOverflowError, the request 500s, and the write is lost.
put/4 therefore estimates the size of that header and refuses a write that
would breach the budget with {:error, :too_large}, leaving the stored value
untouched; Backpex.PreferencesController turns that into a 422 so the
browser can stop carrying the write. A warning is logged well before the
ceiling.
Options
:max_bytes— the budget for the session cookie's wholeSet-Cookieheader (name, signed value and attributes), in bytes (default:4096,Plug.Conn's limit). Use:infinityfor a server-side session store (ETS, Redis, ...), which has no such cap:config :backpex, Backpex.Preferences, adapters: [ {:default, Backpex.Preferences.Adapters.Session, max_bytes: :infinity} ]
The estimate covers the whole session, not just Backpex's subtree. It counts
the ~4/3 growth of Base64 and reserves 256 bytes for the rest of the header:
the signature (or the encryption envelope, with :encryption_salt), the
cookie name and the attributes Plug.Session adds (path, HttpOnly,
SameSite, secure, max-age/expires, domain). The adapter cannot see
the Plug.Session options, so this is deliberately approximate: it is a
budget check, not an exact reproduction of the store's encoding, and it is
conservative for typical options.
If your options are unusually long (a long :key or :domain, :extra
attributes), work out the tail your app actually sends:
# `key` and the cookie options (`:domain`, `:max_age`, `:secure`,
# `:same_site`, `:extra`, ...) as passed to `Plug.Session`
header = Plug.Conn.Cookies.encode(key, Map.new([value: ""] ++ cookie_opts))
# 52 for a signed cookie (the default); about 58 instead with
# `:encryption_salt`, where the value is encrypted, not also signed
tail = byte_size(header) + 52and lower :max_bytes by however much tail exceeds 256. The warning
threshold is 75% of :max_bytes and moves with it. Prefer a database-backed
adapter over raising :max_bytes when per-user data genuinely does not fit.
Write-path limitations
put/4 returns {:error, :requires_http} for any source other than
:controller. Plug.Session cannot write to the Phoenix session outside
an HTTP request cycle. The dispatcher handles this by falling back to
push_event/3, which round-trips the write through the browser and the
preferences controller.
nil values
get/3 maps a stored nil to {:ok, :not_found}. Store a tagged value such
as %{"value" => nil} if application code must distinguish an explicit nil
from an absent preference.
Summary
Functions
Returns the Phoenix session key used to store the preferences tree.