Skip to main content

QuestDb

Struct QuestDb 

Source
pub struct QuestDb { /* private fields */ }

Implementations§

Source§

impl QuestDb

Source

pub fn flush_polars_dataframe<'t, T>( &self, table: T, df: &DataFrame, options: &PolarsIngestOptions<'_>, ) -> Result<()>
where T: TryInto<TableName<'t>>, Error: From<T::Error>,

Flush a polars DataFrame to table in a single call.

This is the recommended DataFrame ingestion entry point: it borrows a direct column sender from the pool, drives the whole frame, and returns the sender to the pool on completion (or error) — callers never handle a sender. Internally this uses the same direct columnar path as Arrow ingestion, with checkpoint commits and retry owned by this method.

table accepts anything convertible into a TableName (a bare &str works). options (PolarsIngestOptions) carries the optional designated-timestamp column, per-column wire-type overrides and batch size.

Commit, checkpoint and at-least-once failover-replay semantics are unchanged from the underlying driver: the call owns the commit (the replay boundary), re-driving the uncommitted tail onto a live endpoint across a transient ErrorCode::FailoverRetry within the pool’s configured reconnect budget, and returns only once the whole df is committed. A re-driven tail can produce duplicate rows unless the destination table has DEDUP UPSERT KEYS covering them.

Source§

impl QuestDb

Source

pub fn connect(conf: &str) -> Result<Self>

Open a pool against conf.

The connect string must use a QWP/WebSocket schema (ws:: / wss:: / ws:: / wss::). Pool-specific keys are recognised:

KeyDefaultMeaning
sender_pool_min1Warm minimum of the ingestion pool, pre-opened at connect unless lazy_connect=true.
sender_pool_max4Hard cap on the ingestion pool; the direct column-sender pool used by DataFrame ingestion is capped separately at the same value.
query_pool_min1 (0 when lazy)Warm minimum of the reader pool, pre-opened at connect unless lazy_connect=true.
query_pool_max4Hard cap on the reader pool.
acquire_timeout_ms5000How long an at-cap borrow waits for a return before failing; 0 fails immediately.
idle_timeout_ms60000Above-minimum idle connections are closed after this long.
pool_reapautoauto runs a background reaper; manual requires reap_idle.
lazy_connectfalseTolerate a down server at startup: connect opens nothing, senders buffer and connect in the background, readers connect on first borrow.

Key names and defaults match the Java client’s QuestDBBuilder; the Java-only lifecycle keys (max_lifetime_ms, housekeeper_interval_ms, query_close_timeout_ms) have no counterpart here — the reaper tick and close_flush_timeout own those responsibilities.

Self::borrow_sender is always store-and-forward (in-memory when no sf_dir, disk-backed when set). Setting sf_dir gives every pooled sender its own slot directory, minted from the configured sender_id base as <base>-ingest-<index>. Those <sender_id>-ingest-* directories are reserved for this pool namespace under sf_dir; use a unique sender_id for each pool that shares an sf_dir. sender_pool_min / sender_pool_max apply to this unified ingestion pool. At cap, borrows return InvalidApiCall except disk-backed ingestion borrows can wait up to close_flush_timeout (default 5s) while an in-flight slot close releases its lock. For a plain pipelined (non-SF) connection — used by DataFrame ingestion — see Self::borrow_direct_column_sender.

Startup matches the Java client. By default connect is eager: it pre-opens sender_pool_min ingest senders — honoring only an explicitly set initial_connect_retry: off (the default) fails fast, sync retries within the reconnect budget, async connects in the background — and query_pool_min readers, which have no retry mode and always connect synchronously, failing fast; sync governs only the ingest side. Reconnect-to-sync promotion applies only to standalone SenderBuilder::build; pools honor only an explicitly set mode. Bare initial_connect_retry=async is likewise not a non-blocking startup while query_pool_min > 0; lazy_connect=true is. Growth borrows beyond the minimum honor the same rules.

With lazy_connect=true the pool tolerates a down server at startup: connect performs no blocking network I/O, query_pool_min defaults to 0 (readers connect lazily on first use), and every ingest borrow creates its local store-and-forward producer immediately and connects in the background, so the borrower can buffer while the server is absent. An explicit blocking initial_connect_retry alongside lazy_connect=true is rejected as a configuration conflict. In disk-backed store-and-forward mode, either variant may pre-open parked recovery senders whose initial connect and replay run in the background. Direct senders open their transport on first borrow. sender_pool_min / query_pool_min are the warm minimums the reaper keeps.

§Store-and-forward durability

Disk store-and-forward (sf_dir) writes queued frames and their symbol dictionary to disk but does not fsync — the data is page-cache durable, matching the standalone QWP/WebSocket sender. That survives a process / JVM crash (unacked frames replay on the next borrow / recovery), but not a host / power crash, which can lose or tear unflushed pages. A recovery that finds a torn symbol dictionary (or a frame whose dictionary cannot be re-registered on the fresh server) fails loudly with a terminal, resend-required error — StoreResendRequired, a code distinct from the transient SocketError you would retry, so callers can branch on it directly. The sender’s own reconnect/failover loops treat it as terminal (they stop) rather than retrying it to their deadline. Those rows must be re-ingested from their source, not retried in place. In-memory store-and-forward (no sf_dir) has no cross-restart durability.

Source

pub fn connect_with_listener( conf: &str, listener: ConnectionListener, inbox_capacity: usize, ) -> Result<Self>

Self::connect with a connection lifecycle listener. Events (see ConnectionEventKind) are delivered on a dedicated dispatcher thread through a bounded inbox — a slow listener can never stall connect, publish, or reconnect paths; on overflow the oldest undelivered event is dropped (counted by Self::connection_events_dropped).

All direct and store-and-forward senders share this one source and inbox. Concurrent emitters are serialized into the inbox in emission order. inbox_capacity == 0 selects the default (64).

The listener is registered before the pool opens anything, so it observes every transition — including the initial Connected of disk recovery senders pre-opened by connect itself. This is the only way to attach a listener to a pool: registration after connect would race those recovery connects and could miss them.

Source

pub fn connect_with_handlers( conf: &str, handlers: ConnectHandlers, ) -> Result<Self>

Self::connect with any combination of a connection lifecycle listener (see Self::connect_with_listener) and a server-rejection handler.

The rejection handler receives every server rejection any of the pool’s store-and-forward connections records — including rejections for frames whose lease was already returned — on a dedicated dispatcher thread through a bounded inbox (overflow drops the oldest event, counted by Self::rejection_events_dropped). Without a handler every rejection is logged instead: warn for retriable policies (the frames are replayed, not lost), error for terminal ones. Use the handler for dead-lettering, alerting, and metrics; producer-side abort logic belongs with the terminal error raised by the sender calls themselves.

Source

pub fn new_buffer(&self) -> Buffer

Create a caller-owned QWP/WebSocket row buffer using this pool’s configured table/column name limit. The buffer is independent of any particular sender borrow and may be filled or moved before it is published by a store-and-forward sender from this pool.

Source

pub fn borrow_sender(&self) -> Result<BorrowedSender<'_>>

Borrow a sender.

Selection: pop the most-recently-returned slot from the free list; failing that, open a new connection if we are below sender_pool_max; failing that, in disk-backed store-and-forward mode only, wait up to close_flush_timeout (default 5s) while an in-flight slot close releases its lock; failing that, wait up to acquire_timeout_ms for a return (acquire_timeout_ms=0 fails fast); failing that, return InvalidApiCall.

A borrow that opens a new connection honors initial_connect_retry: off (the default) connects synchronously and fails fast, sync retries within the reconnect budget before returning. Under lazy_connect=true the connection starts in the background instead, so the borrow succeeds even while the server is away; see Self::connect.

Source

pub fn flush_arrow_batch<'t, T>( &self, table: T, batch: &RecordBatch, timestamp_column: Option<ColumnName<'_>>, overrides: &[ArrowColumnOverride<'_>], ack_level: Option<AckLevel>, ) -> Result<()>
where T: TryInto<TableName<'t>>, Error: From<T::Error>,

Flush a single Arrow RecordBatch to table in one call.

This is the recommended entry point for one-off Arrow ingestion: it borrows a direct column sender from the pool, publishes the batch as a commit boundary, waits for the server Ok ack, and returns the sender to the pool — callers never handle a sender.

timestamp_column selects where each row’s designated timestamp comes from:

  • Some(col) — source it from the named Timestamp(_) column of batch (mirrors the old flush_arrow_batch_at_column).
  • None — let the server stamp each row on arrival (mirrors the old flush_arrow_batch_at_now).

overrides carries per-column wire-type hints (e.g. promote a UTF-8 column to SYMBOL, or a UInt32 to IPv4); pass &[] when the Arrow schema is self-describing.

ack_level chooses how far the call blocks before returning:

  • None — wait for the connect string’s default, i.e. the same level the store-and-forward senders use: AckLevel::Durable when the Enterprise-only durable mode is enabled with request_durable_ack=on, otherwise AckLevel::Ok.
  • Some(level) — wait for exactly level. AckLevel::Durable requires QuestDB Enterprise and request_durable_ack=on; otherwise the call is rejected with ErrorCode::InvalidApiCall.

The call publishes the batch as a commit boundary and blocks until the resolved acknowledgement level is reached. An Ok acknowledgement confirms server acceptance; only the Enterprise durable level confirms durable coverage. On a transient ErrorCode::FailoverRetry it surfaces the error rather than replaying (the batch is fully owned by the caller, so retrying is a plain re-call); the DataFrame path (Self::flush_polars_dataframe) re-drives automatically instead.

Source

pub fn reap_idle(&self) -> usize

Manually reap idle connections.

Closes free-list entries that have been idle longer than idle_timeout_ms, never shrinking the sender pools below sender_pool_min or the reader pool below query_pool_min. Returns the number of connections closed.

Under the default pool_reap=auto, a background thread invokes this logic periodically and this call is harmless. Under pool_reap=manual, callers that want shrinking must invoke this on their own cadence.

Source

pub fn connection_events_dropped(&self) -> u64

Total connection events discarded by the listener inbox’s drop-oldest policy. 0 when no listener is registered.

Source

pub fn connection_events_delivered(&self) -> u64

Total connection events delivered to the listener. 0 when no listener is registered.

Source

pub fn rejection_events_delivered(&self) -> u64

Total server rejections delivered to the rejection handler (or to the default log handler when none was registered).

Source

pub fn rejection_events_dropped(&self) -> u64

Total server rejections discarded by the rejection handler inbox’s drop-oldest policy. Always 0 without a registered handler: the default log handler has no inbox.

Source

pub fn close(self)

Close the pool: stop the reaper (if any), reject future borrows, drop all idle connections, and consume self.

FFI-owned outstanding handles remain return/drop-safe through their internal pool reference, but return after close drops the connection instead of recycling it.

Drop has the same effect; close exists for parity with the C ABI (where Drop is not available) and to give callers a place to handle any reaper-join errors explicitly in the future.

Source

pub fn borrow_reader(&self) -> Result<BorrowedReader<'_>>

Borrow a query Reader from the egress pool.

Egress companion to Self::borrow_sender: pulls a Reader from the pool’s reader free list, lazily opening a fresh connection (via Reader::from_conf on the original connect string) when the free list is empty and the pool is below query_pool_max. The reader pool is lazily grown and capped independently of the two ingestion pools, so heavy ingest can’t starve queries and vice versa (the combined live-connection ceiling across all three pools is 2 * sender_pool_max + query_pool_max).

Borrow at the cap waits up to acquire_timeout_ms for a return (acquire_timeout_ms=0 fails fast), then returns InvalidApiCall.

The returned BorrowedReader derefs to Reader, so the usual prepare / execute cursor flow works unchanged, and returns the reader to the pool on Drop — unless its transport has been torn down (or BorrowedReader::drop_on_return was called), in which case it is dropped and the next borrow opens a fresh one.

Like BorrowedSender, BorrowedReader is not Send or Sync: borrow one reader per worker thread from the same QuestDb.

Trait Implementations§

Source§

impl Debug for QuestDb

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for QuestDb

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V