This is an automated email from the ASF dual-hosted git repository. Caideyipi pushed a commit to branch fix/consensus-topic-config-validation in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 83c81b94b21c325900587abfc2446d8aeba5ed56 Author: Caideyipi <[email protected]> AuthorDate: Mon Jul 20 17:35:55 2026 +0800 [Subscription] Reject unsupported consensus topic attributes --- .../iotdb/confignode/i18n/ConfigNodeMessages.java | 3 ++ .../iotdb/confignode/i18n/ConfigNodeMessages.java | 3 ++ .../persistence/subscription/SubscriptionInfo.java | 47 ++++++++++++++++++++++ .../SubscriptionInfoTopicValidationTest.java | 39 ++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java index 4625d48a14c..494201b1e9f 100644 --- a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java +++ b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java @@ -662,4 +662,7 @@ public final class ConfigNodeMessages { public static final String EXCEPTION_PROCEDURE_COMPLETED_EVICT_TTL_SHOULD_BE_GREATER_THAN_0_BUT_WAS_5A4D0CF6 = "procedure_completed_evict_ttl should be greater than 0, but was "; + public static final String + EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_MODE_CONSENSUS_DOES_NOT_SUPPORT_TOPIC_ATTRIBUTES_ARG_3C2D0BDA = + "Failed to create or alter topic, mode=consensus does not support topic attributes %s"; } diff --git a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java index 6e45b04a001..0193e5a0485 100644 --- a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java +++ b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java @@ -707,4 +707,7 @@ public final class ConfigNodeMessages { public static final String EXCEPTION_PROCEDURE_COMPLETED_EVICT_TTL_SHOULD_BE_GREATER_THAN_0_BUT_WAS_5A4D0CF6 = "procedure_completed_evict_ttl 应大于 0,但当前值为 "; + public static final String + EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_MODE_CONSENSUS_DOES_NOT_SUPPORT_TOPIC_ATTRIBUTES_ARG_3C2D0BDA = + "创建或修改 topic 失败,mode=consensus 不支持 topic 属性 %s"; } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java index df6a0d51231..4d063e83152 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java @@ -110,6 +110,23 @@ public class SubscriptionInfo implements SnapshotProcessor { TopicConstant.OWNER_EPOCH_KEY, TopicConstant.MAX_OWNER_EPOCH_KEY, TopicConstant.OWNER_LEASE_DURATION_MS_KEY); + private static final Set<String> CONSENSUS_TOPIC_SUPPORTED_ATTRIBUTE_KEYS = + Set.of( + SystemConstant.SQL_DIALECT_KEY, + TopicConstant.PATH_KEY, + TopicConstant.PATTERN_KEY, + TopicConstant.DATABASE_KEY, + TopicConstant.TABLE_KEY, + TopicConstant.COLUMN_FILTER_KEY, + TopicConstant.RETENTION_BYTES_KEY, + TopicConstant.RETENTION_MS_KEY, + TopicConstant.MODE_KEY, + TopicConstant.ORDER_MODE_KEY, + TopicConstant.FORMAT_KEY, + TopicConstant.OWNER_ID_KEY, + TopicConstant.OWNER_EPOCH_KEY, + TopicConstant.MAX_OWNER_EPOCH_KEY, + TopicConstant.OWNER_LEASE_DURATION_MS_KEY); private final TopicMetaKeeper topicMetaKeeper; private final ConsumerGroupMetaKeeper consumerGroupMetaKeeper; @@ -329,6 +346,7 @@ public class SubscriptionInfo implements SnapshotProcessor { throw new SubscriptionException(exceptionMessage); } + validateConsensusTopicAttributes(topicConfig); validateConsensusProtocolSupport(topicConfig); if (topicConfig.isConsensusMode() && !topicConfig.isRecordFormat()) { @@ -375,6 +393,35 @@ public class SubscriptionInfo implements SnapshotProcessor { } } + private void validateConsensusTopicAttributes(final TopicConfig topicConfig) + throws SubscriptionException { + if (!topicConfig.isConsensusMode()) { + return; + } + + final List<String> unsupportedAttributes = + topicConfig.getAttribute().keySet().stream() + .filter( + key -> + Objects.isNull(key) + || !CONSENSUS_TOPIC_SUPPORTED_ATTRIBUTE_KEYS.contains( + key.trim().toLowerCase(Locale.ROOT))) + .map(String::valueOf) + .sorted() + .collect(Collectors.toList()); + if (unsupportedAttributes.isEmpty()) { + return; + } + + final String exceptionMessage = + String.format( + ConfigNodeMessages + .EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_MODE_CONSENSUS_DOES_NOT_SUPPORT_TOPIC_ATTRIBUTES_ARG_3C2D0BDA, + unsupportedAttributes); + LOGGER.warn(exceptionMessage); + throw new SubscriptionException(exceptionMessage); + } + private void validateConsensusProtocolSupport(final TopicConfig topicConfig) throws SubscriptionException { if (!topicConfig.isConsensusMode()) { diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java index 2fa1bf714d5..858bc41d758 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java @@ -115,6 +115,45 @@ public class SubscriptionInfoTopicValidationTest { assertCreateRejected(subscriptionInfo, attributes, "mode=consensus only supports format"); } + @Test + public void testRejectUnsupportedAttributesOnConsensusTopic() { + final SubscriptionInfo subscriptionInfo = new SubscriptionInfo(); + final Map<String, String> attributes = newConsensusTableTopicAttributes(); + attributes.put(TopicConstant.START_TIME_KEY, "0"); + attributes.put(TopicConstant.STRICT_KEY, "false"); + attributes.put("processor", "custom-processor"); + + assertCreateRejected( + subscriptionInfo, + attributes, + "mode=consensus does not support topic attributes [processor, start-time, strict]"); + } + + @Test + public void testRejectUnknownAttributeOnConsensusTopic() { + final SubscriptionInfo subscriptionInfo = new SubscriptionInfo(); + final Map<String, String> attributes = newConsensusTableTopicAttributes(); + attributes.put("unknown-attribute", "value"); + + assertCreateRejected( + subscriptionInfo, + attributes, + "mode=consensus does not support topic attributes [unknown-attribute]"); + } + + @Test + public void testAllowPipeAttributesOnLiveTopic() throws Exception { + final SubscriptionInfo subscriptionInfo = new SubscriptionInfo(); + final Map<String, String> attributes = newLiveTableTopicAttributes(); + attributes.put(TopicConstant.START_TIME_KEY, "0"); + attributes.put(TopicConstant.STRICT_KEY, "false"); + attributes.put("processor", "custom-processor"); + + Assert.assertTrue( + subscriptionInfo.validateBeforeCreatingTopic( + new TCreateTopicReq("table_topic").setTopicAttributes(attributes))); + } + @Test public void testRejectEmptyColumnFilter() { final SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
