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

zirui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new eb296a1e8 [INLONG-4664][TubeMQ] Add GroupController and 
TopicBackendWorker (#4679)
eb296a1e8 is described below

commit eb296a1e8dea1360a4355d12163bd0af19c5bc58
Author: Lizhen <[email protected]>
AuthorDate: Fri Jun 17 17:30:30 2022 +0800

    [INLONG-4664][TubeMQ] Add GroupController and TopicBackendWorker (#4679)
---
 .../manager/controller/group/GroupController.java  |  15 +++
 .../group/request/QueryConsumerGroupReq.java}      |  28 +++--
 .../tubemq/manager/service/NodeServiceImpl.java    | 102 ++++++++++++++-
 .../tubemq/manager/service/TopicBackendWorker.java | 140 +++++++++++++++++++++
 .../inlong/tubemq/manager/service/TopicFuture.java |  59 +++++++++
 .../tubemq/manager/service/TopicServiceImpl.java   |  13 ++
 .../tubemq/manager/service/TubeMQErrorConst.java   |   1 +
 .../manager/service/interfaces/NodeService.java    |   9 ++
 .../manager/service/interfaces/TopicService.java   |   9 ++
 9 files changed, 367 insertions(+), 9 deletions(-)

diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/GroupController.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/GroupController.java
index 36d174c24..a5af0fc85 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/GroupController.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/GroupController.java
@@ -29,6 +29,7 @@ import 
org.apache.inlong.tubemq.manager.controller.group.request.DeleteBlackGrou
 import 
org.apache.inlong.tubemq.manager.controller.group.request.DeleteOffsetReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.FilterCondGroupReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.FlowControlGroupReq;
+import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryConsumerGroupReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryOffsetReq;
 import org.apache.inlong.tubemq.manager.controller.node.request.CloneOffsetReq;
 import 
org.apache.inlong.tubemq.manager.controller.topic.request.BatchAddGroupAuthReq;
@@ -86,11 +87,25 @@ public class GroupController {
                 return masterService.baseRequestMaster(gson.fromJson(req, 
FilterCondGroupReq.class));
             case TubeConst.FLOW_CONTROL:
                 return masterService.baseRequestMaster(gson.fromJson(req, 
FlowControlGroupReq.class));
+            case TubeConst.QUERY:
+                return queryGroupExist(gson.fromJson(req, 
QueryConsumerGroupReq.class));
             default:
                 return 
TubeMQResult.errorResult(TubeMQErrorConst.NO_SUCH_METHOD);
         }
     }
 
+    /**
+     * query group exist
+     * @param req
+     * @return
+     */
+    private TubeMQResult queryGroupExist(QueryConsumerGroupReq req) {
+        if (!req.legal()) {
+            return TubeMQResult.errorResult(TubeMQErrorConst.PARAM_ILLEGAL);
+        }
+        return topicService.queryGroupExist(req);
+    }
+
     /**
      * add groups in one batch
      *
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/request/QueryConsumerGroupReq.java
similarity index 56%
copy from 
inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
copy to 
inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/request/QueryConsumerGroupReq.java
index f59313f00..09a6ece2d 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/controller/group/request/QueryConsumerGroupReq.java
@@ -15,13 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.tubemq.manager.service;
+package org.apache.inlong.tubemq.manager.controller.group.request;
 
-public class TubeMQErrorConst {
-    public static final String PARAM_ILLEGAL = "param illegal";
-    public static final String BROKER_IN_OTHER_REGION = "resource already 
used";
-    public static final String RESOURCE_NOT_EXIST = "resource not exsit";
-    public static final String MYSQL_ERROR = "mysql error";
-    public static final String NO_SUCH_CLUSTER = "no such cluster";
-    public static final String NO_SUCH_METHOD = "no such method";
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.inlong.tubemq.manager.controller.node.request.BaseReq;
+
+/**
+ * query consumer group
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class QueryConsumerGroupReq extends BaseReq {
+    private String consumerGroup;
+    private String topicName;
+
+    public boolean legal() {
+        return StringUtils.isNotBlank(topicName) && 
StringUtils.isNotBlank(consumerGroup);
+    }
 }
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/NodeServiceImpl.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/NodeServiceImpl.java
index 40791ff5d..2e41637df 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/NodeServiceImpl.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/NodeServiceImpl.java
@@ -25,7 +25,9 @@ import com.google.gson.Gson;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import lombok.extern.slf4j.Slf4j;
@@ -71,9 +73,14 @@ public class NodeServiceImpl implements NodeService {
     private final CloseableHttpClient httpclient = HttpClients.createDefault();
     private final Gson gson = new Gson();
 
-    @Value("${manager.max.configurable.broker.size:50}")
+    @Value("${manager.max.configurable.broker.size:1}")
     private int maxConfigurableBrokerSize;
 
+    @Value("${manager.max.retry.adding.topic:10}")
+    private int maxRetryAddingTopic;
+
+    private final TopicBackendWorker worker;
+
     @Autowired
     private MasterRepository masterRepository;
 
@@ -83,6 +90,10 @@ public class NodeServiceImpl implements NodeService {
     @Autowired
     private MasterService masterService;
 
+    public NodeServiceImpl(TopicBackendWorker worker) {
+        this.worker = worker;
+    }
+
     /**
      * request node status via http.
      *
@@ -281,6 +292,95 @@ public class NodeServiceImpl implements NodeService {
         } while (end < needReloadList.size());
     }
 
+    /**
+     * handle result, if success, complete it,
+     * if not success, add back to queue without exceeding max retry,
+     * otherwise complete it with exception.
+     *
+     * @param isSuccess
+     * @param topics
+     * @param pendingTopic
+     */
+    private void handleAddingResult(boolean isSuccess, Set<String> topics,
+            Map<String, TopicFuture> pendingTopic) {
+        for (String topic : topics) {
+            TopicFuture future = pendingTopic.get(topic);
+            if (future != null) {
+                if (isSuccess) {
+                    future.complete();
+                } else {
+                    future.increaseRetryTime();
+                    if (future.getRetryTime() > maxRetryAddingTopic) {
+                        future.completeExceptional();
+                    } else {
+                        // add back to queue.
+                        worker.addTopicFuture(future);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adding topic is an async operation, so this method should
+     * 1. check whether pendingTopic contains topic that has failed/succeeded 
to be added.
+     * 2. async add topic to tubemq cluster
+     *
+     * @param brokerInfoList - broker list
+     * @param pendingTopic - topicMap
+     */
+    private void handleAddingTopic(MasterEntry masterEntry,
+            TubeHttpBrokerInfoList brokerInfoList,
+            Map<String, TopicFuture> pendingTopic) {
+        // 1. check tubemq cluster by topic name, remove pending topic if has 
added.
+        Set<String> brandNewTopics = new HashSet<>();
+        for (String topic : pendingTopic.keySet()) {
+            TubeHttpTopicInfoList topicInfoList = 
topicService.requestTopicConfigInfo(masterEntry, topic);
+            if (topicInfoList != null) {
+                // get broker list by topic request
+                List<Integer> topicBrokerList = 
topicInfoList.getTopicBrokerIdList();
+                if (topicBrokerList.isEmpty()) {
+                    brandNewTopics.add(topic);
+                } else {
+                    // remove brokers which have been added.
+                    List<Integer> configurableBrokerIdList =
+                            brokerInfoList.getConfigurableBrokerIdList();
+                    configurableBrokerIdList.removeAll(topicBrokerList);
+                    // add topic to satisfy max broker number.
+                    Set<String> singleTopic = new HashSet<>();
+                    singleTopic.add(topic);
+                    int maxBrokers = Math.min(maxConfigurableBrokerSize, 
configurableBrokerIdList.size());
+                    boolean isSuccess = configBrokersForTopics(masterEntry, 
singleTopic,
+                            configurableBrokerIdList, maxBrokers);
+                    handleAddingResult(isSuccess, singleTopic, pendingTopic);
+                }
+            }
+        }
+        // 2. add new topics to cluster
+        List<Integer> configurableBrokerIdList = 
brokerInfoList.getConfigurableBrokerIdList();
+        int maxBrokers = Math.min(maxConfigurableBrokerSize, 
configurableBrokerIdList.size());
+        boolean isSuccess = configBrokersForTopics(masterEntry, brandNewTopics,
+                configurableBrokerIdList, maxBrokers);
+        handleAddingResult(isSuccess, brandNewTopics, pendingTopic);
+    }
+
+    @Override
+    public void updateBrokerStatus(int clusterId, Map<String, TopicFuture> 
pendingTopic) {
+        MasterEntry masterEntry = 
masterRepository.findMasterEntryByClusterIdEquals(clusterId);
+        if (masterEntry != null) {
+            try {
+                TubeHttpBrokerInfoList brokerInfoList = 
requestBrokerStatus(masterEntry);
+                if (brokerInfoList != null) {
+                    handleAddingTopic(masterEntry, brokerInfoList, 
pendingTopic);
+                }
+            } catch (Exception ex) {
+                log.error("exception caught while requesting broker status", 
ex);
+            }
+        } else {
+            log.error("cannot get master ip by clusterId {}, please check it", 
clusterId);
+        }
+    }
+
     @Override
     public void close() throws IOException {
         httpclient.close();
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicBackendWorker.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicBackendWorker.java
new file mode 100644
index 000000000..aaed365bf
--- /dev/null
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicBackendWorker.java
@@ -0,0 +1,140 @@
+/*
+ * 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.inlong.tubemq.manager.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.inlong.tubemq.manager.repository.TopicRepository;
+import org.apache.inlong.tubemq.manager.service.interfaces.NodeService;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Topic backend thread worker.
+ */
+@Component
+@Slf4j
+public class TopicBackendWorker implements DisposableBean, Runnable  {
+    // old code, stop first
+    private final AtomicBoolean runFlag = new AtomicBoolean(false);
+    private final ConcurrentHashMap<Integer, BlockingQueue<TopicFuture>> 
pendingTopics =
+            new ConcurrentHashMap<>();
+    private final AtomicInteger notSatisfiedCount = new AtomicInteger(0);
+    private final NodeService nodeService;
+
+    @Autowired
+    private TopicRepository topicRepository;
+
+    @Value("${manager.topic.queue.warning.size:100}")
+    private int queueWarningSize;
+
+    // value in seconds
+    @Value("${manager.topic.queue.thread.interval:10}")
+    private int queueThreadInterval;
+
+    @Value("${manager.topic.queue.max.wait:3}")
+    private int queueMaxWait;
+
+    @Value("${manager.topic.queue.max.running.size:20}")
+    private int queueMaxRunningSize;
+
+    TopicBackendWorker() {
+        Thread thread = new Thread(this);
+        // daemon thread
+        thread.setDaemon(true);
+        thread.start();
+        nodeService = new NodeServiceImpl(this);
+    }
+
+    /**
+     * add topic future to pending executing queue.
+     * @param future - TopicFuture.
+     */
+    public void addTopicFuture(TopicFuture future) {
+        BlockingQueue<TopicFuture> tmpQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<TopicFuture> queue = pendingTopics.putIfAbsent(
+                future.getEntry().getClusterId(), tmpQueue);
+        if (queue == null) {
+            queue = tmpQueue;
+        }
+        queue.add(future);
+        if (queue.size() > queueWarningSize) {
+            log.warn("queue size exceed {}, please check it", 
queueWarningSize);
+        }
+    }
+
+    /**
+     * batch executing adding topic, wait util max n seconds or max size 
satisfied.
+     */
+    private void batchAddTopic() {
+        pendingTopics.forEach((clusterId, queue) -> {
+            Map<String, TopicFuture> pendingTopicList = new HashMap<>(32);
+            if (notSatisfiedCount.get() > queueMaxWait || queue.size() > 
queueMaxRunningSize) {
+                notSatisfiedCount.set(0);
+                List<TopicFuture> tmpTopicList = new ArrayList<>();
+                queue.drainTo(tmpTopicList, queueMaxRunningSize);
+                for (TopicFuture topicFuture : tmpTopicList) {
+                    pendingTopicList.put(topicFuture.getEntry().getTopic(), 
topicFuture);
+                }
+            } else {
+                notSatisfiedCount.incrementAndGet();
+            }
+            // update broker status
+            nodeService.updateBrokerStatus(clusterId, pendingTopicList);
+        });
+
+    }
+
+    /**
+     * check topic from db
+     */
+    private void checkTopicFromDB() {
+    }
+
+    @Override
+    public void run() {
+        log.info("TopicBackendWorker has started");
+        while (runFlag.get()) {
+            try {
+                batchAddTopic();
+                checkTopicFromDB();
+                TimeUnit.SECONDS.sleep(queueThreadInterval);
+            } catch (Exception exception) {
+                log.warn("exception caught", exception);
+            }
+        }
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        runFlag.set(false);
+        nodeService.close();
+    }
+}
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicFuture.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicFuture.java
new file mode 100644
index 000000000..89139fd04
--- /dev/null
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicFuture.java
@@ -0,0 +1,59 @@
+/*
+ * 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.inlong.tubemq.manager.service;
+
+import lombok.Getter;
+import org.apache.inlong.tubemq.manager.entry.TopicEntry;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * topic business with future.
+ */
+public class TopicFuture {
+    @Getter
+    private int retryTime = 0;
+    @Getter
+    private final TopicEntry entry;
+    @Getter
+    private final CompletableFuture<TopicEntry> future;
+
+    public TopicFuture(TopicEntry entry, CompletableFuture<TopicEntry> future) 
{
+        this.entry = entry;
+        this.future = future;
+    }
+
+    /**
+     * record retry time.
+     */
+    public void increaseRetryTime() {
+        retryTime += 1;
+    }
+
+    /**
+     * when topic operation finished, complete it.
+     */
+    public void complete() {
+        this.future.complete(this.entry);
+    }
+
+    public void completeExceptional() {
+        this.future.completeExceptionally(new RuntimeException("exceed max 
retry "
+                + retryTime + " adding"));
+    }
+}
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicServiceImpl.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicServiceImpl.java
index 49e2dd873..f088815bd 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicServiceImpl.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TopicServiceImpl.java
@@ -33,6 +33,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.inlong.tubemq.manager.controller.TubeMQResult;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.DeleteOffsetReq;
+import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryConsumerGroupReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryOffsetReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.result.AllBrokersOffsetRes;
 import 
org.apache.inlong.tubemq.manager.controller.group.result.AllBrokersOffsetRes.OffsetInfo;
@@ -98,6 +99,18 @@ public class TopicServiceImpl implements TopicService {
         return null;
     }
 
+    @Override
+    public TubeMQResult queryGroupExist(QueryConsumerGroupReq req) {
+        MasterEntry masterNode = masterService.getMasterNode(req);
+        TubeHttpGroupDetailInfo groupDetailInfo = 
requestGroupRunInfo(masterNode,
+                req.getConsumerGroup());
+        List<String> topicSet = groupDetailInfo.getTopicSet();
+        if (topicSet.stream().anyMatch(topic -> 
topic.equals(req.getTopicName()))) {
+            return TubeMQResult.successResult();
+        }
+        return TubeMQResult.errorResult(TubeMQErrorConst.NO_SUCH_GROUP);
+    }
+
     @Override
     public TopicView requestTopicViewInfo(Long clusterId, String topicName) {
         MasterEntry masterNode = masterService.getMasterNode(clusterId);
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
index f59313f00..21b47e3b3 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/TubeMQErrorConst.java
@@ -24,4 +24,5 @@ public class TubeMQErrorConst {
     public static final String MYSQL_ERROR = "mysql error";
     public static final String NO_SUCH_CLUSTER = "no such cluster";
     public static final String NO_SUCH_METHOD = "no such method";
+    public static final String NO_SUCH_GROUP = "no such group";
 }
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/NodeService.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/NodeService.java
index a2d76e7a3..f440f3485 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/NodeService.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/NodeService.java
@@ -19,6 +19,7 @@ package org.apache.inlong.tubemq.manager.service.interfaces;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.inlong.tubemq.manager.controller.TubeMQResult;
@@ -28,6 +29,7 @@ import 
org.apache.inlong.tubemq.manager.controller.node.request.CloneBrokersReq;
 import org.apache.inlong.tubemq.manager.controller.node.request.CloneTopicReq;
 import org.apache.inlong.tubemq.manager.entry.ClusterEntry;
 import org.apache.inlong.tubemq.manager.entry.MasterEntry;
+import org.apache.inlong.tubemq.manager.service.TopicFuture;
 import org.apache.inlong.tubemq.manager.service.tube.TubeHttpBrokerInfoList;
 
 public interface NodeService {
@@ -86,6 +88,13 @@ public interface NodeService {
 
     void handleReloadBroker(MasterEntry masterEntry, List<Integer> 
needReloadList, ClusterEntry clusterEntry);
 
+    /**
+     * update broker status
+     * @param clusterId
+     * @param pendingTopic
+     */
+    void updateBrokerStatus(int clusterId, Map<String, TopicFuture> 
pendingTopic);
+
     void close() throws IOException;
 
     /**
diff --git 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/TopicService.java
 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/TopicService.java
index ae38be90a..d40dc45ad 100644
--- 
a/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/TopicService.java
+++ 
b/inlong-tubemq/tubemq-manager/src/main/java/org/apache/inlong/tubemq/manager/service/interfaces/TopicService.java
@@ -19,6 +19,7 @@ package org.apache.inlong.tubemq.manager.service.interfaces;
 
 import org.apache.inlong.tubemq.manager.controller.TubeMQResult;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.DeleteOffsetReq;
+import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryConsumerGroupReq;
 import 
org.apache.inlong.tubemq.manager.controller.group.request.QueryOffsetReq;
 import org.apache.inlong.tubemq.manager.controller.node.request.CloneOffsetReq;
 import 
org.apache.inlong.tubemq.manager.controller.topic.request.RebalanceGroupReq;
@@ -38,6 +39,14 @@ public interface TopicService {
      */
     TubeHttpGroupDetailInfo requestGroupRunInfo(MasterEntry masterEntry, 
String group);
 
+    /**
+     * query if a group exist with a topic
+     *
+     * @param req
+     * @return
+     */
+    TubeMQResult queryGroupExist(QueryConsumerGroupReq req);
+
     TopicView requestTopicViewInfo(Long clusterId, String topicName);
 
     /**

Reply via email to