July 19, 2017·4 min read·engineeringkafkalibrdkafkaprotocolcompatibility

Librdkafka reads the new Kafka record format without adding transactions

Librdkafka 0.11 adds message-format compatibility while keeping exactly-once functionality outside this release's implementation.

A client must understand a record's format before it can interoperate with producers that write that format. That compatibility does not automatically give the client every processing guarantee associated with the newer producer. Librdkafka 0.11 makes this distinction explicit. It supports Kafka's new message format while stating that it does not yet implement exactly-once functionality itself.

The 19 July release announcement identifies message version 2, the format associated with KIP-98. This lets librdkafka and clients built on it handle the new format used by the Java client released with Kafka 0.11. The note distinguishes format compatibility from the additional state and APIs required for transactional processing.

I value Kafka's retained history partly because different consumers can use it independently. That requires interoperability between client implementations. It also requires being precise about which client supplies a particular guarantee, since reading the same log does not mean every implementation has identical transactional behavior.

Format support is a prerequisite for interoperability

Kafka 0.11's design documentation describes an idempotent producer that uses a producer identity and sequence numbers. It also describes transactions across topic partitions and coordinated offset writes. Those mechanisms involve more than serializing an application's key and payload.

Librdkafka's release statement concerns compatibility with the newer message representation. A client encountering records produced by another language implementation needs to interpret the protocol correctly. Without that support, an application could be unable to consume data even though it has no need to initiate transactions itself.

The release does not advertise a transactional producer API. Its explicit qualification prevents treating the shared version number as evidence of parity with every Java client capability. A C or C++ application should evaluate the actual implementation available here.

Consumer visibility is another part of the distinction. Kafka's design documentation differentiates committed and uncommitted reads. The fact that a client recognizes a record format does not independently establish that it applies every transaction-aware visibility rule. The release's own statement is limited to compatibility and explicitly excludes exactly-once functionality.

Protocol negotiation becomes the default

The versioned configuration reference changes api.version.request to true by default. The client asks the broker which protocol versions it supports and selects available capabilities accordingly. Previously, users had to enable this behavior explicitly.

That change is useful in a mixed ecosystem because an installed client version alone does not determine what a connected broker can do. Negotiation relates the client's implementation to the server it actually reaches. When negotiation fails or is disabled, the configured broker-version fallback supplies the protocol assumptions.

The release warning identifies an awkward interaction with Kafka 0.9.0.x. Those brokers silently ignore the version request, causing a ten-second connection-setup stall before the client falls back. The documented workaround is to disable the request explicitly when communicating with those older brokers.

This is a version-specific compatibility cost. A default that exposes newer protocol capabilities on current brokers can delay connections to an older implementation. The fallback mechanism keeps the client usable, but its timing is visible to applications establishing or reestablishing connections.

A buffering default also changes application latency

The producer's queue.buffering.max.ms default changes from one second to zero. The release describes this as the internal buffering time and latency for produced messages. An application relying on the old implicit delay now receives a different default batching opportunity.

The setting concerns how long the client waits while accumulating output. It is separate from whether the broker can decode the selected record format. It also does not supply the transactional relationship between output and consumer progress described in Kafka's design documentation.

An explicit buffering configuration therefore remains useful when that delay is part of the intended throughput and latency tradeoff. Upgrading the library can change behavior under otherwise unchanged application configuration because the default itself is part of that configuration.

The release also improves broker-thread responsiveness and adds message-latency reporting. Those changes help connect observed delays to client behavior. They do not justify a general performance number for all applications, since payloads and configured batching still differ.

C++ applications need to rebuild

The release warns that the C++ interface preserves API stability but is not ABI safe. C++ applications must recompile when upgrading the library. Source compatibility and binary compatibility are separate properties, even when the program's calls look unchanged.

Other additions include SCRAM support and client interceptors, with dynamically loaded plugin support for interceptors. These extend the client in different areas. They should not be grouped into an assumption that every application using a derived language binding exposes every new function immediately.

I prefer the release's explicit limits because they make adoption assessable. An application can obtain the new record-format support and protocol negotiation while retaining an accurate account of its processing semantics. Librdkafka 0.11 broadens interoperability with the Kafka 0.11 ecosystem, and leaves implementation of exactly-once functionality as separate work.