Here is the consolidated master checklist of every configuration and structural design choice we covered to ensure guaranteed, end-to-end message ordering in Apache Kafka.
1. Topic Design Constraints
- Message Key Strategy: You must pass a non-null, consistent business key (e.g.,
order_id,customer_id) with every message. Kafka uses this key to hash and route all related events to the exact same partition. - Partition Immutability: Never increase the partition count of a topic while it is actively receiving keyed messages. Changing the count alters the hashing formula, which will split your entity’s history across different partitions and break order.
- Global Ordering Fallback: If absolute ordering across the entire topic is required (instead of just entity-level), you must restrict the topic to a single partition (
num.partitions = 1), sacrificing horizontal scaling.
2. Producer Properties
These properties prevent messages from being written out of sequence due to network drops, broker re-elections, or transaction boundaries.
# 1. Protects against duplicates and reordering caused by producer retries
enable.idempotence=true
# 2. Ensures the message is safely stored on all in-sync replicas before getting an ACK
acks=all
# 3. Allows up to 5 concurrent requests while idempotence handles sequencing (Max safe limit)
max.in-flight.requests.per.connection=5
# 4. Mandatory if writing grouped operations; prevents "dirty reads" on the consumer side
transactional.id=your-unique-producer-id-string3. Consumer Properties
These settings ensure the consumer reads records exactly as they were written, without skipping failed data or reading uncommitted transaction blocks.
# 1. Disables auto-commit so you only advance the offset after processing logic succeeds
enable.auto_commit=false
# 2. Prevents the consumer from reading aborted or ongoing transactional data out of order
isolation.level=read_committed
# 3. Ensures a new consumer group reads from the absolute beginning of the log
auto.offset.reset=earliest4. Application Code Rules
Even with perfect infrastructure configs, application code can scramble message sequence. You must enforce these patterns:
-
Single-Threaded Execution: The consumer loop must process records sequentially. Do not hand records off to multi-threaded executor services or async thread pools inside the loop.
-
Resilient Error Handling: If a message fails, you cannot simply skip it or send it to a blind DLQ. You must either:
- Stop the Line: Fail the batch, rewind the consumer offset using
seek(), and retry the message (e.g., Spring Kafka’sDefaultErrorHandler). - Entity-Based Hold: Flag that specific key as “blocked” in a database (like Redis) and route that message—and all subsequent messages for that key—to a sequential DLQ until a specialized recovery worker drains it.
- Stop the Line: Fail the batch, rewind the consumer offset using
Would you like me to help you verify your existing configuration files (like an application.properties or environment variables), or do you want to explore the performance and throughput impact of running Kafka with these strict ordering settings?