Enable idempotence on the producer: set enable.idempotence=true; this automatically sets acks=all, retries=Integer.MAX_VALUE, and max.in.flight.requests.per.connection=5
Assign a stable transactional.id to the producer (unique per logical producer instance, e.g. 'payments-producer-0'); this enables cross-session exactly-once guarantees
Initialize transactions in the producer application: producer.initTransactions() before sending any messages
Wrap each logical unit of work: beginTransaction() → send() → sendOffsetsToTransaction() (when consuming) → commitTransaction() or abortTransaction() on error
On the consumer side set isolation.level=read_committed so the consumer only reads messages from committed transactions and never sees aborted or in-flight transactional messages
Configure the broker-side transaction.state.log.replication.factor and transaction.state.log.min.isr to at least 2 for the internal __transaction_state topic to survive broker failures
Known gotchas
transactional.id fencing: if a new producer instance starts with the same transactional.id, the broker fences the old epoch — this is by design but will cause the old producer to receive a ProducerFencedException on its next send or commit
Transactions have a server-side timeout controlled by transaction.timeout.ms (default 60 seconds on the broker as transaction.max.timeout.ms caps it); transactions open longer than this are aborted by the broker
read_committed consumers will not see any messages beyond the last stable offset (LSO) while an open transaction exists on the partition; a stalled producer holding an open transaction blocks consumer progress
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp