questdb-rs 7.0.0

QuestDB Client Library for Rust
Documentation

QuestDB Client Library for Rust

Official Rust client for QuestDB, an open-source SQL database designed to process time-series data, faster.

The client library is designed for fast ingestion of data into QuestDB, and for querying it back out.

Its centrepiece is the QuestDB Wire Protocol (QWP) over WebSocket: QuestDB's native binary columnar protocol, covering both directions. Writes are acknowledged per flush and go through QuestDb — a thread-safe connection pool with automatic reconnect — as rows, columns, Apache Arrow RecordBatches or Polars DataFrames. Multi-endpoint failover requires QuestDB Enterprise. Queries stream result sets back over the same protocol as columnar batches, RecordBatches or DataFrames.

The InfluxDB Line Protocol (ILP) over HTTP or TCP, and QWP over UDP, are also supported for ingestion.

Version 7.0.0 requires Rust 1.91.1. QWP over WebSocket requires QuestDB 10.0 or newer.

Transports

The transport is selected by the scheme in the configuration string:

  • ws::addr=... / wss::addr=... (also ws:: / wss::) — QWP over WebSocket, in both directions. For ingestion (QuestDb::connect): binary columnar frames with per-flush acknowledgements, a thread-safe connection pool with automatic reconnect, and row, column, Arrow RecordBatch and Polars DataFrame input. Multi-endpoint failover requires QuestDB Enterprise. For queries (Reader::from_conf or QuestDb::borrow_reader): SQL execution with results streamed back as columnar batches. Requires QuestDB 10.0+.
  • http::addr=... / https::addr=... — ILP request-response, errors returned to the client, supports authentication and TLS.
  • tcp::addr=... / tcps::addr=... — ILP streaming, legacy; errors cause server-side disconnect and surface only in server logs.
  • udp::addr=... — best-effort UDP datagrams (IPv4-only); no acknowledgements, no authentication, no TLS, no transactional guarantees. See the ingress module docs (in particular Protocol::Udp) for semantics and configuration parameters.

ILP Protocol Versions

The library supports the following ILP protocol versions. These apply to ILP/HTTP and ILP/TCP only — QWP uses its own wire format and is not versioned through this mechanism.

  • If you use HTTP and protocol_version=auto or unset, the library will automatically detect the server's latest supported protocol version and use it (recommended).
  • If you use TCP, you can specify the protocol_version=N parameter when constructing the Sender object (TCP defaults to protocol_version=1).
Version Description Server Compatibility
1 Over HTTP it's compatible with InfluxDB Line Protocol (ILP) All QuestDB versions
2 64-bit floats sent as binary, adds n-dimensional arrays 9.0.0+ (2025-07-11)

Note: QuestDB server version 9.0.0 or later is required for protocol_version=2 support.

Quick Start

To start using questdb-rs, add it as a dependency of your project:

cargo add questdb-rs

QWP: the QuestDb connection pool

QuestDb is the entry point for QWP/WebSocket (QuestDB 10.0+): one thread-safe pool covering both writes and reads. A Polars round trip (with the polars feature):

use questdb::{QuestDb, ingress::polars::PolarsIngestOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = QuestDb::connect("ws::addr=localhost:9000;")?;

    // One call: stream the DataFrame in columnar batches, wait for the ack.
    db.flush_polars_dataframe("trades", &df, &PolarsIngestOptions::new())?;

    // Query it back over the same pool.
    let back = db
        .borrow_reader()?
        .execute("SELECT * FROM trades WHERE amount > 0.001")?
        .fetch_all_polars()?;
    println!("{back}");
    Ok(())
}

The pool's main handles:

  • db.flush_polars_dataframe(table, &df, &options) — one-call DataFrame ingestion (polars feature).
  • db.flush_arrow_batch(table, &batch, ts_column, overrides, ack) — one-call Arrow RecordBatch ingestion (arrow feature).
  • db.borrow_sender() — unified ingestion: publish row-built Buffer payloads (table(..).symbol(..).column_f64(..).at(..)) or fill and reuse a columnar Chunk; the same lease also accepts Arrow batches.
  • db.borrow_reader() — run SQL and stream the result set back.

Handles return to the pool on drop, and the pool reconnects to the configured endpoint transparently. With QuestDB Enterprise it also fails over across addr=host-a:9000,host-b:9000 endpoint lists.

To keep the ingest publication log across reconnects and producer-process restarts, configure sf_dir. Its default sf_durability=memory mode relies on the OS page cache and does not protect against host power loss. Periodic durability adds background disk checkpoints:

ws::addr=localhost:9000;sf_dir=/var/lib/my-app/questdb-sf;sender_id=orders;sf_durability=periodic;sf_sync_interval_millis=5000;

The interval defaults to 5000 ms in periodic mode and is a target cadence: runner scheduling and storage-sync latency add to the actual recovery window. Publication can see ordinary backpressure at a segment boundary until the segment has been checkpointed. This local durability is independent of the QuestDB Enterprise server barrier selected by request_durable_ack=on; use both when end-to-end durability is required.

A standalone Reader::from_conf("ws::addr=...") gives the query side without a pool (sync-reader-qwp-ws feature), yielding results as native columnar batches, Arrow RecordBatches (cursor.next_arrow_batch()) or Polars DataFrames (cursor.fetch_all_polars()).

ILP over HTTP

use questdb::{
    Result,
    ingress::{
        Sender,
        Buffer,
        TimestampNanos}};

fn main() -> Result<()> {
   let mut sender = Sender::from_conf("http::addr=localhost:9000;")?;
  let mut buffer = sender.new_buffer();
   buffer
       .table("trades")?
       .symbol("symbol", "ETH-USD")?
       .symbol("side", "sell")?
       .column_f64("price", 2615.54)?
       .column_f64("amount", 0.00044)?

       // Array ingestion (QuestDB 9.0.0+). Slices and ndarray supported through trait
       .column_arr("price_history", &[2615.54f64, 2615.10, 2614.80])?
       .column_arr("volatility", &ndarray::arr1(&[0.012f64, 0.011, 0.013]).view())?
       .at(TimestampNanos::now())?;
   sender.flush(&mut buffer)?;
   Ok(())
}

Docs

Use the QuestDB Rust client guide for task-oriented documentation. The exact API contract is on docs.rs: start with QuestDb, then see the ingress and egress modules.

Examples

A selection of usage examples is available in the examples directory:

Example Description
basic.rs Minimal TCP ingestion example; shows basic row and array ingestion.
auth.rs Adds authentication (user/password, token) to basic ingestion.
auth_tls.rs Like auth.rs, but uses TLS for encrypted TCP connections.
from_conf.rs Configures client via connection string instead of builder pattern.
from_env.rs Reads config from QDB_CLIENT_CONF environment variable.
http.rs Uses HTTP transport and demonstrates array ingestion with ndarray.
protocol_version.rs Shows protocol version selection and feature differences (e.g. arrays).
qwp_ws_chunk_and_query.rs Shares one QuestDb pool between concurrent columnar ingestion and query workers.
qwp_ws_l1_quotes.rs Columnar ingestion over QWP/WebSocket via the QuestDb connection pool.
qwp_egress_read.rs Runs a SQL query and streams the result set over QWP/WebSocket.
polars.rs Round trip: ingests a Polars DataFrame and queries it back as one.

Crate features

The crate provides several optional features to enable additional functionality. You can enable features using Cargo's --features flag or in your Cargo.toml.

Default features

  • sync-sender: Enables sync-sender-tcp, sync-sender-http and sync-sender-qwp-ws (ingestion).
  • sync-reader: Enables sync-reader-qwp-ws and sync-reader-zstd (queries). Querying is first-class, on by default.
  • sync-sender-tcp: Enables ILP/TCP (legacy). Depends on the socket2 crate.
  • sync-sender-http: Enables ILP/HTTP support. Depends on the ureq crate.
  • sync-sender-qwp-ws: Enables unified QWP/WebSocket ingestion through the QuestDb pool.
  • sync-reader-qwp-ws: Enables QWP/WebSocket queries (Reader / Cursor).
  • sync-reader-zstd: Enables zstd decompression of query result batches.
  • tls-webpki-certs: Uses a snapshot of the Common CA Database as root TLS certificates. Depends on the webpki-roots crate.
  • ring-crypto: Uses the ring crate as the cryptography backend for TLS (default crypto backend).

Optional features

  • arrow: Apache Arrow integration in both directions — ingest RecordBatches, read query results as RecordBatches. Also available as the single-direction arrow-ingress / arrow-egress features.

  • polars: Polars integration in both directions — ingest DataFrames, read query results as DataFrames. Also available as polars-ingress / polars-egress.

  • chrono-timestamp: Allows specifying timestamps as chrono::DateTime objects. Depends on the chrono crate.

  • tls-native-certs: Uses OS-provided root TLS certificates for secure connections. Depends on the rustls-native-certs crate.

  • insecure-skip-verify: Allows skipping verification of insecure certificates (not recommended for production).

  • ndarray: Enables integration with the ndarray crate for working with n-dimensional arrays. Without this feature, you can still send slices or implement custom array types via the NdArrayView trait.

  • aws-lc-crypto: Uses aws-lc-rs as the cryptography backend for TLS. Mutually exclusive with the ring-crypto feature.

  • almost-all-features: Convenience feature for development and testing. Enables most features except mutually exclusive crypto backends.

See the Cargo.toml for the full list and details on feature interactions.

C, C++ and Python APIs

This crate is also exposed as a C and C++ API and in turn exposed to Python.

Community

If you need help, have additional questions or want to provide feedback, you may find us on Slack.

You can also sign up to our mailing list to get notified of new releases.