Bier.Cancellation (bier v0.1.0)

Copy Markdown View Source

Cancels the in-flight Postgres query when the HTTP client disconnects (#82).

PostgREST cannot do this (postgrest#699, open since 2016): an accidental unfiltered DELETE keeps running server-side after the client kills the request. On the BEAM the disconnect is observable, so Bier ties it to a real backend cancel:

  1. The request's database work runs in a Task linked to the connection process, while the connection process arms the request socket (active: :once) and waits for either the task result or a socket close notification.
  2. On disconnect the task is killed. DBConnection sees its client die mid-checkout and disconnects the connection, and Postgrex's protocol disconnect sends a wire CancelRequest (with the backend key) before closing — the PostgreSQL backend aborts the query instead of running it to completion, and the pool slot is re-established clean.
  3. [:bier, :query, :cancelled] is emitted (see Bier.Telemetry) and the request falls through to Bier.Plugs.FallbackController as {:error, :client_disconnected} (a 499 nobody will receive, kept for request telemetry/logging symmetry).

Scope and limitations

  • Disconnect detection watches the raw HTTP/1 socket (Bandit / ThousandIsland, TCP and SSL). Any other adapter shape falls back to running the work inline, exactly as before this module existed.
  • The request body is fully read (Bier.Plugs.ReadBody) before any query runs, so bytes arriving mid-query are only possible from a pipelining client. Such bytes are consumed by the watcher (they cannot be pushed back) and watching stops for that request — a non-issue for REST API clients, which do not pipeline.

Summary

Functions

Run fun (a zero-arity function performing the request's database work), cancelling it if the HTTP client disconnects while it is in flight.

Functions

run(conn, config, fun)

@spec run(Plug.Conn.t(), map(), (-> result)) ::
  result | {:error, :client_disconnected}
when result: term()

Run fun (a zero-arity function performing the request's database work), cancelling it if the HTTP client disconnects while it is in flight.

Returns fun's result, or {:error, :client_disconnected} after a disconnect-triggered cancel. Raises/throws/exits from fun propagate unchanged (same class, reason, and stacktrace) so error semantics are identical to calling fun directly.