September 19, 2019·4 min read·engineeringkafkatransactionsclients

librdkafka 1.2 makes committed transactions the default consumer view

librdkafka 1.2 defaults to read_committed isolation. Aborted transactional records disappear from the application view, and an open transaction can hold back progress through a partition.

A record can exist in a Kafka partition without yet being eligible for a transaction-aware reader. The September 19 librdkafka 1.2.0 release adds that reader behavior and makes it the default. Its new isolation.level setting defaults to read_committed. Previous releases delivered records from aborted and uncommitted transactions as well as committed records.

The default is a substantive part of the release. An application upgrading its native Kafka client can observe a different record stream without adding an explicit isolation setting. Aborted transactional records are filtered out, and the reader does not advance into a transaction that remains uncommitted. The feature concerns what the consumer may observe, not merely a label attached to returned messages.

I value Kafka as durable history, but a transactional producer also defines which parts of its attempted history should become visible. A reader that honors that distinction can consume the committed result without treating every attempted write as an application event. The isolation choice therefore belongs in the reader's contract.

Visibility stops at a stable boundary

The release explains that read_committed changes the consumer's end-of-partition view to a stable transactional boundary. It identifies that boundary with the Last Stable Offset. A consumer must not pass an unresolved transaction simply because later records are physically present in the partition.

That has an operational consequence. A reader can appear caught up to its eligible data while records remain beyond the unresolved boundary. The difference is not necessarily a failure to fetch from the broker. It follows from the isolation mode's requirement to avoid exposing records before their transaction outcome is known.

The release also says nontransactional records remain readable. However, the consumer does not advance through a still-uncommitted transaction in the partition. That means a statement that nontransactional messages are unaffected needs this ordering qualification. Their own status does not permit the consumer to ignore the stable boundary ahead of them.

Partition order and transaction visibility consequently interact. A consumer reads an ordered partition, and its eligible prefix depends on resolved transactions. Filtering aborted records is one result of the implementation. Waiting for an unresolved transaction is another, with different implications for the apparent distance between the physical log and the application's view.

The previous behavior remains selectable

The configuration file at the release tag documents both supported isolation values. Selecting read_uncommitted returns all messages, including messages from aborted transactions. The release identifies that as the way to retain the behavior of earlier versions.

isolation.level=read_committed

This fragment makes the new default explicit in configuration supplied to librdkafka. It does not activate a transaction in the producer. The producer determines its write transaction, while the consumer's isolation setting determines how the reader treats the result.

That separation is useful for applications implemented through different Kafka clients. A producer and consumer do not have to be the same program or use the same language library to participate in the broker's transaction model. This release adds the consumer-side interpretation to librdkafka. The announcement does not claim to deliver a new transactional producer API with this feature.

An explicit setting also records intent for future readers of the application's configuration. Relying on a default can produce correct behavior, but the release is itself an example of a default changing between client versions. A diagnostic reader intentionally examining attempted writes may select a different view from an application consuming committed events. The documented choice tells a maintainer whether seeing aborted attempts is intentional or contrary to the application contract.

Committing a transaction differs from committing a consumer position

The tagged configuration separately documents consumer offset management. enable.auto.commit controls periodic background offset commits. enable.auto.offset.store controls the in-memory position that will be committed, normally the next offset after the last message supplied to the application. Neither is the transaction-isolation setting.

This distinction prevents overclaiming what transaction-aware reads provide. Hiding aborted producer writes does not establish that the consumer has completed its own external side effect before its offset is committed. The configuration guide explicitly recommends manual offset storage after processing for long-running work to avoid committing progress before processing finishes.

An application can therefore choose committed producer data and still need a separate strategy for its processing progress. The first choice determines eligible inputs. The second determines what a restarted reader considers already consumed. A database write performed by the reader has its own relationship to those progress updates.

My preference is to keep those meanings separate when discussing reliable replay. Kafka retains the records, the isolation setting determines which transactional records the reader sees, and the consumer's stored position determines where it resumes. librdkafka 1.2 supplies the transaction-aware input view while leaving the application's progress and side-effect coordination as explicit design decisions.