Skip to main content

Crate weir_client

Crate weir_client 

Source
Expand description

Synchronous blocking client for the weir daemon.

Connects over a Unix domain socket (or TCP + mutual TLS behind the tls feature) and exchanges frames using the weir wire protocol v1. Each method issues one request and reads one response — no pipelining. For concurrent producers, create one WeirClient per thread.

§Platforms

The Unix-socket transport is, unsurprisingly, Unix-only. The TCP + mutual TLS transport is not — it builds wherever rustls does, Windows included, as of 2.0.3. Before then it was gated on unix by accident: it lives in its own module but imported two types that happened to sit in the Unix one.

§Throughput: one record per round-trip

push is synchronous: it sends one frame and blocks for the ack before returning, so one connection carries one record at a time. What that costs depends on the tier, and the two are bounded by different things:

  • Buffered is round-trip bound. The daemon acks from memory, so the ceiling is roughly 1 / RTT.
  • Durable is fsync bound, not RTT bound. The ack waits on the daemon’s fsync, which dominates the round trip by orders of magnitude — batching at the daemon amortises it across concurrent producers, which is why fanning out helps this tier more than it helps Buffered.

Measured on one machine (Apple M3 Max, Unix socket, 256-byte records): a bare AF_UNIX round trip is ~7 µs, and a single client reaches ~32,000 rec/s Buffered and ~5,700 rec/s Durable. On a 2-vCPU CI runner the same figures are ~13,900 and ~3,700. Treat these as the shape of the problem, not as a spec: they move with hardware, filesystem, and record size. An earlier version of this paragraph quoted “~50 µs RTT” and “~20k rec/s”, which matched neither tier on any machine we have measured.

To go faster, fan out across connections: create one WeirClient per producer thread (they’re independent and the daemon handles many concurrently). Ordering is only guaranteed within a single connection’s sequential pushes, not across connections.

A pooled client needs no protocol change at all — the wire protocol already permits several frames in flight on one connection, and fanning out across connections is measurable today: roughly 5× on Buffered at 16 connections and 9× on Durable at 48. A built-in batched-push frame is the part that needs new wire machinery. An earlier version of this paragraph attributed the protocol cost to both and so steered readers away from the one option that already works.

§Example

use weir_client::{WeirClient, Durability}; // Durability is re-exported from weir-core

let mut client = WeirClient::connect("/run/weir/weir.sock").unwrap();
client.push(b"hello world", Durability::Durable).unwrap();

§Knowing which record you pushed

push returns (): the record was accepted, and nothing more. push_tracked instead returns a RecordCoordinate — the buffer segment the record landed in, its index within that segment, and the record_id the downstream sink will see for it as its idempotency key. That is what lets a producer reconcile what it sent against what arrived, rather than carrying its own sequence inside the payload and reconciling server-side.

use weir_client::{WeirClient, Durability};

let mut client = WeirClient::connect("/run/weir/weir.sock").unwrap();
let at = client.push_tracked(b"hello world", Durability::Durable).unwrap();
println!("{} record {} -> {}", at.segment(), at.index(), at.record_id_hex());

It is a buffer address, not a per-producer sequence: other producers’ records interleave, so your own indices have holes. See push_tracked for the full list of what it does and does not promise, including how to detect a daemon that predates it.

§Ack vs. delivery

A successful push means the record is durably buffered at the requested Durability tier — fsync’d to the write-ahead buffer for Durable, in memory for Buffered. It does not mean the record has reached your downstream sink yet: the daemon drains buffered records to the sink in batches, only once a WAB segment seals (its size threshold, or daemon shutdown). For a small smoke test the sink may not be touched at all — watch weir_records_ack_total (acceptance), not the sink-commit metric, to confirm the daemon took your records.

push_tracked does not change this. Its coordinate says where the record will be read from, not that it has been read — it is an address, not a delivery receipt.

§Running the daemon

This crate is the producer side; the daemon is the weir-server binary:

mkdir -p /run/weir/wab            # the daemon does not create its directories
weir-server --wab-dir /run/weir/wab --socket-path /run/weir/weir.sock

--wab-dir must already exist. On macOS, do not place the socket directly in /tmp (it is a symlink the hardened bind rejects) — use a dedicated 0700 directory. Run weir-server --help for the full option list.

§Observability

The daemon serves Prometheus metrics at 127.0.0.1:9185/metrics by default. The counters a producer cares about are labelled by tier/reason, e.g. weir_records_accepted_total{tier="durable"}, weir_records_ack_total{tier="durable"}, and weir_records_nack_total{tier="durable",reason="empty_payload"}.

Structs§

BatchOutcome
Per-record outcomes for a push_batch.
ClientTlsConfig
TLS configuration for WeirClient::connect_tls.
RecordCoordinate
Re-exported: the durable address WeirClient::push_tracked returns for an accepted record. Lives in weir-core because both ends of the socket build it from the same bytes. Where one accepted record lives in the write-ahead buffer, plus the id the downstream sees for it.
WeirClient
A synchronous blocking client for one connection to the weir daemon.

Enums§

ClientError
All errors that can be returned by WeirClient methods.
Durability
Re-export of weir_core::Durability so the common producer path needs a single crate import (weir_client::Durability). Durability tier requested by the producer for a given record.
NackReason
Re-export of weir_core::NackReason — the payload of ClientError::Nack. Re-exported so consumers can match on the reason (e.g. to distinguish the connection-closing Nacks) without taking a direct dependency on weir-core. Reason byte carried as the first byte of every Nack message payload. Wire values are fixed and must not change without a WIRE_VERSION bump.

Type Aliases§

DefaultTransport
The transport WeirClient uses when no type parameter is given.
TlsStream
The TLS stream type returned by WeirClient::connect_tls.