JsonlPort (JsonlPort v1.1.0)

Copy Markdown View Source

Runs a program that speaks the beamports --jsonl request protocol as an Erlang port, and sends it one request at a time.

The protocol is one JSON object per line in each direction: a request {"version":1,"request_id":N,"operation":"...","payload":{...}} and a reply with the same request_id and either "ok":true,"data":... or "ok":false,"error":"...". duckport, oxiport and tantiport speak it when started with --jsonl.

Each JsonlPort process owns one program, started on the first request and restarted on the next request after it exits. Requests are serialised: a request waits until the one before it has been answered.

A request that gets no valid reply in time ends with an error, and the program is killed before the next request starts. A write that the program finishes later therefore cannot overlap the next request, but it may still have happened: the caller has to treat {:error, :uncertain_native_outcome} as unknown, not as failed.

{:ok, pid} =
  JsonlPort.start_link(
    executable: "/usr/local/bin/duckport",
    args: ["--jsonl", "--database", "/var/db/app/analytics.duckdb"]
  )

{:ok, %{"rows" => [%{"answer" => 42}]}} =
  JsonlPort.request(pid, "query", %{sql: "SELECT 42 AS answer", params: []})

Summary

Types

Why a request failed.

Functions

Returns a specification to start this module under a supervisor.

Sends one request and waits up to timeout milliseconds for its reply.

Kills the program, if it is running, once the request in progress (if any) has been answered. The next request starts it again.

Starts a process for one program. The program itself starts with the first request.

Types

error()

@type error() ::
  :missing_executable
  | :packet_limit
  | :invalid_protocol
  | :uncertain_native_outcome
  | {:native_exit, term()}
  | String.t()

Why a request failed.

  • :missing_executable - :executable is not a regular file.
  • :packet_limit - the request or the reply is larger than 16 MiB.
  • :invalid_protocol - the reply is not JSON, has another version, or answers another request.
  • :uncertain_native_outcome - no reply before the timeout; the program was killed, and the request may or may not have taken effect.
  • {:native_exit, status} - the program exited before replying.
  • a binary - the program's own error, from "ok":false; or the message of an exception raised while handling the request.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

request(server, operation, payload \\ %{}, timeout \\ 30000)

@spec request(GenServer.server(), String.t(), term(), timeout()) ::
  {:ok, term()} | {:error, error()}

Sends one request and waits up to timeout milliseconds for its reply.

payload is encoded with Jason. Returns {:ok, data} with the reply's decoded data, or {:error, reason} (see error/0).

reset(server, timeout \\ 5000)

@spec reset(GenServer.server(), timeout()) :: :ok

Kills the program, if it is running, once the request in progress (if any) has been answered. The next request starts it again.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Starts a process for one program. The program itself starts with the first request.

Options:

  • :executable - absolute path of the program (required).
  • :args - its arguments, for example ["--jsonl", "--index", path].
  • :name - registers the process, as in GenServer.start_link/3.