DrinkMeAlice.Session (drink_me_alice v0.1.0)

Copy Markdown

A caller-owned, synchronous SurrealDB session: one socket, owned by the process that opened it, one request at a time, no background process.

For short-lived work — scripts, provisioning, migrations, drills — where a supervised DrinkMeAlice.Conn owner is more machinery than the job needs. Same transport (Ws), same frame guard and same bounded JSON decode profile as Conn; no credits (the caller is the only sender) and no reconnect (a lost transport is reported, and the caller opens a fresh session).

The session value carries the Mint/WebSocket parser state; every call returns the updated session, so fragmented replies never leave stale buffer state for the next call. Close it with close/1 — Ws.close/1 is idempotent, and a dead session is safe to close.

Credentials never enter error values: failures are sanitized codes.

Config

host, port, ns, db, user, pass, signin (:root | :namespace | :database, default :root), connect_timeout (default 2_000), reply_budget_ms (default 5_000, at most 60_000). A root session may set ns: nil — or omit ns entirely; the two are normalized to the same thing — and a root or namespace session may set db: nil or omit it.

Summary

Functions

Close the socket. Idempotent.

Connect, sign in, use ns db. Closes the socket on any failed step — including an unexpected exception — and never raises or exits: an omitted optional key and an explicit nil are the same configuration, and even a malformed top-level configuration form is a typed {:error, _}, never an exception.

Run one query. {:ok, session, results} where results is the per-statement array (callers check every status), or {:error, reason} — {:rpc_error, error_map} for a server refusal (the whole object, so Outcome.session_refusal?/1 can read it), {:lost, reason} when the request may have been sent but no reply arrived. After {:lost, _} the session is not reusable: close it.

Run one RPC method; {:ok, session, result} or {:error, reason}.

Open a session, run fun.(query) and close — the socket is closed on every exit path. query is (sql, vars) -> {:ok, results} | {:error, reason}. Returns {:ok, fun_result} or {:error, reason} when the session could not open; a body that returns {:error, r} surfaces as {:error, r}, never as {:ok, {:error, r}}.

Types

t()

@opaque t()

Functions

close(session)

@spec close(t()) :: :ok

Close the socket. Idempotent.

open(config)

@spec open(map() | keyword()) :: {:ok, t()} | {:error, term()}

Connect, sign in, use ns db. Closes the socket on any failed step — including an unexpected exception — and never raises or exits: an omitted optional key and an explicit nil are the same configuration, and even a malformed top-level configuration form is a typed {:error, _}, never an exception.

query(s, sql, vars \\ %{})

@spec query(t(), binary(), map()) :: {:ok, t(), list()} | {:error, term()}

Run one query. {:ok, session, results} where results is the per-statement array (callers check every status), or {:error, reason} — {:rpc_error, error_map} for a server refusal (the whole object, so Outcome.session_refusal?/1 can read it), {:lost, reason} when the request may have been sent but no reply arrived. After {:lost, _} the session is not reusable: close it.

rpc(s, method, params)

@spec rpc(t(), binary(), list()) :: {:ok, t(), term()} | {:error, term()}

Run one RPC method; {:ok, session, result} or {:error, reason}.

with_session(config, fun)

@spec with_session(map() | keyword(), (function() -> term())) ::
  :ok | {:ok, term()} | {:error, term()}

Open a session, run fun.(query) and close — the socket is closed on every exit path. query is (sql, vars) -> {:ok, results} | {:error, reason}. Returns {:ok, fun_result} or {:error, reason} when the session could not open; a body that returns {:error, r} surfaces as {:error, r}, never as {:ok, {:error, r}}.