This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 016d58b47fb [Subscription] Relax group credentials and report 
connection failures (#18658)
016d58b47fb is described below

commit 016d58b47fbc406541a7a7d0ac8b3a96d6c89a4e
Author: Caideyipi <[email protected]>
AuthorDate: Thu Sep 17 11:18:34 2026 +0800

    [Subscription] Relax group credentials and report connection failures 
(#18658)
---
 .../subscription/i18n/SubscriptionMessages.java    |   2 +
 .../subscription/i18n/SubscriptionMessages.java    |   2 +
 .../base/AbstractSubscriptionConsumer.java         |  14 ++
 .../base/AbstractSubscriptionProviders.java        |  24 ++-
 .../SubscriptionConsumerConnectionFailureTest.java | 167 +++++++++++++++++++++
 .../iotdb/confignode/i18n/ManagerMessages.java     |   2 +
 .../iotdb/confignode/i18n/ManagerMessages.java     |   2 +
 .../persistence/subscription/SubscriptionInfo.java |   8 +-
 .../meta/consumer/ConsumerGroupMeta.java           |  31 +---
 .../consumer/ConsumerGroupDeSerTest.java           |  26 ++++
 10 files changed, 240 insertions(+), 38 deletions(-)

diff --git 
a/iotdb-client/subscription/src/main/i18n/en/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
 
b/iotdb-client/subscription/src/main/i18n/en/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
index 95f26d55d68..3693c7da2ca 100644
--- 
a/iotdb-client/subscription/src/main/i18n/en/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
+++ 
b/iotdb-client/subscription/src/main/i18n/en/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
@@ -155,6 +155,8 @@ public final class SubscriptionMessages {
       + " provider unavailable";
   public static final String 
LOG_EXCEPTION_OCCURRED_ARG_CLOSING_REMOVING_SUBSCRIPTION_PROVIDER_ARG_BECAUSE_ARG_2EC38739
 = "Exception occurred when {} closing and removing subscription provider {} 
because of {}";
   public static final String 
EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_5DB83198
 = "Cluster has no available subscription providers to connect with initial 
endpoints %s";
+  public static final String 
EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_712D99EA
 =
+      "Cluster has no available subscription providers to connect with initial 
endpoints %s. Connection failures: %s";
   public static final String 
LOG_ARG_HAS_BEEN_LAUNCHED_SET_CORE_POOL_SIZE_ARG_WILL_0FDECBE3 = "{} has been 
launched, set core pool size to {} will be ignored";
   public static final String 
LOG_INTERRUPT_WORKER_WHICH_MAY_CAUSE_SOME_TASK_INCONSISTENT_PLEASE_CHECK_04926D9F
 = "Interrupt the worker, which may cause some task inconsistent. Please check 
the biz logs.";
   public static final String 
LOG_THREAD_POOL_CAN_T_SHUTDOWN_EVEN_INTERRUPTING_WORKER_THREADS_WHICH_A49166F9 =
diff --git 
a/iotdb-client/subscription/src/main/i18n/zh/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
 
b/iotdb-client/subscription/src/main/i18n/zh/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
index 562a4d72572..e3eea001964 100644
--- 
a/iotdb-client/subscription/src/main/i18n/zh/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
+++ 
b/iotdb-client/subscription/src/main/i18n/zh/org/apache/iotdb/rpc/subscription/i18n/SubscriptionMessages.java
@@ -143,6 +143,8 @@ public final class SubscriptionMessages {
   public static final String 
LOG_ARG_FAILED_SENDING_HEARTBEAT_SUBSCRIPTION_PROVIDER_ARG_BECAUSE_ARG_SET_0B38FB1F
 = "{} 无法向 SubscriptionProvider {} 发送心跳,原因:{},将 SubscriptionProvider 设为不可用";
   public static final String 
LOG_EXCEPTION_OCCURRED_ARG_CLOSING_REMOVING_SUBSCRIPTION_PROVIDER_ARG_BECAUSE_ARG_2EC38739
 = "{} 关闭并移除 SubscriptionProvider {} 时发生异常,原因:{}";
   public static final String 
EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_5DB83198
 = "集群没有可连接初始 endpoint %s 的可用 SubscriptionProvider";
+  public static final String 
EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_712D99EA
 =
+      "集群没有可连接初始 endpoint %s 的可用 SubscriptionProvider。连接失败原因:%s";
   public static final String 
LOG_ARG_HAS_BEEN_LAUNCHED_SET_CORE_POOL_SIZE_ARG_WILL_0FDECBE3 = "{} 已启动,将 core 
pool size 设置为 {} 的操作会被忽略";
   public static final String 
LOG_INTERRUPT_WORKER_WHICH_MAY_CAUSE_SOME_TASK_INCONSISTENT_PLEASE_CHECK_04926D9F
 = "中断 worker 可能造成某些任务不一致。请检查业务日志。";
   public static final String 
LOG_THREAD_POOL_CAN_T_SHUTDOWN_EVEN_INTERRUPTING_WORKER_THREADS_WHICH_A49166F9 =
diff --git 
a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionConsumer.java
 
b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionConsumer.java
index 38b16ed7cf9..73a51aa9923 100644
--- 
a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionConsumer.java
+++ 
b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionConsumer.java
@@ -585,6 +585,20 @@ abstract class AbstractSubscriptionConsumer implements 
AutoCloseable {
     return provider;
   }
 
+  String sanitizeConnectionFailureMessage(final Throwable throwable) {
+    String message = throwable.getMessage();
+    if (Objects.isNull(message) || message.isEmpty()) {
+      message = throwable.getClass().getName();
+    }
+    if (Objects.nonNull(password) && !password.isEmpty()) {
+      message = message.replace(password, "***");
+    }
+    if (Objects.nonNull(encryptedPassword) && !encryptedPassword.isEmpty()) {
+      message = message.replace(encryptedPassword, "***");
+    }
+    return message;
+  }
+
   /////////////////////////////// file ops ///////////////////////////////
 
   private Path getFileDir(final String topicName) throws IOException {
diff --git 
a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionProviders.java
 
b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionProviders.java
index be164a7da6a..d89fe998cdf 100644
--- 
a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionProviders.java
+++ 
b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/consumer/base/AbstractSubscriptionProviders.java
@@ -31,6 +31,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -81,6 +82,8 @@ final class AbstractSubscriptionProviders {
     // close stale providers
     closeProviders();
 
+    final Map<TEndPoint, String> connectionFailures = new LinkedHashMap<>();
+    final List<Throwable> connectionFailureCauses = new ArrayList<>();
     for (final TEndPoint endPoint : initialEndpoints) {
       final AbstractSubscriptionProvider defaultProvider;
       final int defaultDataNodeId;
@@ -88,6 +91,8 @@ final class AbstractSubscriptionProviders {
       try {
         defaultProvider = consumer.constructProviderAndHandshake(endPoint);
       } catch (final Exception e) {
+        connectionFailures.put(endPoint, 
consumer.sanitizeConnectionFailureMessage(e));
+        connectionFailureCauses.add(e);
         LOGGER.warn(
             
SubscriptionMessages.LOG_ARG_FAILED_CREATE_CONNECTION_ARG_BECAUSE_ARG_E536E22A,
             consumer,
@@ -136,11 +141,20 @@ final class AbstractSubscriptionProviders {
     }
 
     if (hasNoAvailableProviders()) {
-      throw new SubscriptionConnectionException(
-          String.format(
-              SubscriptionMessages
-                  
.EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_5DB83198,
-              initialEndpoints));
+      final SubscriptionConnectionException exception =
+          new SubscriptionConnectionException(
+              String.format(
+                  SubscriptionMessages
+                      
.EXCEPTION_CLUSTER_HAS_NO_AVAILABLE_SUBSCRIPTION_PROVIDERS_CONNECT_INITIAL_ENDPOINTS_ARG_712D99EA,
+                  initialEndpoints,
+                  connectionFailures),
+              connectionFailureCauses.isEmpty()
+                  ? null
+                  : connectionFailureCauses.get(connectionFailureCauses.size() 
- 1));
+      for (int i = 0; i < connectionFailureCauses.size() - 1; i++) {
+        exception.addSuppressed(connectionFailureCauses.get(i));
+      }
+      throw exception;
     }
 
     nextDataNodeId = subscriptionProviders.firstKey();
diff --git 
a/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/consumer/base/SubscriptionConsumerConnectionFailureTest.java
 
b/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/consumer/base/SubscriptionConsumerConnectionFailureTest.java
new file mode 100644
index 00000000000..9570ec6cf3e
--- /dev/null
+++ 
b/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/consumer/base/SubscriptionConsumerConnectionFailureTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.session.subscription.consumer.base;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import 
org.apache.iotdb.rpc.subscription.exception.SubscriptionConnectionException;
+import org.apache.iotdb.rpc.subscription.exception.SubscriptionException;
+import 
org.apache.iotdb.rpc.subscription.payload.poll.SubscriptionCommitContext;
+import 
org.apache.iotdb.rpc.subscription.payload.response.PipeSubscribeHeartbeatResp;
+import org.apache.iotdb.session.AbstractSessionBuilder;
+import org.apache.iotdb.session.subscription.SubscriptionTreeSessionBuilder;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+public class SubscriptionConsumerConnectionFailureTest {
+
+  private static final String HOST = "127.0.0.1";
+  private static final int FIRST_PORT = 10_011;
+  private static final String PASSWORD = "test-password";
+
+  @Test
+  public void testOpenReportsAllInitialEndpointHandshakeFailures() {
+    final TestPullConsumer consumer = new TestPullConsumer();
+
+    final SubscriptionConnectionException exception =
+        Assert.assertThrows(SubscriptionConnectionException.class, 
consumer::open);
+
+    Assert.assertTrue(exception.getMessage().contains(HOST));
+    
Assert.assertTrue(exception.getMessage().contains(String.valueOf(FIRST_PORT)));
+    
Assert.assertTrue(exception.getMessage().contains(String.valueOf(FIRST_PORT + 
1)));
+    Assert.assertTrue(exception.getMessage().contains("first endpoint 
handshake failed"));
+    Assert.assertTrue(exception.getMessage().contains("second endpoint 
handshake failed"));
+    Assert.assertFalse(exception.getMessage().contains(PASSWORD));
+    Assert.assertNotNull(exception.getCause());
+    Assert.assertEquals(1, exception.getSuppressed().length);
+  }
+
+  private static class TestPullConsumer extends 
AbstractSubscriptionPullConsumer {
+
+    private TestPullConsumer() {
+      super(
+          new AbstractSubscriptionPullConsumerBuilder()
+              .nodeUrls(Arrays.asList(HOST + ":" + FIRST_PORT, HOST + ":" + 
(FIRST_PORT + 1)))
+              .username("test-user")
+              .password(PASSWORD));
+    }
+
+    @Override
+    protected AbstractSubscriptionProvider constructSubscriptionProvider(
+        final TEndPoint endPoint,
+        final String username,
+        final String password,
+        final String encryptedPassword,
+        final String consumerId,
+        final String consumerGroupId,
+        final String ownerId,
+        final Long ownerEpoch,
+        final int thriftMaxFrameSize,
+        final long heartbeatIntervalMs,
+        final int connectionTimeoutInMs) {
+      return new TestSubscriptionProvider(
+          endPoint,
+          username,
+          password,
+          encryptedPassword,
+          consumerId,
+          consumerGroupId,
+          ownerId,
+          ownerEpoch,
+          thriftMaxFrameSize,
+          heartbeatIntervalMs,
+          connectionTimeoutInMs);
+    }
+  }
+
+  private static class TestSubscriptionProvider extends 
AbstractSubscriptionProvider {
+
+    private TestSubscriptionProvider(
+        final TEndPoint endPoint,
+        final String username,
+        final String password,
+        final String encryptedPassword,
+        final String consumerId,
+        final String consumerGroupId,
+        final String ownerId,
+        final Long ownerEpoch,
+        final int thriftMaxFrameSize,
+        final long heartbeatIntervalMs,
+        final int connectionTimeoutInMs) {
+      super(
+          endPoint,
+          username,
+          password,
+          encryptedPassword,
+          consumerId,
+          consumerGroupId,
+          ownerId,
+          ownerEpoch,
+          thriftMaxFrameSize,
+          heartbeatIntervalMs,
+          connectionTimeoutInMs);
+    }
+
+    @Override
+    protected AbstractSessionBuilder constructSubscriptionSessionBuilder(
+        final String host,
+        final int port,
+        final String username,
+        final String password,
+        final String encryptedPassword,
+        final int thriftMaxFrameSize,
+        final int connectionTimeoutInMs) {
+      final boolean useEncryptedPassword = Objects.nonNull(encryptedPassword);
+      return new SubscriptionTreeSessionBuilder()
+          .host(host)
+          .port(port)
+          .username(username)
+          .password(useEncryptedPassword ? encryptedPassword : password)
+          .useEncryptedPassword(useEncryptedPassword)
+          .thriftMaxFrameSize(thriftMaxFrameSize)
+          .connectionTimeoutInMs(connectionTimeoutInMs);
+    }
+
+    @Override
+    synchronized void handshake() throws SubscriptionException {
+      throw new SubscriptionConnectionException(
+          (getEndPoint().port == FIRST_PORT
+                  ? "first endpoint handshake failed"
+                  : "second endpoint handshake failed")
+              + " with password "
+              + PASSWORD);
+    }
+
+    @Override
+    synchronized void close() {
+      setUnavailable();
+    }
+
+    @Override
+    PipeSubscribeHeartbeatResp heartbeat(
+        final List<SubscriptionCommitContext> processorBufferedCommitContexts) 
{
+      return new PipeSubscribeHeartbeatResp();
+    }
+  }
+}
diff --git 
a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ManagerMessages.java
 
b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ManagerMessages.java
index 10ea4763af9..a61e8e2aa5b 100644
--- 
a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ManagerMessages.java
+++ 
b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ManagerMessages.java
@@ -143,6 +143,8 @@ public final class ManagerMessages {
       "Failed to collect user name for user id {}";
   public static final String 
FAILED_TO_CREATE_CONSUMER_IN_CONSUMER_GROUP_RESULT_STATUS =
       "Failed to create consumer {} in consumer group {}. Result status: {}.";
+  public static final String 
EXCEPTION_FAILED_TO_SUBSCRIBE_TOPIC_ARG_FOR_CONSUMER_ARG_CONSUMER_DOES_NOT_EXIST_IN_CONSUMER_GROUP_BFF68F12
 =
+      "Failed to subscribe topic %s for consumer %s because the consumer does 
not exist in the consumer group";
   public static final String FAILED_TO_CREATE_PEER_FOR_CONSENSUS_GROUP =
       "Failed to create peer for consensus group";
   public static final String FAILED_TO_CREATE_PIPE_RESULT_STATUS =
diff --git 
a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ManagerMessages.java
 
b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ManagerMessages.java
index 499e922f7a7..fa8fc8e3217 100644
--- 
a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ManagerMessages.java
+++ 
b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ManagerMessages.java
@@ -142,6 +142,8 @@ public final class ManagerMessages {
       "为用户 id {} 收集用户名失败";
   public static final String 
FAILED_TO_CREATE_CONSUMER_IN_CONSUMER_GROUP_RESULT_STATUS =
       "在 consumer group {} 中创建 consumer {} 失败。结果状态:{}。";
+  public static final String 
EXCEPTION_FAILED_TO_SUBSCRIBE_TOPIC_ARG_FOR_CONSUMER_ARG_CONSUMER_DOES_NOT_EXIST_IN_CONSUMER_GROUP_BFF68F12
 =
+      "订阅 topic %s 失败,因为 consumer %s 不存在于 consumer group 中";
   public static final String FAILED_TO_CREATE_PEER_FOR_CONSENSUS_GROUP =
       "为 consensus group 创建 peer 失败";
   public static final String FAILED_TO_CREATE_PIPE_RESULT_STATUS =
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 36b6f35c31d..6ea5152f4df 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
@@ -1144,12 +1144,14 @@ public class SubscriptionInfo implements 
SnapshotProcessor {
         throw new SubscriptionException(exceptionMessage);
       }
 
-      // 2.2. check username
+      // 2.2. check consumer membership
       if (!consumerGroupMeta.allowSubscribeTopicForConsumer(topicName, 
consumerId)) {
         final String exceptionMessage =
             String.format(
-                "Failed to subscribe topic %s for consumer %s because 
inconsistent username under the same consumer group",
-                topicName, consumerId);
+                ManagerMessages
+                    
.EXCEPTION_FAILED_TO_SUBSCRIBE_TOPIC_ARG_FOR_CONSUMER_ARG_CONSUMER_DOES_NOT_EXIST_IN_CONSUMER_GROUP_BFF68F12,
+                topicName,
+                consumerId);
         LOGGER.warn(exceptionMessage);
         throw new SubscriptionException(exceptionMessage);
       }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/subscription/meta/consumer/ConsumerGroupMeta.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/subscription/meta/consumer/ConsumerGroupMeta.java
index c6153db8668..2efffb89182 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/subscription/meta/consumer/ConsumerGroupMeta.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/subscription/meta/consumer/ConsumerGroupMeta.java
@@ -149,22 +149,6 @@ public class ConsumerGroupMeta {
       return;
     }
     final ConsumerMeta existedConsumerMeta = 
consumerIdToConsumerMeta.values().iterator().next();
-    final boolean match =
-        Objects.equals(existedConsumerMeta.getUsername(), 
consumerMeta.getUsername())
-            && Objects.equals(existedConsumerMeta.getPassword(), 
consumerMeta.getPassword());
-    if (!match) {
-      final String exceptionMessage =
-          String.format(
-              "Failed to create consumer %s because inconsistent username & 
password under the same consumer group, expected %s:%s, actual %s:%s",
-              consumerMeta.getConsumerId(),
-              existedConsumerMeta.getUsername(),
-              existedConsumerMeta.getPassword(),
-              consumerMeta.getUsername(),
-              consumerMeta.getPassword());
-      LOGGER.warn(exceptionMessage);
-      throw new SubscriptionException(exceptionMessage);
-    }
-
     final String expectedSqlDialect = 
existedConsumerMeta.getConfig().getSqlDialect();
     final String actualSqlDialect = consumerMeta.getConfig().getSqlDialect();
     final boolean isExpectedTableModel =
@@ -268,20 +252,7 @@ public class ConsumerGroupMeta {
   }
 
   public boolean allowSubscribeTopicForConsumer(final String topic, final 
String consumerId) {
-    if (!consumerIdToConsumerMeta.containsKey(consumerId)) {
-      return false;
-    }
-    final Set<String> subscribedConsumerIdSet = 
topicNameToSubscribedConsumerIdSet.get(topic);
-    if (Objects.isNull(subscribedConsumerIdSet)) {
-      return true;
-    }
-    if (subscribedConsumerIdSet.isEmpty()) {
-      return true;
-    }
-    final String subscribedConsumerId = 
subscribedConsumerIdSet.iterator().next();
-    return Objects.equals(
-        
Objects.requireNonNull(consumerIdToConsumerMeta.get(subscribedConsumerId)).getUsername(),
-        
Objects.requireNonNull(consumerIdToConsumerMeta.get(consumerId)).getUsername());
+    return consumerIdToConsumerMeta.containsKey(consumerId);
   }
 
   public void addSubscription(final String consumerId, final Set<String> 
topics) {
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/subscription/consumer/ConsumerGroupDeSerTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/subscription/consumer/ConsumerGroupDeSerTest.java
index fc8d0122b2b..589571ab2b2 100644
--- 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/subscription/consumer/ConsumerGroupDeSerTest.java
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/subscription/consumer/ConsumerGroupDeSerTest.java
@@ -114,4 +114,30 @@ public class ConsumerGroupDeSerTest {
             consumerGroupMeta.checkAuthorityBeforeJoinConsumerGroup(
                 new ConsumerMeta("table_consumer", 2, 
tableConsumerAttributes)));
   }
+
+  @Test
+  public void testConsumerGroupShouldAllowDifferentCredentials() throws 
SubscriptionException {
+    final Map<String, String> firstConsumerAttributes = new HashMap<>();
+    firstConsumerAttributes.put("username", "first_user");
+    firstConsumerAttributes.put("password", "first_password");
+
+    final Map<String, String> secondConsumerAttributes = new HashMap<>();
+    secondConsumerAttributes.put("username", "second_user");
+    secondConsumerAttributes.put("password", "second_password");
+
+    final ConsumerGroupMeta consumerGroupMeta =
+        new ConsumerGroupMeta(
+            "test_consumer_group",
+            1,
+            new ConsumerMeta("first_consumer", 1, firstConsumerAttributes));
+    final ConsumerMeta secondConsumer =
+        new ConsumerMeta("second_consumer", 2, secondConsumerAttributes);
+
+    consumerGroupMeta.checkAuthorityBeforeJoinConsumerGroup(secondConsumer);
+    consumerGroupMeta.addConsumer(secondConsumer);
+    consumerGroupMeta.addSubscription("first_consumer", 
Collections.singleton("test_topic"));
+
+    Assert.assertTrue(
+        consumerGroupMeta.allowSubscribeTopicForConsumer("test_topic", 
"second_consumer"));
+  }
 }

Reply via email to