Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5fa49ee
docs(ingress): reorient rustdoc and README around QWP/WebSocket
jerrinot May 19, 2026
dbb9827
docs(ingress): canonical questdb.com URLs and add Rust-guide CTA
jerrinot May 20, 2026
1d6216b
docs(readme): drop "Protocol Versions" section
jerrinot May 20, 2026
a327397
docs(readme): trim Examples section to QWP-only references
jerrinot May 20, 2026
e3f5087
examples(qwp-ws): add basic, failover, and error-handling examples
jerrinot May 20, 2026
22b946a
docs(readme): surface the Rust client guide on the index page
jerrinot May 20, 2026
2fddad7
docs(ingress): drop the intro ILP framing
jerrinot May 20, 2026
3fbdb3f
docs(ingress): trim SenderBuilder /// docs; link out for depth
jerrinot May 20, 2026
54a63db
docs(ingress): rewrite Buffer dispatch comments to cover QWP/WebSocket
jerrinot May 20, 2026
dcb3430
fix(ingress): reject auto_flush_interval in connect string
jerrinot May 20, 2026
0272c76
docs(ingress): migrate private-module questdb.io URLs to questdb.com
jerrinot May 20, 2026
ee9d2b8
build(release): track versioned URLs in questdb-rs/src/ingress/mod.md
jerrinot May 20, 2026
0a175eb
Fix QWP WebSocket failover flush example
jerrinot May 20, 2026
490b274
ci(tests): skip doctests on single-transport feature matrix legs
jerrinot May 20, 2026
103607d
ci(fuzz): set +x before `##vso[task.setvariable]` JAVA_HOME echoes
jerrinot May 20, 2026
f0b3b62
docs(readme): canonical questdb.com URL for the mailing list link
jerrinot May 20, 2026
a2172cf
docs(ingress): fix ingest loop flush example
jerrinot May 20, 2026
59173c6
docs(qwp-ws): wait before polling async errors
jerrinot May 20, 2026
f145094
docs(qwp-ws): use temp dir in failover example
jerrinot May 20, 2026
72cd697
docs(qwp-ws): simplify basic example config
jerrinot May 20, 2026
4f0ca73
docs(qwp-ws): close after polling wait errors
jerrinot May 20, 2026
eee3c3f
align reconnect startup retry semantics
jerrinot May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .bumpversion.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ filename = "questdb-rs/README.md"
search = "https://github.com/questdb/c-questdb-client/blob/{current_version}/questdb-rs/"
replace = "https://github.com/questdb/c-questdb-client/blob/{new_version}/questdb-rs/"

[[tool.bumpversion.files]]
filename = "questdb-rs/src/ingress/mod.md"
search = "https://github.com/questdb/c-questdb-client/tree/{current_version}/"
replace = "https://github.com/questdb/c-questdb-client/tree/{new_version}/"

[[tool.bumpversion.files]]
filename = "questdb-rs/src/ingress/mod.md"
search = "https://github.com/questdb/c-questdb-client/blob/{current_version}/"
replace = "https://github.com/questdb/c-questdb-client/blob/{new_version}/"

[[tool.bumpversion.files]]
filename = "questdb-rs-ffi/Cargo.toml"
search = "version = \"{current_version}\""
Expand Down
8 changes: 8 additions & 0 deletions ci/run_all_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,19 @@ def main():
run_cmd('cargo', 'test', '--no-default-features',
'--features=ring-crypto,tls-native-certs,sync-sender',
'--', '--nocapture', cwd='questdb-rs')
# Narrow single-transport matrix legs: verify the library compiles
# and tests pass when a downstream consumer enables only ILP/TCP or
# only ILP/HTTP. Skip doctests via `--lib --tests --examples` -- the
# crate-level docs describe QWP/WebSocket (the default transport) so
# they assume `sync-sender-qwp-ws` is enabled, which docs.rs builds
# with anyway (see Cargo.toml `package.metadata.docs.rs`).
run_cmd('cargo', 'test', '--no-default-features',
'--features=ring-crypto,tls-webpki-certs,sync-sender-tcp',
'--lib', '--tests', '--examples',
'--', '--nocapture', cwd='questdb-rs')
run_cmd('cargo', 'test', '--no-default-features',
'--features=ring-crypto,tls-webpki-certs,sync-sender-http',
'--lib', '--tests', '--examples',
'--', '--nocapture', cwd='questdb-rs')
run_cmd('cargo', 'test', '--features=almost-all-features',
'--', '--nocapture', cwd='questdb-rs')
Expand Down
8 changes: 8 additions & 0 deletions ci/run_fuzz_pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ stages:
exit 1
fi
done
# Disable xtrace before emitting `##vso[task.setvariable]`:
# under `set -x`, bash also prints the command to stderr with
# single-quoted arguments. The Azure agent parses `##vso` from
# either stream and "last line wins" -- the stderr copy can
# win the race, leaking a trailing `'` into JAVA_HOME and
# making Maven@3 fail with `Not found jdkUserInputPath`.
# See https://questdb.com/blog/azure-pipelines-stdout-stderr-race-condition/
set +x
echo "##vso[task.setvariable variable=JAVA_HOME_17_X64]$JAVA_PATH_17"
echo "##vso[task.setvariable variable=JAVA_HOME]$JAVA_PATH_25"
displayName: "Install missing deps + resolve JDKs"
Expand Down
16 changes: 16 additions & 0 deletions ci/run_tests_pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ stages:
exit 1
fi
done
# Disable xtrace before emitting `##vso[task.setvariable]`:
# under `set -x`, bash also prints the command to stderr with
# single-quoted arguments. The Azure agent parses `##vso` from
# either stream and "last line wins" -- the stderr copy can
# win the race, leaking a trailing `'` into JAVA_HOME and
# making Maven@3 fail with `Not found jdkUserInputPath`.
# See https://questdb.com/blog/azure-pipelines-stdout-stderr-race-condition/
set +x
echo "##vso[task.setvariable variable=JAVA_HOME_17_X64]$JAVA_PATH_17"
echo "##vso[task.setvariable variable=JAVA_HOME]$JAVA_PATH_25"
displayName: "Install missing deps + resolve JDKs"
Expand Down Expand Up @@ -327,6 +335,14 @@ stages:
exit 1
fi
done
# Disable xtrace before emitting `##vso[task.setvariable]`:
# under `set -x`, bash also prints the command to stderr with
# single-quoted arguments. The Azure agent parses `##vso` from
# either stream and "last line wins" -- the stderr copy can
# win the race, leaking a trailing `'` into JAVA_HOME and
# making Maven@3 fail with `Not found jdkUserInputPath`.
# See https://questdb.com/blog/azure-pipelines-stdout-stderr-race-condition/
set +x
echo "##vso[task.setvariable variable=JAVA_HOME_17_X64]$JAVA_PATH_17"
echo "##vso[task.setvariable variable=JAVA_HOME]$JAVA_PATH_25"
displayName: "Install missing deps + resolve JDKs"
Expand Down
11 changes: 7 additions & 4 deletions doc/QWP_WEBSOCKET_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ All code is under `questdb-rs/src/ingress/sender/`:

Public configuration lives in `ingress/conf.rs`: `QwpWsConfig`, `SfDurability`
(`Memory`; `Flush` and `Append` are parsed but currently rejected),
`QwpWsInitialConnectMode` (`Off | Sync | Async`, default `Off`).
`QwpWsInitialConnectMode` (`Off | Sync | Async`; effective default `Sync`
when any `reconnect_*` knob is explicitly configured, otherwise `Off`).

---

Expand Down Expand Up @@ -450,9 +451,11 @@ counts after disruptions must use QuestDB table-level dedup
(`DEDUP UPSERT KEYS(...)`); this matches the Java client's documented
behavior.

`initial_connect_retry` is `QwpWsInitialConnectMode` (`Off | Sync | Async`,
default `Off`). `Async` lets the runner start before the first connection
succeeds; `Sync` retries inline; `Off` fails fast on the first attempt.
`initial_connect_retry` is `QwpWsInitialConnectMode` (`Off | Sync | Async`).
When unset, the effective default is `Sync` if any `reconnect_*` knob is
explicitly configured, otherwise `Off`. `Async` lets the runner start before
the first connection succeeds; `Sync` retries inline; `Off` fails fast on the
first attempt.

### 6.3 Error visibility

Expand Down
12 changes: 12 additions & 0 deletions questdb-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,15 @@ required-features = ["sync-sender-http", "ndarray", "bigdecimal"]
[[example]]
name = "qwp_ws_unified_sfa_bench"
required-features = ["sync-sender-qwp-ws"]

[[example]]
name = "qwp_ws_basic"
required-features = ["sync-sender-qwp-ws"]

[[example]]
name = "qwp_ws_failover"
required-features = ["sync-sender-qwp-ws"]

[[example]]
name = "qwp_ws_error_handling"
required-features = ["sync-sender-qwp-ws"]
201 changes: 108 additions & 93 deletions questdb-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,141 +3,156 @@
Official Rust client for [QuestDB](https://questdb.io/), an open-source SQL
database designed to process time-series data, faster.

The client library is designed for fast ingestion of data into QuestDB. It
supports three transports: the InfluxDB Line Protocol (ILP) over HTTP
(recommended) or TCP, and the QuestDB Wire Protocol (QWP) over UDP for
high-throughput ingestion on trusted networks.

* [QuestDB Database docs](https://questdb.io/docs/)
* [Docs on InfluxDB Line Protocol](https://questdb.io/docs/reference/api/ilp/overview/)
The client library is designed for fast ingestion of data into QuestDB
over the **QuestDB Wire Protocol over WebSocket (QWP/WebSocket)**: a
columnar binary protocol with explicit asynchronous server
acknowledgements, multi-host failover, optional on-disk durability, and a
structured error model.

* [Rust client documentation](https://questdb.com/docs/connect/clients/rust/) —
the full guide on the QuestDB documentation site.
* [`ingress` module docs](https://docs.rs/questdb-rs/6.1.0/questdb/ingress/) —
this crate's API reference: protocol details, configuration parameters,
and patterns.
* [QuestDB Database docs](https://questdb.com/docs/) — the wider
documentation site (SQL reference, deployment, operations).

## Transports

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

* `http::addr=...` / `https::addr=...` — request-response, errors returned
to the client, supports authentication and TLS. Recommended for most
workloads.
* `tcp::addr=...` / `tcps::addr=...` — streaming, legacy; errors cause
server-side disconnect and surface only in server logs.
* `qwpudp::addr=...` — best-effort UDP datagrams (IPv4-only); no
acknowledgements, no authentication, no TLS, no transactional guarantees.
See the [`ingress`](https://docs.rs/questdb-rs/latest/questdb/ingress/)
module docs (in particular `Protocol::QwpUdp`) for semantics and
configuration parameters.

## Protocol Versions

The library supports the following ILP protocol versions. These apply to
ILP/HTTP and ILP/TCP only — QWP/UDP 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 InfluxDB Line Protocol (ILP) | All QuestDB versions |
| **2** | 64-bit floats sent as binary, adds n-dimentional arrays | 9.0.0+ (2023-10-30) |

**Note**: QuestDB server version 9.0.0 or later is required for `protocol_version=2` support.
* `ws::addr=...` / `wss::addr=...` — **QWP/WebSocket**, the recommended
transport. Columnar binary frames, asynchronous server ACKs with explicit
frame-sequence-number watermarks, multi-host failover, optional
store-and-forward durability. Supports HTTP basic and bearer-token auth,
plus TLS via `wss::`. The long-form aliases `qwpws::` / `qwpwss::` are
also accepted.
* `http::addr=...` / `https::addr=...` — **ILP/HTTP** (legacy).
Request-response with error returns and per-request retry, no
asynchronous ACKs or multi-host failover. Suitable for existing
deployments and one-shot batches.
* `tcp::addr=...` / `tcps::addr=...` — **ILP/TCP** (legacy). Streaming with
no error reporting to the client; the server logs failures and silently
disconnects. Lowest overhead, lowest observability.

See the [`ingress` module docs](https://docs.rs/questdb-rs/6.1.0/questdb/ingress/)
for the full configuration-parameter reference, including the
QWP-specific keys (`sf_dir`, `sender_id`, `reconnect_*`,
`request_durable_ack`, `qwp_ws_progress`, `max_in_flight`).

## Quick Start

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

```bash
cargo add questdb-rs
```

Then you can try out this quick example, which connects to a QuestDB server
running on your local machine:
A minimal ingest using QWP/WebSocket:

```rust ignore
use questdb::{
Result,
ingress::{
Sender,
Buffer,
TimestampNanos}};
ingress::{Sender, 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(())
let mut sender = Sender::from_conf("ws::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)?
.at(TimestampNanos::now())?;
sender.flush(&mut buffer)?;
sender.close_drain()?;
Ok(())
}
```

`flush` returns once the frame has been appended to the local publication
log. `close_drain` waits for already-published frames to be acknowledged
by the server (bounded by `close_flush_timeout_millis`, default 5s) before
the sender is dropped.

## Docs

Most of the client documentation is on the
[`ingress`](https://docs.rs/questdb-rs/6.1.0/questdb/ingress/) module page.
This crate's API reference is on the
[`ingress`](https://docs.rs/questdb-rs/6.1.0/questdb/ingress/) module
page: configuration keys, the QWP error model, FSN-based completion,
progress modes, multi-host failover, store-and-forward, authentication,
TLS, the `Buffer` API, and the legacy ILP transports.

For the full Rust client guide — failover, store-and-forward operations,
migration from ILP, worked examples — see the
[Rust client documentation](https://questdb.com/docs/connect/clients/rust/)
on the QuestDB documentation site.

## Examples

A selection of usage examples is available in the [examples directory](https://github.com/questdb/c-questdb-client/tree/6.1.0/questdb-rs/examples):
QWP/WebSocket examples in the
[examples directory](https://github.com/questdb/c-questdb-client/tree/6.1.0/questdb-rs/examples):

| Example | Description |
|---------|-------------|
| [`basic.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/basic.rs) | Minimal TCP ingestion example; shows basic row and array ingestion. |
| [`auth.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/auth.rs) | Adds authentication (user/password, token) to basic ingestion. |
| [`auth_tls.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/auth_tls.rs) | Like `auth.rs`, but uses TLS for encrypted TCP connections. |
| [`from_conf.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/from_conf.rs) | Configures client via connection string instead of builder pattern. |
| [`from_env.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/from_env.rs) | Reads config from `QDB_CLIENT_CONF` environment variable. |
| [`http.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/http.rs) | Uses HTTP transport and demonstrates array ingestion with `ndarray`. |
| [`protocol_version.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/protocol_version.rs) | Shows protocol version selection and feature differences (e.g. arrays). |
| [`qwp_ws_basic.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/qwp_ws_basic.rs) | Minimal QWP/WebSocket ingestion: build a sender, flush a row, `close_drain`. |
| [`qwp_ws_failover.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/qwp_ws_failover.rs) | Multi-host `addr=` list with on-disk store-and-forward and `sender_id`. |
| [`qwp_ws_error_handling.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/qwp_ws_error_handling.rs) | Server-error handling via `poll_qwp_ws_error` and via the `qwp_ws_error_handler` callback. |
| [`qwp_ws_unified_sfa_bench.rs`](https://github.com/questdb/c-questdb-client/blob/6.1.0/questdb-rs/examples/qwp_ws_unified_sfa_bench.rs) | Throughput benchmark with store-and-forward. |

Build and run any of these with, for example:

```sh
cargo run --example qwp_ws_basic --features sync-sender-qwp-ws
```

## 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`.
The crate provides optional features. Enable them via Cargo's
`--features` flag or in your `Cargo.toml`.

### Default features
- **sync-sender**: Enables both `sync-sender-tcp` and `sync-sender-http`.
- **sync-sender-tcp**: Enables ILP/TCP (legacy). Depends on the `socket2` crate.
- **sync-sender-http**: Enables ILP/HTTP support. Depends on the `ureq` crate.
- **tls-webpki-certs**: Uses a snapshot of the [Common CA Database](https://www.ccadb.org/) 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

- **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.
* **sync-sender** — umbrella for the synchronous sender transports.
* **sync-sender-qwp-ws** — QWP over WebSocket. Recommended transport.
* **sync-sender-http** — ILP over HTTP (legacy).
* **sync-sender-tcp** — ILP over TCP (legacy).
* **tls-webpki-certs** — bundled TLS roots from the
[Common CA Database](https://www.ccadb.org/) via
[`webpki-roots`](https://crates.io/crates/webpki-roots).
* **ring-crypto** — default TLS crypto backend, via the `ring` crate.

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

> See the `Cargo.toml` for the full list and details on feature interactions.
* **chrono_timestamp** — accept timestamps as `chrono::DateTime`.
* **tls-native-certs** — OS-provided root TLS certificates via
`rustls-native-certs`.
* **insecure-skip-verify** — disable TLS verification (test-only,
not for production).
* **ndarray** — integrate with the `ndarray` crate for N-dimensional
arrays. Without this feature, slices and custom types via the
`NdArrayView` trait still work.
* **aws-lc-crypto** — alternative TLS backend via `aws-lc-rs`. Mutually
exclusive with `ring-crypto`.
* **almost-all-features** — dev/test convenience: enables most features
except mutually exclusive crypto backends.

> See `Cargo.toml` for the full list and feature interactions.

## C, C++ and Python APIs

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

* This project's [GitHub page](https://github.com/questdb/c-questdb-client)
for the C and C++ API.
* [Python bindings](https://github.com/questdb/py-questdb-client).
* [c-questdb-client](https://github.com/questdb/c-questdb-client) — the C
and C++ API.
* [py-questdb-client](https://github.com/questdb/py-questdb-client) —
Python bindings.

## Community

If you need help, have additional questions or want to provide feedback, you
may find us on [Slack](https://slack.questdb.io/).

You can also sign up to our [mailing list](https://questdb.io/community/) to
get notified of new releases.
If you need help, have questions, or want to provide feedback,
[join us on Slack](https://slack.questdb.io/). You can also sign up to
the [mailing list](https://questdb.com/community/) to get notified of new
releases.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading