diff --git a/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java b/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java index 80130cd7188fb..f370fe8608044 100644 --- a/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java +++ b/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java @@ -179,8 +179,22 @@ public class ConsumerConfig extends AbstractConfig { "Negative duration is not allowed." + "
Note that altering partition numbers while setting this config to latest may cause message delivery loss since " + - "producers could start to send messages to newly added partitions (i.e. no initial offsets exist yet) before consumers reset their offsets."; + "
Note that increasing a topic's partition count while this config is set to latest may cause silent " +
+ "message loss: producers may begin appending records to a newly created partition before the consumer discovers it, " +
+ "and latest resets the position to the log end offset, skipping any records produced during that discovery gap.
To avoid this, prefer by_duration:<duration>. When a partition has no committed offset, " +
+ "by_duration determines the starting position by issuing a ListOffsets lookup for " +
+ "now() - duration. If the target timestamp is earlier than the partition's creation time, the lookup " +
+ "returns offset 0, ensuring that records produced during the discovery window are still consumed. Size the duration to cover " +
+ "the worst-case partition-discovery latency for the group protocol in use:
consumer group protocol (KIP-848), newly assigned partitions are pushed on the next " +
+ "group heartbeat, so a value slightly greater than group.consumer.heartbeat.interval.ms " +
+ "(server default 5000 ms) is sufficient, for example by_duration:PT5S.classic group protocol, new partitions are discovered through periodic metadata refresh, " +
+ "so the duration must exceed metadata.max.age.ms (client default 300000 ms), " +
+ "for example by_duration:PT5M.Consumers with a valid committed offset are unaffected. The reset applies only to partitions whose offset is " +
+ "missing or out of range, so by_duration does not force existing consumers to replay historical data on restart.
fetch.min.bytes
diff --git a/docs/operations/basic-kafka-operations.md b/docs/operations/basic-kafka-operations.md
index 4ed37d87417ee..320139b8327f5 100644
--- a/docs/operations/basic-kafka-operations.md
+++ b/docs/operations/basic-kafka-operations.md
@@ -62,6 +62,16 @@ $ bin/kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic my_topic
* **Key Distribution Changes**: If data is partitioned by `hash(key) % number_of_partitions`, the default partitioner's mapping logic changes when the partition count increases. This means that messages with the same key may be routed to different partitions after the expansion, potentially affecting message ordering guarantees for existing keys. Kafka will not attempt to automatically redistribute existing data.
* **Potential Data Loss with `auto.offset.reset=latest`**: Existing consumers configured with `auto.offset.reset=latest` might miss messages produced to the new partitions during the window between partition creation and consumer discovery. This occurs because consumers may not immediately detect the new partitions, and any messages produced to those partitions before the consumer rebalances will be skipped.
+ **Recommendation:** Use `auto.offset.reset=by_duration: