Configuration

Every connection, producer, consumer, admin and transport option, with their defaults.

krafka has one place for each setting. Connection settings — how the process reaches the cluster — live on the Kafka builder and apply to every client built from that handle. Role settings live on the role builders: kafka.producer(), kafka.consumer(group), kafka.consumer_without_group(), kafka.share_consumer(group), and on the AdminClient that kafka.admin() returns. Where a setting has a Java client equivalent, its description names it.

use krafka::Kafka;
use std::time::Duration;

let kafka = Kafka::builder("kafka1:9092,kafka2:9092") // connection settings
    .client_id("orders-service")
    .request_timeout(Duration::from_secs(30))
    .connect()
    .await?;

let producer = kafka.producer().linger(Duration::from_millis(5)).build().await?; // role settings
let consumer = kafka.consumer("orders").build().await?;

No setter returns a Result: connect() and build() validate, and an invalid value is a KrafkaError::Config naming the setting. A second identity, other credentials or a separate connection pool is a second Kafka handle.

Cargo Features

Generated from Cargo.toml and xtask/kips.toml; Default follows default = [...], and the gated API versions come from the api_versions! table.

FeatureDefaultGates API versionsDescription
zstdno—Zstd compression on the producer via zstd, which compiles C (zstd-sys). Zstd decompression is pure Rust and always on.
aws-mskno—AWS MSK IAM authentication with the SDK credential chain; compiles C and needs CMake (aws-lc-sys).
oauth-oidcno—Built-in OIDC token provider for SASL/OAUTHBEARER: the client_credentials grant (KIP-768) and RFC 7523 client assertions (KIP-1258). Adds no cryptography dependency.
native-tls-rootsno—Load platform-native root certificates via rustls-native-certs.
tls-encrypted-keysno—Passphrase-encrypted PKCS#8 client keys (ssl.key.password) via the RustCrypto pkcs8 crate.
unstable-protocolnoApiVersions v5, InitProducerId v6Protocol versions Kafka marks latestVersionUnstable; a released broker advertises them only with unstable.api.versions.enable=true. No semver promise.
test-brokerno—In-process fake Kafka broker (krafka::testing) for testing your own code against a real client. Not for production builds; no semver promise.
internalno—Tooling only: exposes krafka::__private to krafka's own benches and fuzz targets. Not a user feature; no semver promise.
ringyes—rustls crypto backend using ring.
rustls-aws-lc-rsno—rustls crypto backend using aws-lc-rs; offers post-quantum X25519MLKEM768 key exchange first. Compiles C and needs CMake (aws-lc-sys).

Connection Configuration (Kafka::builder)

OptionTypeDefaultDescription
bootstrap_serversStringRequiredThe argument of Kafka::builder: comma-separated host:port pairs
client_idString"krafka"Client identifier sent with every request of every client (client.id)
securityAuthConfigplaintextTLS and SASL; see Authentication
request_timeoutDuration30sHow long one request may wait for its response (request.timeout.ms). Must be ≥ connect_timeout
connect_timeoutDuration10sBudget for one connection attempt to one broker: TCP, then the TLS and SASL handshakes. The floor on request_timeout
metadata_max_ageDuration5mMax age before a metadata refresh (metadata.max.age.ms)
metadata_topic_cache_ttlOption<Duration>Some(5m)How long a topic entry may sit idle before a partial refresh evicts it (metadata.max.idle.ms). Any use resets the timer. None disables eviction
allow_auto_create_topicsboolfalseLet the broker create a topic a client asks about but the cluster does not have (allow.auto.create.topics). The broker must also have auto.create.topics.enable=true
metadata_recovery_strategyMetadataRecoveryStrategyRebootstrapWhat to do when every known broker is unreachable (metadata.recovery.strategy, KIP-899)
metadata_recovery_rebootstrap_triggerDuration5mHow long metadata refreshes may fail before a rebootstrap (metadata.recovery.rebootstrap.trigger.ms, KIP-1102)
proxyProxyConfigdirectSOCKS5 route for every connection; see SOCKS5 Proxy

The transport settings below are connection settings too.

Transport

OptionTypeDefaultDescription
tcp_nodelaybooltrueDisable Nagle's algorithm; the client batches already.
tcp_keepaliveOption<Duration>Some(60s)Keeps NAT/firewall state alive. Set it below the middlebox's idle timeout, or an idle consumer stops receiving after that timeout.
max_response_sizeusize100 MiBLargest accepted response frame. Raise it above the topic's max.message.bytes: Kafka returns at least one full record batch even when it exceeds fetch.max.bytes, so a larger message stalls the partition permanently. Lower it to bound memory.
max_in_flight_requestsusize10Per-connection in-flight cap, for every request type (Java's max.in.flight.requests.per.connection is a producer setting). Senders wait at the cap; they are not rejected. Worst-case memory is max_response_size × max_in_flight_requests. No bearing on ordering: krafka keeps one batch per partition on the wire regardless.
socket_send_bufferOption<usize>None (OS default)SO_SNDBUF for every broker socket (Java send.buffer.bytes). Raise it on a high bandwidth-delay-product link.
socket_receive_bufferOption<usize>None (OS default)SO_RCVBUF (Java receive.buffer.bytes); the one that matters for a consumer on a long link.
connection_attempt_delayDuration250msHappy Eyeballs stagger (RFC 8305 §5), clamped to 100 ms – 2 s.
connections_max_idleOption<Duration>Some(9min)Idle-eviction window (Java connections.max.idle.ms). None disables eviction.
max_connectionsOption<usize>NoneCap on live connections across all brokers, coordination connections included. Replacing a dead or session-expired connection never counts as growth; a connection beyond the cap fails with a retriable Network error, and a coordination connection falls back to the broker's data connection.
tls_reload_intervalOption<Duration>NoneRe-read TLS certificate files from disk on a timer (KIP-1288). kafka.refresh_tls() reloads on demand.
use krafka::Kafka;
use std::time::Duration;

let kafka = Kafka::builder("localhost:9092")
    .tcp_keepalive(Some(Duration::from_secs(30)))
    .max_response_size(200 * 1024 * 1024)
    .socket_receive_buffer(Some(4 * 1024 * 1024)) // long-haul fetch
    .max_connections(Some(64))
    .tls_reload_interval(Some(Duration::from_secs(3600)))
    .connect()
    .await?;

connect() rejects values that cannot be honoured — a zero max_in_flight_requests (nothing could ever be sent), a max_response_size below 1 KiB, a zero interval where None is the way to switch a period off, or a request_timeout below connect_timeout.

Every client of the handle shares the pool, and with it the proxy, the descriptor cap and the reload interval.

Producer Configuration (kafka.producer())

OptionTypeDefaultDescription
acksAcksAllAcknowledgment level for durability
compressionCompressionNoneCompression codec
compression_levelOption<i32>NoneCodec level (Gzip 0–9, Zstd); rejected with Snappy or LZ4
topic_compressionper topic—Override the codec for one topic
batch_sizeusize16384Maximum bytes per batch (must be >= 1)
lingerDuration5msHow long a partial batch may wait for more records (KIP-1030). 0 still batches — see Producer
delivery_timeoutDuration120sBound from a batch's creation to its records' outcome (delivery.timeout.ms); the only bound on retries. Must be ≥ linger + request_timeout
retry_backoffDuration100msFirst retry delay (retry.backoff.ms); doubles per retry, ±20 % jitter, capped at 1 s
max_request_sizeusize100MiBLargest encoded Produce request
buffer_memoryusize32MiBTotal bytes the producer may buffer for unsent records (buffer.memory, must be >= 1)
max_blockDuration60sOne budget for everything send()/enqueue() block on: metadata for an unresolved topic, then buffer_memory (max.block.ms). Also bounds each transaction coordinator call
idempotentbooltrueIdempotent production (KIP-679, requires acks=All)
client_rackStringnoneThe producer's rack (client.rack), read by partitioner_rack_aware
partitioner_rack_awareboolfalseKeyless records go only to partitions led in client_rack (KIP-1123)
partitionerimpl Partitionerbuilt-inCustom partitioner
interceptorimpl ProducerInterceptornoneAppend an interceptor to the chain
transaction_timeoutDuration60sbuild_transactional only: how long the coordinator lets a transaction stay open
two_phase_commitboolfalsebuild_transactional only: KIP-939 external two-phase commit
metrics_pushbooltruePush metrics to brokers that subscribe to them (KIP-714, enable.metrics.push); see Metrics

Acks Values

use krafka::producer::Acks;

Acks::None    // 0: Don't wait for acknowledgment
Acks::Leader  // 1: Wait for leader acknowledgment
Acks::All     // -1: Wait for all in-sync replicas

Compression Values

Every codec decodes in every build (zstd through the pure-Rust ruzstd). Gzip, Snappy and LZ4 also encode in every build; zstd encoding is opt-in through the zstd feature, which compiles the C zstd library through zstd-sys. Use Compression::is_available() to check whether a codec can encode; build() rejects a codec that cannot.

use krafka::Compression;

Compression::None    // No compression
Compression::Gzip    // Gzip compression
Compression::Snappy  // Snappy compression
Compression::Lz4     // LZ4 compression
Compression::Zstd    // Zstandard compression (encoding needs feature = "zstd")

Producer Builder Example

use krafka::producer::Acks;
use krafka::{Compression, Kafka};
use std::time::Duration;

let kafka = Kafka::builder("kafka1:9092,kafka2:9092")
    .client_id("my-producer")
    .connect()
    .await?;
let producer = kafka
    .producer()
    .acks(Acks::All)
    .compression(Compression::Lz4)
    .batch_size(64 * 1024)
    .linger(Duration::from_millis(5))
    .delivery_timeout(Duration::from_secs(120))
    .retry_backoff(Duration::from_millis(200))
    .build()
    .await?;

Consumer Configuration (kafka.consumer(group))

kafka.consumer(group) joins consumer group group; kafka.consumer_without_group() builds a consumer that assigns its own partitions and commits nothing.

OptionTypeDefaultDescription
group_instance_idStringnoneStatic membership instance ID (KIP-345)
auto_offset_resetAutoOffsetResetLatestWhere to start when no offset
enable_auto_commitbooltrueAuto-commit offsets
auto_commit_intervalDuration5sAuto-commit interval
fetch_min_bytesi321Min bytes to return from fetch
fetch_max_bytesi3252428800Max bytes per fetch response
max_partition_fetch_bytesi321048576Max bytes per partition
topic_fetch_max_bytesper topic—Override max_partition_fetch_bytes for one topic
fetch_max_waitDuration500msHow long a broker holds a fetch waiting for fetch_min_bytes. Independent of the poll() timeout: poll() long-polls client-side until its own deadline.
max_poll_recordsi32500Max records per poll; -1 = unlimited; 0 and other negative values rejected
max_buffered_recordsi32500Fetched records held before they are handed out; 0 = no cap
session_timeoutDuration45sGroup session timeout (session.timeout.ms)
heartbeat_intervalDuration3sHeartbeat interval; must be below session_timeout
max_poll_intervalDuration5mMax time between poll() calls, and the rebalance timeout. Enforced: exceeding it leaves the group so the partitions are reassigned; the next poll() reports the loss and rejoins.
isolation_levelIsolationLevelReadUncommittedTransaction isolation
group_protocolGroupProtocolClassicGroup protocol: Classic or Consumer (KIP-848)
group_remote_assignorStringnoneServer-side assignor for GroupProtocol::Consumer (group.remote.assignor), e.g. uniform or range; rejected with Classic
partition_assignment_strategiesVec<PartitionAssignmentStrategy>[Range, CooperativeSticky]Preference-ordered assignor list, advertised in JoinGroup (partition.assignment.strategy). The Java default; it lets a group move from eager to cooperative in one rolling bounce. partition_assignment_strategy sets a single one.
client_rackStringnoneRack for closest-replica fetching (KIP-392)
lag_staleness_thresholdDuration60sWatermarks older than this are reported stale by lag()
idle_poll_backoffDuration10msBackoff between polls when nothing can be fetched. Set to Duration::ZERO for minimum latency.
max_decompressed_sizeusize128MiBDecompression-bomb limit per record batch
initial_offsets(TopicPartition, Offset) pairs—Start offsets used before auto_offset_reset
rebalance_listenerimpl ConsumerRebalanceListenernoneAssignment-change callbacks
interceptorimpl ConsumerInterceptornoneAppend an interceptor to the chain
key_deserializer / value_deserializerimpl DeserializernoneDecode keys/values before they are returned
metrics_pushbooltruePush metrics to brokers that subscribe to them (KIP-714, enable.metrics.push)

AutoOffsetReset Values

use krafka::consumer::AutoOffsetReset;

AutoOffsetReset::Earliest  // Start from the earliest offset
AutoOffsetReset::Latest    // Start from the latest offset
AutoOffsetReset::ByDuration(Duration::from_secs(86_400))
                           // First record of the last 24 h, else the log end (KIP-1106)
AutoOffsetReset::None      // Error if no committed offset (strictly enforced)

IsolationLevel Values

use krafka::consumer::IsolationLevel;

IsolationLevel::ReadUncommitted  // Read all messages
IsolationLevel::ReadCommitted    // Only read committed transaction messages

Consumer Builder Example

use krafka::Kafka;
use krafka::consumer::{AutoOffsetReset, IsolationLevel};
use std::time::Duration;

let kafka = Kafka::builder("kafka1:9092,kafka2:9092")
    .client_id("my-consumer")
    .connect()
    .await?;
let consumer = kafka
    .consumer("my-consumer-group")
    .auto_offset_reset(AutoOffsetReset::Earliest)
    .enable_auto_commit(false)
    .fetch_min_bytes(1024)
    .fetch_max_bytes(10 * 1024 * 1024)
    .max_partition_fetch_bytes(1024 * 1024)
    .fetch_max_wait(Duration::from_millis(100))
    .max_poll_records(1000)
    .session_timeout(Duration::from_secs(30))
    .heartbeat_interval(Duration::from_secs(10))
    .isolation_level(IsolationLevel::ReadCommitted)
    .group_instance_id("instance-1")
    .build()
    .await?;

Share Consumer Configuration (kafka.share_consumer(group))

kafka.share_consumer(group) builds a KIP-932 share-group member; see Share Consumer.

OptionTypeDefaultDescription
acknowledgement_modeAcknowledgementModeImplicitImplicit accepts a poll's records when the next poll()/recv() starts; Explicit settles each record with ack, release or reject
acquire_modeAcquireModeBatchOptimizedRecordLimit makes max_poll_records a hard limit (KIP-1206, Kafka 4.2+)
max_poll_recordsi32500Records per poll(), and the MaxRecords of every ShareFetch; at least 1
batch_sizei32500Acquisition batch-size hint, capped at max_poll_records
fetch_min_bytesi321Bytes a broker must have before answering a ShareFetch
fetch_max_bytesi3252428800Max bytes per ShareFetch response
fetch_max_waitDuration500msHow long a broker may hold a ShareFetch; capped by the poll() timeout
client_rackStringnoneThe consumer's rack
max_decompressed_sizeusize128MiBDecompression-bomb limit per record batch
acknowledgement_commit_callbackFn(&AcknowledgementCommit)noneOutcome of every acknowledgement request, per partition
key_deserializer / value_deserializerimpl DeserializernoneDecode keys/values before they are returned
metrics_pushbooltruePush metrics to brokers that subscribe to them (KIP-714, enable.metrics.push)

Admin Client Configuration (kafka.admin())

kafka.admin() returns an AdminClient directly; it contacts no broker until its first call. Its settings are set on the client:

OptionTypeDefaultDescription
default_api_timeoutDuration60sBound on one admin call — lookups, attempts and backoff — when its options set no timeout, as Java's default.api.timeout.ms
retry_backoffDuration100msInitial backoff between attempts inside one call (retry.backoff.ms). Exponential (2×) to a 1 s ceiling with 20 % jitter.
metrics_pushboolfalsePush metrics to brokers that subscribe to them (KIP-714, enable.metrics.push). Off by default, as in the Java admin client

Retries and the call deadline

On NOT_CONTROLLER the client fetches metadata, finds the new controller and sends again. Retries continue until the call's deadline: its options' timeout, else default_api_timeout. See Admin Client for what is retried.

use std::time::Duration;

// A cluster whose elections take a while.
let admin = kafka
    .admin()
    .default_api_timeout(Duration::from_secs(120))
    .retry_backoff(Duration::from_millis(250));

Validation

Every setting is checked where it takes effect: connection settings by Kafka::builder(..).connect(), role settings by the role builder's build() (or build_transactional). The error is a KrafkaError::Config that names the setting:

// Without the `zstd` feature this fails at build time, not on the first send.
let result = kafka
    .producer()
    .compression(krafka::Compression::Zstd)
    .build()
    .await;

Two checks keep the surface complete:

  • tests/builder_surface.rs — do these methods exist, with these shapes? Each line fails to compile if the method it names disappears.
  • just config-reachability — is any field unreachable? It walks every config struct's fields and requires each to have a same-named setter on its builder, or an entry in an exception list carrying a reason.

SOCKS5 Proxy

proxy routes every broker connection through a SOCKS5 proxy, for brokers reachable only through a bastion. The proxy resolves broker hostnames; the client sends them unresolved.

Proxy Without Authentication

use krafka::{Kafka, ProxyConfig};

let kafka = Kafka::builder("kafka.internal:9092")
    .proxy(ProxyConfig::new("socks5-proxy.corp:1080"))
    .connect()
    .await?;

Proxy With Authentication

use krafka::{Kafka, ProxyConfig};

let kafka = Kafka::builder("kafka.internal:9092")
    .proxy(ProxyConfig::with_credentials(
        "socks5-proxy.corp:1080",
        "proxy-user",
        "proxy-password",
    ))
    .connect()
    .await?;

Proxy credentials are zeroized from memory on drop and redacted in Debug output.

Proxy With TLS/SASL

Proxy and authentication combine — the SOCKS5 tunnel is established first, then TLS and/or SASL negotiation proceeds over the tunneled connection:

use krafka::auth::{AuthConfig, TlsConfig};
use krafka::{Kafka, ProxyConfig};

let kafka = Kafka::builder("kafka.secure.internal:9093")
    .security(AuthConfig::ssl(TlsConfig::new()))
    .proxy(ProxyConfig::new("bastion:1080"))
    .connect()
    .await?;

Topic Configuration

For NewTopic when creating topics:

use krafka::admin::NewTopic;

let topic = NewTopic::new("my-topic", 12, 3)?
    .with_config("cleanup.policy", "compact")
    .with_config("retention.ms", "604800000")      // 7 days
    .with_config("segment.bytes", "1073741824")    // 1GB
    .with_config("min.insync.replicas", "2");

Common Topic Configs

ConfigTypeDefaultDescription
cleanup.policyStringdeletedelete or compact
retention.msLong604800000 (7 days)Message retention time; -1 keeps forever
retention.bytesLong-1Partition size limit
segment.bytesInt1GBSegment file size
min.insync.replicasInt1Min replicas for write
compression.typeStringproducerServer compression
max.message.bytesInt1048588Max record batch size

Environment Variables

AuthConfig::from_env() builds the security settings from the standard KAFKA_SECURITY_PROTOCOL, KAFKA_SASL_* and KAFKA_SSL_* variables; see Authentication. Everything else is read by your application and passed to the builders.

Performance Tuning Profiles

High Throughput Producer

use krafka::producer::Acks;
use krafka::Compression;
use std::time::Duration;

let producer = kafka
    .producer()
    .acks(Acks::All)
    .compression(Compression::Lz4)
    .batch_size(1024 * 1024)
    .linger(Duration::from_millis(50))
    .build()
    .await?;

Low Latency Producer

use krafka::producer::Acks;
use std::time::Duration;

let producer = kafka
    .producer()
    .acks(Acks::Leader)
    .idempotent(false) // idempotence needs acks = All
    .linger(Duration::ZERO)
    .build()
    .await?;

High Throughput Consumer

let consumer = kafka
    .consumer("high-throughput")
    .fetch_max_bytes(100 * 1024 * 1024)
    .max_partition_fetch_bytes(10 * 1024 * 1024)
    .max_poll_records(10_000)
    .build()
    .await?;

Low Latency Consumer

use std::time::Duration;

let consumer = kafka
    .consumer("low-latency")
    .fetch_min_bytes(1)
    .fetch_max_wait(Duration::from_millis(10))
    .max_poll_records(10)
    .build()
    .await?;