One process per chat.
A session is created by Raxol.Gateway.SessionRouter for a Route and runs a
Raxol.Gateway.Handler. Each inbound event is dispatched to the handler; a
{:reply, rendered, state} result is delivered back through the session's
:deliver function (the router builds it from the adapter and connection). A
session stops itself after :idle_timeout ms of inactivity.
A session has a conversation_id (defaulting to its route key) that is stable
across a platform handoff: the router can start a session on a new route with
an existing conversation_id, so a configured :log resumes the same history.
Handler startup
start_link/1 returns as soon as the process exists; the handler's init/2
runs in a handle_continue after it. This keeps an expensive handler (one
starting a per-chat TEA app, say) from blocking the router that started it,
which starts sessions synchronously inside its own handle_call.
Two consequences for callers. A handler that fails to initialize stops the
session after start_link/1 has already returned {:ok, pid}, so the
failure arrives as a DOWN rather than a return value -- SessionRouter emits
[:raxol_gateway, :session, :down] for it. And "the handler has started" is
not implied by start_link/1 returning: any GenServer.call/3 to the session
is serialized behind the continue and can be used as the barrier.
Because startup is no longer reported through a return value, it is reported
through telemetry instead: [:raxol_gateway, :session, :ready] once the
handler has initialized, and [..., :init_timeout] when it never does. A
:started from the router with no :ready behind it is a broken handler.
Handler init is bounded
A handler stuck in init/2 parks the session inside its own continue, where
it can read no message -- not a queued event, and not the idle timer, which is
armed only once init succeeds. Nothing would ever reap it, while the router
goes on routing that chat to it. So init/1 spawns a watchdog that kills the
session if the handler has not returned within :handler_init_timeout. The
kill has to be brutal (a wedged process cannot honour a graceful exit), which
is why the diagnosis is emitted as telemetry before it lands.
This matters most for a handler that starts a per-chat TEA app: start_link
on deployment-authored init/1 code has no timeout of its own.
Options
:route(required) -- theRaxol.Gateway.Routethis session serves:handler(required) --{module, opts}implementingGateway.Handler:deliver--(Route.t(), rendered -> any()), default a no-op.{:error, reason}, a raised exception, or an exit reports[:raxol_gateway, :session, :delivery_failed]; the session remains alive.:idle_timeout-- ms before the session stops (default 10 minutes):handler_init_timeout-- ms the handler'sinit/2may take before the session is killed (default 30 seconds), or:infinityto wait forever:conversation_id-- a stable id for this chat (defaultRoute.key/1):log--{module, server}whoseappend(server, conversation_id, items)records each inbound event and each successfully delivered outbound reply (e.g.{Raxol.Agent.Conversation.Log, log_server}); default none
Summary
Functions
Returns a specification to start this module under a supervisor.
The session's stable conversation id.
Dispatch an inbound event to the session.
The route this session serves.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec conversation_id(GenServer.server(), timeout()) :: String.t()
The session's stable conversation id.
@spec dispatch(GenServer.server(), term()) :: :ok
Dispatch an inbound event to the session.
@spec route(GenServer.server()) :: Raxol.Gateway.Route.t()
The route this session serves.
@spec start_link(keyword()) :: GenServer.on_start()