Repository: apache/seatunnel
Pull request #8825: [Improve][Connector-V2] RocketMQ Source add message tag config
Author: login: wuchunfu
Labels: name: document
name: approved
name: e2e
Created: 2025-02-25T09:30:45Z | Merged: 2025-03-13T07:18:15Z

<!--

Thank you for contributing to SeaTunnel! Please make sure that your code changes
are covered with tests. And in case of new features or big changes
remember to adjust the documentation.

Feel free to ping committers for the review!

## Contribution Checklist
  - Make sure that the pull request corresponds to a [GITHUB issue](https://github.com/apache/seatunnel/issues).
  - Name the pull request in the form "[Feature] [component] Title of the pull request", where *Feature* can be replaced by `Hotfix`, `Bug`, etc.
  - Minor fixes should be named following this pattern: `[hotfix] [docs] Fix typo in README.md doc`.
-->

### Purpose of this pull request

<!-- Describe the purpose of this pull request. For example: This pull request adds checkstyle plugin.-->
RocketMQ Source add message tag config

### Does this PR introduce _any_ user-facing change?

<!--
Note that it means *any* user-facing change including all aspects such as the documentation fix.
If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change compared to the released SeaTunnel versions or within the unreleased branches such as dev.
If no, write 'No'.
If you are adding/modifying connector documents, please follow our new specifications: https://github.com/apache/seatunnel/issues/4544.
-->


### How was this patch tested?

<!--
If tests were added, say they were added here. Please make sure to add some test cases that check the changes thoroughly including negative and positive cases if possible.
If it was tested in a way different from regular unit tests, please clarify how you tested step by step, ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future.
If tests were not added, please describe why they were not added and/or why it was difficult to add.
If you are adding E2E test cases, maybe refer to https://github.com/apache/seatunnel/blob/dev/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-cdc-mysql-e2e/src/test/resources/mysqlcdc_to_mysql.conf, here is a good example.
-->


### Check list

* [ ] If any new Jar binary package adding in your PR, please add License Notice according
  [New License Guide](https://github.com/apache/seatunnel/blob/dev/docs/en/contribution/new-license.md)
* [ ] If necessary, please update the documentation to describe the new feature. https://github.com/apache/seatunnel/tree/dev/docs
* [ ] If you are contributing the connector code, please check that the following files are updated:
  1. Update [plugin-mapping.properties](https://github.com/apache/seatunnel/blob/dev/plugin-mapping.properties) and add new connector information in it
  2. Update the pom file of [seatunnel-dist](https://github.com/apache/seatunnel/blob/dev/seatunnel-dist/pom.xml)
  3. Add ci label in [label-scope-conf](https://github.com/apache/seatunnel/blob/dev/.github/workflows/labeler/label-scope-conf.yml)
  4. Add e2e testcase in [seatunnel-e2e](https://github.com/apache/seatunnel/tree/dev/seatunnel-e2e/seatunnel-connector-v2-e2e/)
  5. Update connector [plugin_config](https://github.com/apache/seatunnel/blob/dev/config/plugin_config)
* [ ] Update the [`release-note`](https://github.com/apache/seatunnel/blob/dev/release-note.md).


Diff:
diff --git a/docs/en/connector-v2/source/RocketMQ.md b/docs/en/connector-v2/source/RocketMQ.md
index 0691afbc72fb..948e17b11b6b 100644
--- a/docs/en/connector-v2/source/RocketMQ.md
+++ b/docs/en/connector-v2/source/RocketMQ.md
@@ -27,10 +27,11 @@ Source connector for Apache RocketMQ.
 
 ## Source Options
 
-|                Name                 |  Type   | Required |          Default           |                                                                                                    Description                                                                                                     |
+| Name                                |  Type   | Required |          Default           | Description                                                                                                                                                                                                        |
 |-------------------------------------|---------|----------|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | topics                              | String  | yes      | -                          | `RocketMQ topic` name. If there are multiple `topics`, use `,` to split, for example: `"tpc1,tpc2"`.                                                                                                               |
 | name.srv.addr                       | String  | yes      | -                          | `RocketMQ` name server cluster address.                                                                                                                                                                            |
+| tags                                | String  | no       | -                          | `RocketMQ tag` name. If there are multiple `tags`, use `,` to split, for example: `"tag1,tag2"`.                                                                                                                   |
 | acl.enabled                         | Boolean | no       | false                      | If true, access control is enabled, and access key and secret key need to be configured.                                                                                                                           |
 | access.key                          | String  | no       |                            |                                                                                                                                                                                                                    |
 | secret.key                          | String  | no       |                            | When ACL_ENABLED is true, secret key cannot be empty.                                                                                                                                                              |
@@ -218,3 +219,62 @@ sink {
 }
 ```
 
+### Specified tag example:
+
+> Here you can specify a tag to consume data. If there are multiple tags, use `,` to separate them, for example: "tag1,tag2"
+
+```hocon
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+  
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 2
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "localhost:9876"
+    topics = "test_topic"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "test_tag"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+  # If you would like to get more information about how to configure seatunnel and see full list of transform plugins,
+  # please go to https://seatunnel.apache.org/docs/category/transform
+}
+
+sink {
+  Console {
+    plugin_input = "rocketmq_table"
+  }
+}
+```
diff --git a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/config/ConsumerConfig.java b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/config/ConsumerConfig.java
index 642278792ed5..db778dc9a34d 100644
--- a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/config/ConsumerConfig.java
+++ b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/config/ConsumerConfig.java
@@ -29,8 +29,17 @@ public class ConsumerConfig extends Config {
                     .stringType()
                     .noDefaultValue()
                     .withDescription(
-                            "RocketMq topic name. If there are multiple topics, use , to split, for example: "
+                            "RocketMq topic name. If there are multiple topics, use `,` to split, for example: "
                                     + "\"tpc1,tpc2\".");
+
+    public static final Option<String> TAGS =
+            Options.key("tags")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "RocketMq tag name. If there are multiple tags, use `,` to split, for example: "
+                                    + "\"tag1,tag2\".");
+
     public static final Option<String> CONSUMER_GROUP =
             Options.key("consumer.group")
                     .stringType()
diff --git a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/ConsumerMetadata.java b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/ConsumerMetadata.java
index 77597ed16490..ed814953692f 100644
--- a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/ConsumerMetadata.java
+++ b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/ConsumerMetadata.java
@@ -33,6 +33,7 @@
 public class ConsumerMetadata implements Serializable {
     private RocketMqBaseConfiguration baseConfig = RocketMqBaseConfiguration.newBuilder().build();
     private List<String> topics;
+    private List<String> tags;
     private boolean enabledCommitCheckpoint = false;
     private StartMode startMode;
     private Map<MessageQueue, Long> specificStartOffsets;
diff --git a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSource.java b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSource.java
index 3558d3a5f86b..d99af9a8aee6 100644
--- a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSource.java
+++ b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSource.java
@@ -55,8 +55,10 @@
 import com.google.auto.service.AutoService;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static org.apache.seatunnel.connectors.seatunnel.rocketmq.config.Config.ACCESS_KEY;
 import static org.apache.seatunnel.connectors.seatunnel.rocketmq.config.Config.ACL_ENABLED;
@@ -112,6 +114,20 @@ public void prepare(Config config) throws PrepareFailException {
                         config.getString(ConsumerConfig.TOPICS.key())
                                 .split(DEFAULT_FIELD_DELIMITER)));
 
+        if (config.hasPath(ConsumerConfig.TAGS.key())) {
+            String tags = config.getString(ConsumerConfig.TAGS.key());
+            if (tags != null && !tags.trim().isEmpty()) {
+                this.metadata.setTags(
+                        Arrays.stream(tags.split(DEFAULT_FIELD_DELIMITER))
+                                .map(String::trim)
+                                .filter(tag -> !tag.isEmpty())
+                                .distinct()
+                                .collect(Collectors.toList()));
+            } else {
+                this.metadata.setTags(Collections.emptyList());
+            }
+        }
+
         RocketMqBaseConfiguration.Builder baseConfigBuilder =
                 RocketMqBaseConfiguration.newBuilder()
                         .consumer()
diff --git a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceFactory.java b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceFactory.java
index fd04ac37d884..12c46031f7fc 100644
--- a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceFactory.java
+++ b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceFactory.java
@@ -41,6 +41,7 @@ public OptionRule optionRule() {
                 .required(ConsumerConfig.TOPICS, Config.NAME_SRV_ADDR)
                 .optional(
                         Config.FORMAT,
+                        ConsumerConfig.TAGS,
                         ConsumerConfig.START_MODE,
                         ConsumerConfig.CONSUMER_GROUP,
                         ConsumerConfig.COMMIT_ON_CHECKPOINT,
diff --git a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceReader.java b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceReader.java
index 0dfa0b179d1a..65a1400361d6 100644
--- a/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceReader.java
+++ b/seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceReader.java
@@ -146,9 +146,20 @@ record ->
                                                                 .collect(Collectors.toList());
                                                 long lastOffset = -1;
                                                 for (MessageExt record : messages) {
-                                                    deserializationSchema.deserialize(
-                                                            record.getBody(), output);
-                                                    lastOffset = record.getQueueOffset();
+                                                    // Check if the tags are specified and match the
+                                                    // record's tag
+                                                    boolean shouldProcess =
+                                                            metadata.getTags() == null
+                                                                    || metadata.getTags().isEmpty()
+                                                                    || metadata.getTags()
+                                                                            .contains(
+                                                                                    record
+                                                                                            .getTags());
+                                                    if (shouldProcess) {
+                                                        deserializationSchema.deserialize(
+                                                                record.getBody(), output);
+                                                        lastOffset = record.getQueueOffset();
+                                                    }
                                                     if (Boundedness.BOUNDED.equals(
                                                                     context.getBoundedness())
                                                             && record.getQueueOffset()
diff --git a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java
index db3500119b40..e59237e73d7f 100644
--- a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java
+++ b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java
@@ -49,7 +49,10 @@
 import org.apache.rocketmq.common.message.Message;
 import org.apache.rocketmq.common.message.MessageExt;
 import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.protocol.route.QueueData;
+import org.apache.rocketmq.common.protocol.route.TopicRouteData;
 import org.apache.rocketmq.remoting.protocol.LanguageCode;
+import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
 
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
@@ -77,6 +80,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 import static org.apache.seatunnel.e2e.connector.rocketmq.RocketMqContainer.NAMESRV_PORT;
 
@@ -204,6 +208,42 @@ public void testTextFormatSinkRocketMq(TestContainer container)
         Assertions.assertEquals(10, data.size());
     }
 
+    @TestTemplate
+    public void testSourceRocketMqTextTagToConsole(TestContainer container)
+            throws IOException, InterruptedException {
+        String topic = "test_topic_text_tag";
+        String tag = "tag_test";
+
+        // delete topic if exist
+        deleteTopicIfExist(topic);
+
+        DefaultSeaTunnelRowSerializer serializer =
+                new DefaultSeaTunnelRowSerializer(
+                        topic, tag, SEATUNNEL_ROW_TYPE, SchemaFormat.TEXT, DEFAULT_FIELD_DELIMITER);
+        generateTestData(serializer::serializeRow, topic, 0, 32);
+        Container.ExecResult execResult =
+                container.executeJob("/rocketmq-source_text_tag_to_console.conf");
+        Assertions.assertEquals(0, execResult.getExitCode(), execResult.getStderr());
+    }
+
+    @TestTemplate
+    public void testSourceRocketMqTextErrorTagToConsole(TestContainer container)
+            throws IOException, InterruptedException {
+        String topic = "test_topic_text_error_tag";
+        String tag = "test_error_tag";
+
+        // delete topic if exist
+        deleteTopicIfExist(topic);
+
+        DefaultSeaTunnelRowSerializer serializer =
+                new DefaultSeaTunnelRowSerializer(
+                        topic, tag, SEATUNNEL_ROW_TYPE, SchemaFormat.TEXT, DEFAULT_FIELD_DELIMITER);
+        generateTestData(serializer::serializeRow, topic, 0, 32);
+        Container.ExecResult execResult =
+                container.executeJob("/rocketmq-source_text_error_tag_to_console.conf");
+        Assertions.assertEquals(0, execResult.getExitCode(), execResult.getStderr());
+    }
+
     @TestTemplate
     public void testSourceRocketMqTextToConsole(TestContainer container)
             throws IOException, InterruptedException {
@@ -458,4 +498,32 @@ public RocketMqBaseConfiguration newConfiguration() {
     interface ProducerRecordConverter {
         Message convert(SeaTunnelRow row);
     }
+
+    private void deleteTopicIfExist(String topicName) {
+        DefaultMQAdminExt admin = new DefaultMQAdminExt();
+        admin.setInstanceName(UUID.randomUUID().toString());
+        try {
+            admin.start();
+            TopicRouteData topicRouteData = admin.examineTopicRouteInfo(topicName);
+            if (topicRouteData != null
+                    && topicRouteData.getQueueDatas() != null
+                    && !topicRouteData.getQueueDatas().isEmpty()) {
+                Set<String> brokerNames =
+                        topicRouteData.getQueueDatas().stream()
+                                .map(QueueData::getBrokerName)
+                                .collect(Collectors.toSet());
+                admin.deleteTopicInBroker(brokerNames, topicName);
+                admin.deleteTopicInNameServer(brokerNames, topicName, "delete_topic");
+                log.info("Deleted topic: {}", topicName);
+            } else {
+                log.info("Topic {} does not exist", topicName);
+            }
+        } catch (Exception e) {
+            log.warn("Failed to delete topic {}: {}", topicName, e.getMessage());
+        } finally {
+            if (admin != null) {
+                admin.shutdown();
+            }
+        }
+    }
 }
diff --git a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_error_tag_to_console.conf b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_error_tag_to_console.conf
new file mode 100644
index 000000000000..71c06a181ec4
--- /dev/null
+++ b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_error_tag_to_console.conf
@@ -0,0 +1,94 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_error_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "error_tag_test"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
+    rules = {
+      row_rules = [
+        {
+          rule_type = MIN_ROW
+          rule_value = 0
+        },
+        {
+          rule_type = MAX_ROW
+          rule_value = 0
+        }
+      ],
+      field_rules = [
+        {
+          field_name = c_string
+          field_type = string
+          field_value = [
+            {
+              rule_type = NULL
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
new file mode 100644
index 000000000000..e69aad1614aa
--- /dev/null
+++ b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
@@ -0,0 +1,95 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "tag_test"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
+    rules = {
+      row_rules = [
+        {
+          rule_type = MIN_ROW
+          rule_value = 32
+        },
+        {
+          rule_type = MAX_ROW
+          rule_value = 32
+        }
+      ],
+      field_rules = [
+        {
+          field_name = c_string
+          field_type = string
+          field_value = [
+            {
+              rule_type = NOT_NULL
+              equals_to = "string"
+            }
+          ]
+        }
+      ]
+    }
+  }
+}


Reviews:
user: login: Hisoka-X
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-26T03:01:33Z
user: login: wuchunfu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-26T03:14:41Z
user: login: Hisoka-X
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-26T08:39:03Z
user: login: wuchunfu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-28T00:16:49Z
user: login: wuchunfu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-28T00:16:54Z
user: login: Hisoka-X
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T03:50:12Z
user: login: Hisoka-X
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T08:39:44Z
user: login: Hisoka-X
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-12T03:53:09Z
user: login: hailin0
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T07:18:09Z

Review comments:
diff_hunk: @@ -31,6 +31,15 @@ public class ConsumerConfig extends Config {
                     .withDescription(
                             "RocketMq topic name. If there are multiple topics, use , to split, for example: "
                                     + "\"tpc1,tpc2\".");
+
+    public static final Option<String> TAGS =
+            Options.key("tags")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "RocketMq tag name. If there are multiple tags, use , to split, for example: "
path: seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/config/ConsumerConfig.java
user: login: Hisoka-X
body: ```suggestion
                            "RocketMq tag name. If there are multiple tags, use `,` to split, for example: "
```
created_at: 2025-02-26T03:00:16Z
author_association: MEMBER
diff_hunk: @@ -218,3 +219,62 @@ sink {
 }
 ```
 
+### Specified timestamp Simple:
+
+> This is to specify a time to consume, and I dynamically sense the existence of a new partition every 1000 milliseconds to pull the consumption
path: docs/en/connector-v2/source/RocketMQ.md
user: login: Hisoka-X
body: Please update the description
created_at: 2025-02-26T03:01:30Z
author_association: MEMBER
diff_hunk: @@ -218,3 +219,62 @@ sink {
 }
 ```
 
+### Specified timestamp Simple:
+
+> This is to specify a time to consume, and I dynamically sense the existence of a new partition every 1000 milliseconds to pull the consumption
path: docs/en/connector-v2/source/RocketMQ.md
user: login: wuchunfu
body: Thank you for your reminder, I neglected it.
created_at: 2025-02-26T03:14:41Z
author_association: MEMBER
diff_hunk: @@ -204,6 +204,22 @@ public void testTextFormatSinkRocketMq(TestContainer container)
         Assertions.assertEquals(10, data.size());
     }
 
+    @TestTemplate
+    public void testSourceRocketMqTextTagToConsole(TestContainer container)
+            throws IOException, InterruptedException {
+        DefaultSeaTunnelRowSerializer serializer =
+                new DefaultSeaTunnelRowSerializer(
+                        "test_topic_text_tag",
+                        "test_tag",
+                        SEATUNNEL_ROW_TYPE,
+                        SchemaFormat.TEXT,
+                        DEFAULT_FIELD_DELIMITER);
+        generateTestData(serializer::serializeRow, "test_topic_text_tag", 0, 100);
+        Container.ExecResult execResult =
+                container.executeJob("/rocketmq-source_text_tag_to_console.conf");
+        Assertions.assertEquals(0, execResult.getExitCode(), execResult.getStderr());
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java
user: login: Hisoka-X
body: Can we write data of different tags to verify whether the filtering is effective?
created_at: 2025-02-26T08:38:19Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,92 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "test_tag"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
+    rules = {
+      field_rules = [
+        {
+          field_name = id
+          field_type = bigint
+          field_value = [
+            {
+              rule_type = NOT_NULL
+            },
+            {
+              rule_type = MIN
+              rule_value = 0
+            },
+            {
+              rule_type = MAX
+              rule_value = 99
+            }
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
user: login: Hisoka-X
body: Use an exact amount of data, not a range
created_at: 2025-02-26T08:38:59Z
author_association: MEMBER
diff_hunk: @@ -204,6 +204,22 @@ public void testTextFormatSinkRocketMq(TestContainer container)
         Assertions.assertEquals(10, data.size());
     }
 
+    @TestTemplate
+    public void testSourceRocketMqTextTagToConsole(TestContainer container)
+            throws IOException, InterruptedException {
+        DefaultSeaTunnelRowSerializer serializer =
+                new DefaultSeaTunnelRowSerializer(
+                        "test_topic_text_tag",
+                        "test_tag",
+                        SEATUNNEL_ROW_TYPE,
+                        SchemaFormat.TEXT,
+                        DEFAULT_FIELD_DELIMITER);
+        generateTestData(serializer::serializeRow, "test_topic_text_tag", 0, 100);
+        Container.ExecResult execResult =
+                container.executeJob("/rocketmq-source_text_tag_to_console.conf");
+        Assertions.assertEquals(0, execResult.getExitCode(), execResult.getStderr());
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java
user: login: wuchunfu
body: done
created_at: 2025-02-28T00:16:48Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,92 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "test_tag"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
+    rules = {
+      field_rules = [
+        {
+          field_name = id
+          field_type = bigint
+          field_value = [
+            {
+              rule_type = NOT_NULL
+            },
+            {
+              rule_type = MIN
+              rule_value = 0
+            },
+            {
+              rule_type = MAX
+              rule_value = 99
+            }
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
user: login: wuchunfu
body: done
created_at: 2025-02-28T00:16:54Z
author_association: MEMBER
diff_hunk: @@ -218,3 +219,62 @@ sink {
 }
 ```
 
+### Specified tag Simple:
path: docs/en/connector-v2/source/RocketMQ.md
user: login: Hisoka-X
body: ```suggestion
### Specified tag example
```
created_at: 2025-03-03T03:48:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_error_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "error_tag_test"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_error_tag_to_console.conf
user: login: Hisoka-X
body: Please add assert of row size.
created_at: 2025-03-03T03:49:58Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,85 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "tag_test"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
user: login: Hisoka-X
body: ditto.
created_at: 2025-03-03T03:50:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,95 @@
+#
+# 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.
+#
+######
+###### This config file is a demonstration of streaming processing in seatunnel config
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+
+  # You can set spark configuration here
+  spark.app.name = "SeaTunnel"
+  spark.executor.instances = 1
+  spark.executor.cores = 1
+  spark.executor.memory = "1g"
+  spark.master = local
+}
+
+source {
+  Rocketmq {
+    plugin_output = "rocketmq_table"
+    name.srv.addr = "rocketmq-e2e:9876"
+    topics = "test_topic_text_tag"
+    format = text
+    # The default field delimiter is ","
+    field_delimiter = ","
+    tags = "tag_test"
+    schema = {
+      fields {
+        id = bigint
+        c_map = "map<string, smallint>"
+        c_array = "array<tinyint>"
+        c_string = string
+        c_boolean = boolean
+        c_tinyint = tinyint
+        c_smallint = smallint
+        c_int = int
+        c_bigint = bigint
+        c_float = float
+        c_double = double
+        c_decimal = "decimal(2, 1)"
+        c_bytes = bytes
+        c_date = date
+        c_timestamp = timestamp
+      }
+    }
+  }
+}
+
+transform {
+}
+
+sink {
+  Assert {
+    plugin_input = "rocketmq_table"
+    rules = {
+      row_rules = [
+        {
+          rule_type = MIN_ROW
+          rule_value = 0
+        },
+        {
+          rule_type = MAX_ROW
+          rule_value = 100
+        }
+      ],
path: seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/resources/rocketmq-source_text_tag_to_console.conf
user: login: Hisoka-X
body: ```suggestion
      row_rules = [
        {
          rule_type = MIN_ROW
          rule_value = 100
        },
        {
          rule_type = MAX_ROW
          rule_value = 100
        }
      ],
```
created_at: 2025-03-06T08:39:30Z
author_association: MEMBER

PR comments:
user: login: wuchunfu
created_at: 2025-02-26T08:19:37Z
body: @Hisoka-X PATL, thanks.
author_association: MEMBER
user: login: wuchunfu
created_at: 2025-02-28T00:17:17Z
body: @Hisoka-X PATL, thanks.
author_association: MEMBER
user: login: wuchunfu
created_at: 2025-03-06T02:58:08Z
body: @Hisoka-X @hailin0 PTAL, thanks
author_association: MEMBER
user: login: wuchunfu
created_at: 2025-03-11T01:37:53Z
body: @Hisoka-X @hailin0 PTAL, thanks.
author_association: MEMBER