Supervises a local DuckDB Quack server process with MuonTrap.
This is a convenience for local development, tests, demos, and notebooks. It
starts the external duckdb executable and serves DuckDB's Quack HTTP
protocol. It is not an embedded DuckDB driver and is not required when your
Quack server runs elsewhere.
children =
QuackDB.Server.child_specs(
server: [name: MyApp.DuckDB, duckdb: :managed, endpoint: "quack:localhost:9494"],
client: [name: MyApp.QuackDB, pool_size: 5]
)Use duckdb: :managed to download and cache DuckDB's official CLI binary via
QuackDB.Binary. Pass duckdb: "/path/to/duckdb" or set
QUACKDB_BINARY_PATH when you want to provide the executable yourself.
child_specs/1 generates one shared random token when neither side provides
:token, then injects the same token and URI into the server and client specs.
By default the server runs DuckDB directly under MuonTrap with -interactive
so the process stays alive after quack_serve/2 starts. It installs and loads
the quack extension before serving, unless load_quack?: false is set. Boot
SQL is written to an Elixir-owned temporary init file by default so generated
local tokens are not embedded in the operating-system argument vector.
Startup waits until the Quack endpoint is ready. For the default DuckDB CLI
command, readiness is detected from the quack_serve/2 result row printed to
stdout. :poll_interval is only the fallback probe interval for custom daemon
output handling or custom commands that do not expose that row. Probes run in
a cancellable task so daemon exits and output remain visible while HTTP waits;
the startup deadline also cancels an in-flight probe.
Startup failures report :server_start_failed, :server_start_timeout, or
:database_locked for recognized DuckDB file-lock conflicts. Error metadata
includes :database, :uri, :last_error, an :output_tail of at most 8 KiB,
and :exit_reason when the daemon exits. The retained output redacts the
server token, replaces invalid UTF-8, and truncates on a character boundary.
Explicitly configured logger callbacks still receive original output.
Capture is best-effort, newline-delimited MuonTrap output; disabling
stderr forwarding can omit diagnostics. Other secrets in custom boot output
are not automatically redacted.
Stopping this process signals the daemon; it does not run CHECKPOINT or
guarantee an immediately copyable database file. Preserve a remaining WAL for
recovery. Quiesce writers and explicitly checkpoint before a single-file copy,
then stop clients and the supervised server and verify OS-process termination.
A supervised server is a permanent child: use Supervisor.terminate_child/2
rather than GenServer.stop/1 to stop it without automatic restart.
Summary
Functions
Returns a specification to start this module under a supervisor.
Returns matching server and client child specs, in startup order.
Returns the local MuonTrap wrapper's OS PID, not the DuckDB child PID.
Types
@type option() :: {:name, GenServer.name()} | {:duckdb, String.t() | :managed} | {:duckdb_options, keyword()} | {:database, String.t()} | {:endpoint, String.t()} | {:uri, String.t()} | {:token, String.t()} | {:load_quack?, boolean()} | {:install_quack?, boolean()} | {:boot_sql, String.t()} | {:boot_sql_source, :init_file | :cmd} | {:settings, keyword(QuackDB.SQL.parameter())} | {:global_settings, keyword(QuackDB.SQL.parameter())} | {:serve_options, keyword()} | {:recovery_mode, :no_wal_writes | String.t()} | {:attach_as, atom() | String.t()} | {:wait, boolean()} | {:wait_timeout, timeout()} | {:poll_interval, pos_integer()} | {:daemon_options, Keyword.t()} | {:daemon_command, {String.t(), [String.t()]}}
Functions
@spec child_spec([option()]) :: Supervisor.child_spec()
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec child_specs(keyword()) :: [Supervisor.child_spec()]
Returns matching server and client child specs, in startup order.
:server is a list of Server options. :client accepts a keyword list for a
QuackDB pool, or {module, options} for another client such as an Ecto Repo.
The client must accept :uri and :token options. URI/token precedence is
server, then client, then generated defaults. Both specs retain the generated
credentials across restarts.
alias QuackDB.Server
children =
Server.child_specs(
server: [name: MyApp.DuckDB],
client: {MyApp.Repo, pool_size: 2}
)A :rest_for_one supervisor can restart the client when its server restarts
and invalidates existing sessions.
@spec info(GenServer.server()) :: map()
@spec os_pid(GenServer.server()) :: non_neg_integer() | :error
Returns the local MuonTrap wrapper's OS PID, not the DuckDB child PID.
This does not identify the process holding a conflicting database file lock.
@spec start_link([option()]) :: GenServer.on_start()
@spec statistics(GenServer.server()) :: map()
@spec token(GenServer.server()) :: String.t()
@spec uri(GenServer.server()) :: String.t()