Skip to Content

Rust Client SDK

Installing Rust client SDK

cargo add oxia-client

The crate is oxia-client; the import root is oxia. The client is asynchronous and runs on the Tokio  runtime. API documentation is available at https://docs.rs/oxia-client .

Optional Cargo features (all off by default):

FeatureDescription
tlsTLS connections, including custom CA and mutual TLS — see TLS.
otelReport client metrics through OpenTelemetry — see Metrics.

Client API

Initializing the client

To use the Oxia client, create a client instance. It is cheap to clone (all clones share one set of connections, batchers and sessions), safe to share across tasks, and valid until it is explicitly closed. Every operation returns a request builder: chain options onto it and .await it directly.

use oxia::OxiaClient; #[tokio::main] async fn main() -> Result<(), oxia::OxiaError> { let client = OxiaClient::connect("localhost:6648").await?; // ... use the client ... client.close().await?; Ok(()) }

When creating the client it is possible to pass several options through the builder:

use std::time::Duration; let client = OxiaClient::builder() .service_address("localhost:6648") .namespace("my-namespace") .identity("my-client-identity") .request_timeout(Duration::from_secs(30)) .build() .await?;

Available client options (see OxiaClientBuilder):

OptionDescriptionDefault
namespaceOxia namespace to use.default
request_timeoutDeadline for each unary RPC. Long-lived streams are unbounded.30s
session_timeoutSession timeout for ephemeral records.15s
session_keep_aliveHow often the session is heartbeated.session_timeout / 10
identityStable client identity attached to ephemeral records.Random UUID
batch_max_sizeMaximum size of a batched request.128 KiB
max_requests_per_batchMaximum number of operations in a batch.1000
tlsTLS settings: custom CA, mutual TLS, domain-name override. Requires the tls Cargo feature.Plaintext (or TLS with system roots for an https:// address)
meter_providerOpenTelemetry meter provider to report metrics through. Requires the otel Cargo feature.Global provider

Errors surface as OxiaError; a record that does not exist is reported as Err(OxiaError::KeyNotFound) rather than a null value.

Writing records

// Write a record with the expectation that it does not already exist. let res1 = client .put("my-key", "value-1") .expected_record_not_exists() .await?; // Write with the expectation that it has not changed since the previous write. // If it has, the operation fails with OxiaError::UnexpectedVersionId. let res2 = client .put("my-key", "value-2") .expected_version_id(res1.version.version_id) .await?; // Ephemeral record: deleted automatically when the client session ends. client.put("/workers/worker-1", "host:port").ephemeral().await?; // Atomic sequence key: the server appends a monotonic suffix; requires a partition key. let seq = client .put("/events/", "event-data") .partition_key("/events/") .sequence_key_deltas([1]) .await?; // Attach a secondary index entry at write time. client .put("/offset/12345", "...") .secondary_index("partition", "p-17") .await?;

put options:

OptionDescription
partition_keyRoute to a specific shard (co-locate related keys).
expected_version_id / expected_record_not_existsConditional write (compare-and-swap, or assert the record is absent).
ephemeralBind the record to the client session — see ephemerals.
sequence_key_deltasServer-assigned monotonic suffixes — see sequence keys. Requires a partition key.
secondary_indexIndex the record under (index_name, secondary_key) — see secondary indexes.

Reading records

use oxia::ComparisonType; let record = client.get("my-key").await?; println!("{} = {:?}", record.key, record.value); // Metadata-only read (skips the value payload). let meta = client.get("my-key").include_value(false).await?; // Range-style get: returns the closest key ≤ the lookup key. let floor = client.get("/users/50").comparison(ComparisonType::Floor).await?;

get options:

OptionDescription
comparisonEqual (default), Floor, Ceiling, Lower, Higher. Non-equal modes scan all shards unless partition_key is set.
include_valueSet to false for a metadata-only read.
partition_keyRoute to a specific shard.
use_indexLook up via a named secondary index.

Deleting records

// Unconditional delete. client.delete("my-key").await?; // Conditional delete: only succeeds if the version matches. client .delete("my-key") .expected_version_id(res2.version.version_id) .await?;

Deleting a range of records

Delete all records whose keys fall within [min, max):

client.delete_range("/users/", "/users//").await?;

Without a partition key, the call fans out to every shard. Pass partition_key to scope the delete to a single shard.

Listing keys

List keys in [min, max) without fetching values:

let keys = client.list("/users/", "/users//").await?; for key in keys { println!("{key}"); } // Narrow to a single shard via partition_key, or query a secondary index via use_index. let keys = client.list("/users/", "/users//").partition_key("/users/").await?; let by_email = client.list("", "\u{ffff}").use_index("email").await?;

Keys come back in Oxia’s slash-aware key order. For a very large range, use .stream().await? to obtain an ordered async Stream instead of collecting a Vec.

Scanning records

Scan records in a key range, returning keys and values:

for record in client.range_scan("/users/", "/users//").await? { println!("key: {}, value: {:?}", record.key, record.value); } // With a secondary index. let by_email = client.range_scan("", "\u{ffff}").use_index("email").await?;

Like list, range_scan accepts partition_key and use_index, and offers .stream().await? for an ordered, memory-bounded async stream.

Sessions and ephemerals

Sessions are managed transparently: the first put(...).ephemeral() creates a per-shard session, the client heartbeats it, and ephemerals are cleaned up when the client closes or the session expires. See ephemerals for the lifecycle details.

Notifications and sequence updates

Both are subscription handles you consume with recv() (or as a Stream):

// Change feed for the namespace. let mut notifications = client.notifications().await?; while let Some(notification) = notifications.recv().await { println!("{notification}"); } // Updates for a specific sequence prefix (pass the sequence's partition key). let mut updates = client.sequence_updates("/events/", "/events/").await?; while let Some(key) = updates.recv().await { println!("New sequence key: {key}"); }

See notifications and sequence keys for semantics.

TLS

TLS support is compiled in only when the tls Cargo feature is enabled:

cargo add oxia-client --features tls

An https:// service address (or tls://, the scheme the Go client uses) enables TLS with default settings, verifying the server certificate against the operating system’s trusted roots:

let client = OxiaClient::connect("https://oxia.example.com:6648").await?;

For a custom CA, a client certificate (mutual TLS), or a domain-name override, pass TlsOptions to the builder:

use oxia::TlsOptions; let ca_pem = std::fs::read("ca.crt")?; let cert_pem = std::fs::read("client.crt")?; let key_pem = std::fs::read("client.key")?; let client = OxiaClient::builder() .service_address("https://oxia.example.com:6648") .tls( TlsOptions::new() .trusted_ca_pem(ca_pem) .identity_pem(cert_pem, key_pem), ) .build() .await?;

All of the client’s connections — including the per-shard connections it opens from the cluster’s shard assignments — use the same TLS configuration.

Metrics

The client can report per-operation metrics through OpenTelemetry . Metrics support is compiled in only when the otel Cargo feature is enabled; without it, metric recording compiles to a no-op with zero overhead.

cargo add oxia-client --features otel

Attach a meter provider when building the client, or omit it to use the global OpenTelemetry meter provider:

let client = OxiaClient::builder() .service_address("localhost:6648") .meter_provider(&provider) .build() .await?;

Instruments are reported under the oxia_client meter — the same names as the Go client — each carrying type (put, get, delete, delete_range) and result (success, failure) attributes:

InstrumentUnitDescription
oxia_client_opmillisecondsLatency of client operations, measured submit-to-complete (includes internal retries).
oxia_client_op_valuebytesValue size of operations that carry a value (the value written by a put, or returned by a get).

For the complete reference see the docs.rs API documentation .

Last updated on