From 92fe00e184dbe4a32f01d8f283252ca3ec898d14 Mon Sep 17 00:00:00 2001 From: anonymous Date: Wed, 27 Aug 2025 15:34:12 -0500 Subject: [PATCH] KAFKA-19497; Topic replay code does not handle creation and deletion in the same delta (#20242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a small logic bug in topic replay. If a topic is created and then removed before the TopicsDelta is applied, we end up with the deleted topic in createdTopics on the delta. Tis issue is fixed by removing the topicName from createTopics when replaying RemoveTopicRecord to make sure the deleted topics won't appear in createTopics. Reviewers: José Armando García Sancio , Kevin Wu , Alyssa Huang --- .../main/java/org/apache/kafka/image/TopicsDelta.java | 1 + .../java/org/apache/kafka/image/TopicsImageTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/metadata/src/main/java/org/apache/kafka/image/TopicsDelta.java b/metadata/src/main/java/org/apache/kafka/image/TopicsDelta.java index 63ae2d06026..9b0ad7a916a 100644 --- a/metadata/src/main/java/org/apache/kafka/image/TopicsDelta.java +++ b/metadata/src/main/java/org/apache/kafka/image/TopicsDelta.java @@ -136,6 +136,7 @@ public final class TopicsDelta { String topicName; if (topicDelta != null) { topicName = topicDelta.image().name(); + createdTopics.remove(topicName); if (image.topicsById().containsKey(record.topicId())) { deletedTopicIds.add(record.topicId()); } diff --git a/metadata/src/test/java/org/apache/kafka/image/TopicsImageTest.java b/metadata/src/test/java/org/apache/kafka/image/TopicsImageTest.java index 9154e799a33..bfdccc424e2 100644 --- a/metadata/src/test/java/org/apache/kafka/image/TopicsImageTest.java +++ b/metadata/src/test/java/org/apache/kafka/image/TopicsImageTest.java @@ -897,4 +897,15 @@ public class TopicsImageTest { var result = IMAGE1.topicIdToNameView().get("zar"); assertNull(result); } + + @Test + public void testTopicsDeltaCreateThenDelete() { + TopicsDelta delta = new TopicsDelta(TopicsImage.EMPTY); + delta.replay(new TopicRecord().setName("test").setTopicId(FOO_UUID)); + assertEquals(delta.createdTopicIds().contains(FOO_UUID), true); + assertEquals(delta.deletedTopicIds().contains(FOO_UUID), false); + delta.replay(new RemoveTopicRecord().setTopicId(FOO_UUID)); + assertEquals(delta.deletedTopicIds().contains(FOO_UUID), false); + assertEquals(delta.createdTopicIds().contains(FOO_UUID), false); + } }