From a7b48c5c2303f0f3503f58cc1be510e39af21263 Mon Sep 17 00:00:00 2001 From: David Arthur Date: Mon, 1 Jun 2026 11:04:58 -0400 Subject: [PATCH 1/2] make new Uuid no-dash behavior opt-in --- .../main/java/org/apache/kafka/common/Uuid.java | 17 ++++++++++++++++- .../java/org/apache/kafka/common/UuidTest.java | 9 +++++++++ .../controller/ReplicationControlManager.java | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/clients/src/main/java/org/apache/kafka/common/Uuid.java b/clients/src/main/java/org/apache/kafka/common/Uuid.java index 5463f7e4a7fcf..33ddf794997fe 100644 --- a/clients/src/main/java/org/apache/kafka/common/Uuid.java +++ b/clients/src/main/java/org/apache/kafka/common/Uuid.java @@ -71,9 +71,24 @@ private static Uuid unsafeRandomUuid() { /** * Static factory to retrieve a type 4 (pseudo randomly generated) UUID. *

- * This will not generate a UUID equal to 0, 1, or one whose string representation contains a dash ("-"). + * This will not generate a UUID equal to 0, 1, or one whose string representation starts with a dash ("-"). */ public static Uuid randomUuid() { + Uuid uuid = unsafeRandomUuid(); + while (RESERVED.contains(uuid) || uuid.toString().startsWith("-")) { + uuid = unsafeRandomUuid(); + } + return uuid; + } + + /** + * Static factory to retrieve a type 4 (pseudo randomly generated) UUID. + *

+ * This will not generate a UUID equal to 0, 1, or one whose string representation contains a dash ("-"). + * Note that this will result in 30% rejected UUIDs, and so should not be used in performance sensitive + * code paths. + */ + public static Uuid randomUuidNoDashes() { Uuid uuid = unsafeRandomUuid(); while (RESERVED.contains(uuid) || uuid.toString().contains("-")) { uuid = unsafeRandomUuid(); diff --git a/clients/src/test/java/org/apache/kafka/common/UuidTest.java b/clients/src/test/java/org/apache/kafka/common/UuidTest.java index 8b440d48491ca..bdec1c8f73f6b 100644 --- a/clients/src/test/java/org/apache/kafka/common/UuidTest.java +++ b/clients/src/test/java/org/apache/kafka/common/UuidTest.java @@ -81,6 +81,15 @@ public void testStringConversion() { public void testRandomUuid() { Uuid randomID = Uuid.randomUuid(); + assertNotEquals(Uuid.ZERO_UUID, randomID); + assertNotEquals(Uuid.METADATA_TOPIC_ID, randomID); + assertFalse(randomID.toString().startsWith("-")); + } + + @RepeatedTest(value = 100, name = RepeatedTest.LONG_DISPLAY_NAME) + public void testRandomUuidNoDash() { + Uuid randomID = Uuid.randomUuidNoDashes(); + assertNotEquals(Uuid.ZERO_UUID, randomID); assertNotEquals(Uuid.METADATA_TOPIC_ID, randomID); assertFalse(randomID.toString().contains("-")); diff --git a/metadata/src/main/java/org/apache/kafka/controller/ReplicationControlManager.java b/metadata/src/main/java/org/apache/kafka/controller/ReplicationControlManager.java index ae1854f3cedbb..e725870ce4898 100644 --- a/metadata/src/main/java/org/apache/kafka/controller/ReplicationControlManager.java +++ b/metadata/src/main/java/org/apache/kafka/controller/ReplicationControlManager.java @@ -815,7 +815,7 @@ private ApiError createTopic(ControllerRequestContext context, numPartitions, e.throttleTimeMs()); return ApiError.fromThrowable(e); } - Uuid topicId = Uuid.randomUuid(); + Uuid topicId = Uuid.randomUuidNoDashes(); CreatableTopicResult result = new CreatableTopicResult(). setName(topic.name()). setTopicId(topicId). From 96580687f8e0eff0611b9e4e91fe5b7ce10d562f Mon Sep 17 00:00:00 2001 From: David Arthur Date: Mon, 1 Jun 2026 11:28:44 -0400 Subject: [PATCH 2/2] update protocol.md --- docs/design/protocol.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/protocol.md b/docs/design/protocol.md index 35144739059c7..0a326ed4c6ed9 100644 --- a/docs/design/protocol.md +++ b/docs/design/protocol.md @@ -218,11 +218,11 @@ A final question is why we don't use a system like Protocol Buffers or Thrift to ## Recommendations for 3rd‑party Clients: Member ID Format -When a Kafka client participates in group protocols (e.g., `ConsumerGroupHeartbeat` RPC), it must generate a **member ID** to identify itself to the broker. While the protocol does not strictly enforce the format of this ID, we strongly recommend the following: +When a Kafka client participates in group protocols (e.g., `ConsumerGroupHeartbeat` RPC), it must generate a **member ID** to identify itself to the broker. While the protocol does not strictly enforce the format of this ID, we recommend the following: 1. **Use a base64‑encoded UUID** as the member ID. 2. **Encode the UUID using URL‑safe base64** (without `+` or `/` characters). -3. **Omit hyphens** — the resulting string should be a continuous sequence of alphanumeric characters (e.g., `abc123def456`). +3. **Omit leading hyphens** — the resulting string should not include a leading hyphen **Example** A standard UUID (`00000000-0000-0000-0000-000000000000`) should be transformed into a URL‑safe base64 string like: `YzYxNjQ4OTItZDE1Mi00Y2E4LWIyNzUtYmIwMzAwMDAwMDAw`