Normandy.Behaviours.SessionRegistry.Redis
(normandy v2.2.0)
View Source
Distributed SessionRegistry using Redis as the shared session_id → pid name
table, with cross-node routing over Erlang distribution (Redis replaces Horde's
CRDT, not distribution). A per-node owner GenServer monitors locally registered pids
(clearing the key on :DOWN) and refreshes key TTLs so a crashed node's keys lapse on
their own.
The handle is a Normandy.Behaviours.SessionRegistry.Redis.Handle,
%Handle{owner:, conn:, namespace:}: the owner's
registered name, the Redix connection and the key namespace. new/1 returns one;
build one with handle/1 from the values given to start_link/1, or with
fetch_handle/1 for an owner that opened its own connection from :url.
whereis/2 runs in the caller, on the handle's connection: a lookup never waits on
the owner. register/3 and unregister/2 go through the owner, which monitors the
pids. An owner started with :url registers its connection as
Module.concat(name, Redix), so a handle keeps working across owner restarts; that
name is reserved for it, and an owner whose derived name another process holds fails
to start with {:error, {:connection_name_taken, name}} (pass :conn or rename the
owner). A handle is meant for the node its owner runs on: on another node, a lookup
reaches the connection over distribution.
Registration is atomic via SET … NX (no read-modify-write), so a concurrent start
cluster-wide yields one winner; the losers route to it. Use as a :via registry
through child_name/2 (the via term is {handle, session_id}) to get atomic,
supervisor-driven starts (closes the start race), exactly like the Horde impl.
Refresh and lost ownership
Every ttl_ms / 2 the owner extends the TTL of each key it holds, in pipelines of
1,000 EVALSHA calls. A key that lapsed (a late refresh, an eviction) is
claimed again with SET … NX. A key another node now holds means two servers for
one session: the owner stops its local process with exit reason
{:shutdown, :registration_lost}, logs a warning and emits
[:normandy, :session_registry, :ownership_lost]. A Turn.Server stopped this way
is not restarted (restart: :transient); its session goes on where the key points.
The stop is an exit signal: a registered process that traps exits receives it as a
message and must stop itself; the owner no longer monitors it either way.
Summary
Functions
Returns a specification to start this module under a supervisor.
The handle of a running owner, asked of it once: for an owner started with :url,
whose connection only the owner knows. Keep the result; a lookup with it never calls
the owner. The handle names the owner's connection (Module.concat(owner, Redix)
for a :url owner, else the :conn given), so it stays valid when the owner
restarts under the same name, as long as a :conn given by name is restarted too.
Builds the handle of an owner started with start_link/1's :name, :conn and
:namespace. Starts and calls nothing.
Starts an owner GenServer with its own Redix connection to :url and returns its
handle. :url and :namespace are required.
Starts the per-node owner GenServer. Build the handle with handle/1 from the same
:name, :conn and :namespace, or with fetch_handle/1 when the owner connects
from :url.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec fetch_handle(atom()) :: Normandy.Behaviours.SessionRegistry.Redis.Handle.t()
The handle of a running owner, asked of it once: for an owner started with :url,
whose connection only the owner knows. Keep the result; a lookup with it never calls
the owner. The handle names the owner's connection (Module.concat(owner, Redix)
for a :url owner, else the :conn given), so it stays valid when the owner
restarts under the same name, as long as a :conn given by name is restarted too.
@spec handle(keyword()) :: Normandy.Behaviours.SessionRegistry.Redis.Handle.t()
Builds the handle of an owner started with start_link/1's :name, :conn and
:namespace. Starts and calls nothing.
Options
:owner(atom/0) - Required. The owner's registered name (start_link/1's:name).:conn(GenServer.server/0) - Required. The Redix connection the owner uses (pid or name).:namespace(String.t/0) - Required. The key namespace (start_link/1's:namespace).
@spec new(keyword()) :: Normandy.Behaviours.SessionRegistry.Redis.Handle.t()
Starts an owner GenServer with its own Redix connection to :url and returns its
handle. :url and :namespace are required.
Options
:name(atom/0) - The owner's name (default: a unique one).:url(String.t/0) - Required. The Redis URL.:namespace(String.t/0) - Required. The key namespace.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the per-node owner GenServer. Build the handle with handle/1 from the same
:name, :conn and :namespace, or with fetch_handle/1 when the owner connects
from :url.
Options
:name(atom/0) - Required. The owner's registered name (the handle's:owner).:conn(GenServer.server/0) - A started Redix connection (pid or name).:url(String.t/0) - A Redis URL to connect to when:connis not given; the owner starts that connection linked, registered asModule.concat(name, Redix), a name no other process may hold (the start fails with{:error, {:connection_name_taken, name}}).:namespace(String.t/0) - Required. The key namespace.:ttl_ms(pos_integer/0) - Registration TTL in ms, refreshed everyttl_ms / 2; a dead node's keys lapse after it. The default value is60000.