diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml
index af674592bb9..5aa4f460c7c 100644
--- a/.github/workflows/bazel.yml
+++ b/.github/workflows/bazel.yml
@@ -7,6 +7,10 @@ on:
- master
- develop
- bazel
+
+permissions:
+ actions: write
+
jobs:
build:
name: "bazel-compile (${{ matrix.os }})"
@@ -19,4 +23,14 @@ jobs:
- name: Build
run: bazel build --config=remote //...
- name: Run Tests
- run: bazel test --config=remote //...
\ No newline at end of file
+ run: bazel test --config=remote //...
+ - name: Retry if failed
+ # if it failed , retry 2 times at most
+ if: failure() && fromJSON(github.run_attempt) < 3
+ continue-on-error: true
+ env:
+ GH_REPO: ${{ github.repository }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ echo "Attempting to retry workflow..."
+ gh workflow run rerun-workflow.yml -F run_id=${{ github.run_id }}
\ No newline at end of file
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
new file mode 100644
index 00000000000..8179f362879
--- /dev/null
+++ b/.github/workflows/integration-test.yml
@@ -0,0 +1,45 @@
+name: Run Integration Tests
+on:
+ pull_request:
+ types: [opened, reopened, synchronize]
+ push:
+ branches: [master, develop]
+
+jobs:
+ it-test:
+ name: "maven-compile (${{ matrix.os }}, JDK-${{ matrix.jdk }})"
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ jdk: [8]
+ steps:
+ - name: Cache Maven Repos
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Set up JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{ matrix.jdk }}
+ distribution: "adopt"
+ cache: "maven"
+
+ - name: Run integration tests with Maven
+ run: mvn clean verify -Pit-test -Pskip-unit-tests
+
+ - name: Publish Test Report
+ uses: mikepenz/action-junit-report@v3
+ if: always()
+ with:
+ report_paths: 'test/target/failsafe-reports/TEST-*.xml'
+ annotate_only: true
+ include_passed: true
+ detailed_summary: true
diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml
index 06db86e0157..f17c20b1ab8 100644
--- a/.github/workflows/maven.yaml
+++ b/.github/workflows/maven.yaml
@@ -4,6 +4,10 @@ on:
types: [opened, reopened, synchronize]
push:
branches: [master, develop, bazel]
+
+permissions:
+ actions: write
+
jobs:
java_build:
name: "maven-compile (${{ matrix.os }}, JDK-${{ matrix.jdk }})"
@@ -25,10 +29,30 @@ jobs:
cache: "maven"
- name: Build with Maven
run: mvn -B package --file pom.xml
- - name: Upload JVM crash logs
+
+ - name: Upload Auth JVM crash logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: jvm-crash-logs
path: /Users/runner/work/rocketmq/rocketmq/auth/hs_err_pid*.log
- retention-days: 1
\ No newline at end of file
+ retention-days: 1
+
+ - name: Upload broker JVM crash logs
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: jvm-crash-logs
+ path: /Users/runner/work/rocketmq/rocketmq/broker/hs_err_pid*.log
+ retention-days: 1
+
+ - name: Retry if failed
+ # if it failed , retry 2 times at most
+ if: failure() && fromJSON(github.run_attempt) < 3
+ continue-on-error: true
+ env:
+ GH_REPO: ${{ github.repository }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ echo "Attempting to retry workflow..."
+ gh workflow run rerun-workflow.yml -F run_id=${{ github.run_id }}
\ No newline at end of file
diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml
index ef2db755d00..99d7309fd0c 100644
--- a/.github/workflows/pr-ci.yml
+++ b/.github/workflows/pr-ci.yml
@@ -21,7 +21,7 @@ jobs:
- name: Build distribution tar
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
@@ -30,7 +30,7 @@ jobs:
run: |
mkdir -p ./pr
echo ${{ github.event.number }} > ./pr/NR
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v4
with:
name: pr
path: pr/
diff --git a/.github/workflows/pr-e2e-test.yml b/.github/workflows/pr-e2e-test.yml
index 9082b6b2227..ead7103d603 100644
--- a/.github/workflows/pr-e2e-test.yml
+++ b/.github/workflows/pr-e2e-test.yml
@@ -25,7 +25,7 @@ jobs:
java-version: ["8"]
steps:
- name: 'Download artifact'
- uses: actions/github-script@v3.1.0
+ uses: actions/github-script@v7
with:
script: |
var artifacts = await github.actions.listWorkflowRunArtifacts({
@@ -68,7 +68,7 @@ jobs:
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
@@ -85,7 +85,7 @@ jobs:
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
@@ -96,6 +96,7 @@ jobs:
a=(`ls versionlist`)
printf '%s\n' "${a[@]}" | jq -R . | jq -s .
echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT
+
deploy:
if: ${{ success() }}
name: Deploy RocketMQ
@@ -158,7 +159,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
@@ -187,6 +188,8 @@ jobs:
test-cmd: |
cd ../common && mvn -Prelease -DskipTests clean package -U
cd ../rocketmq-admintools && source bin/env.sh
+ wget https://go.dev/dl/go1.22.6.linux-amd64.tar.gz && \
+ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.6.linux-amd64.tar.gz
cd ../golang && go get -u github.com/apache/rocketmq-clients/golang && gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./mqgotest/... -timeout 2m -v
job-id: 0
- name: Publish Test Report
@@ -197,7 +200,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
@@ -233,7 +236,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
diff --git a/.github/workflows/push-ci.yml b/.github/workflows/push-ci.yml
index b679d56d2f0..b23d69788cb 100644
--- a/.github/workflows/push-ci.yml
+++ b/.github/workflows/push-ci.yml
@@ -31,7 +31,7 @@ jobs:
- name: Build distribution tar
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
@@ -53,7 +53,7 @@ jobs:
repository: apache/rocketmq-docker.git
ref: master
path: rocketmq-docker
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Download distribution tar
with:
name: rocketmq
@@ -72,7 +72,7 @@ jobs:
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
@@ -90,7 +90,7 @@ jobs:
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
@@ -163,7 +163,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
@@ -192,6 +192,8 @@ jobs:
test-cmd: |
cd ../common && mvn -Prelease -DskipTests clean package -U
cd ../rocketmq-admintools && source bin/env.sh
+ wget https://go.dev/dl/go1.22.6.linux-amd64.tar.gz && \
+ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.6.linux-amd64.tar.gz
cd ../golang && go get -u github.com/apache/rocketmq-clients/golang && gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./mqgotest/... -timeout 2m -v
job-id: 0
- name: Publish Test Report
@@ -202,7 +204,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
@@ -238,7 +240,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
diff --git a/.github/workflows/rerun-workflow.yml b/.github/workflows/rerun-workflow.yml
new file mode 100644
index 00000000000..bf83fc51b63
--- /dev/null
+++ b/.github/workflows/rerun-workflow.yml
@@ -0,0 +1,21 @@
+name: Rerun workflow
+on:
+ workflow_dispatch:
+ inputs:
+ run_id:
+ required: true
+
+permissions:
+ actions: write
+
+jobs:
+ rerun:
+ runs-on: ubuntu-latest
+ steps:
+ - name: rerun ${{ inputs.run_id }}
+ env:
+ GH_REPO: ${{ github.repository }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
+ gh run rerun ${{ inputs.run_id }} --failed
\ No newline at end of file
diff --git a/.github/workflows/snapshot-automation.yml b/.github/workflows/snapshot-automation.yml
index 99855d3aa0d..9fb16cb13ca 100644
--- a/.github/workflows/snapshot-automation.yml
+++ b/.github/workflows/snapshot-automation.yml
@@ -69,7 +69,7 @@ jobs:
MAVEN_SETTINGS: ${{ github.workspace }}/.github/asf-deploy-settings.xml
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
@@ -91,7 +91,7 @@ jobs:
repository: apache/rocketmq-docker.git
ref: master
path: rocketmq-docker
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Download distribution tar
with:
name: rocketmq
@@ -110,7 +110,7 @@ jobs:
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
@@ -125,7 +125,7 @@ jobs:
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
@@ -200,7 +200,7 @@ jobs:
annotate_only: true
include_passed: true
detailed_summary: true
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
diff --git a/acl/pom.xml b/acl/pom.xml
index a52d6b66b48..c9d5085dcc1 100644
--- a/acl/pom.xml
+++ b/acl/pom.xml
@@ -13,7 +13,7 @@
org.apache.rocketmq
rocketmq-all
- 5.2.1-SNAPSHOT
+ 5.3.1-SNAPSHOT
rocketmq-acl
rocketmq-acl ${project.version}
diff --git a/acl/src/main/java/org/apache/rocketmq/acl/common/AclUtils.java b/acl/src/main/java/org/apache/rocketmq/acl/common/AclUtils.java
index 65f04f54339..937619beee4 100644
--- a/acl/src/main/java/org/apache/rocketmq/acl/common/AclUtils.java
+++ b/acl/src/main/java/org/apache/rocketmq/acl/common/AclUtils.java
@@ -40,7 +40,7 @@ public class AclUtils {
public static byte[] combineRequestContent(RemotingCommand request, SortedMap fieldsMap) {
try {
- StringBuilder sb = new StringBuilder("");
+ StringBuilder sb = new StringBuilder();
for (Map.Entry entry : fieldsMap.entrySet()) {
if (!SessionCredentials.SIGNATURE.equals(entry.getKey())) {
sb.append(entry.getValue());
@@ -71,12 +71,12 @@ public static void IPv6AddressCheck(String netAddress) {
if (isAsterisk(netAddress) || isMinus(netAddress)) {
int asterisk = netAddress.indexOf("*");
int minus = netAddress.indexOf("-");
-// '*' must be the end of netAddress if it exists
+ // '*' must be the end of netAddress if it exists
if (asterisk > -1 && asterisk != netAddress.length() - 1) {
throw new AclException(String.format("NetAddress examine scope Exception netAddress is %s", netAddress));
}
-// format like "2::ac5:78:1-200:*" or "2::ac5:78:1-200" is legal
+ // format like "2::ac5:78:1-200:*" or "2::ac5:78:1-200" is legal
if (minus > -1) {
if (asterisk == -1) {
if (minus <= netAddress.lastIndexOf(":")) {
@@ -128,7 +128,7 @@ public static String[] getAddresses(String netAddress, String partialAddress) {
}
public static boolean isScope(String netAddress, int index) {
-// IPv6 Address
+ // IPv6 Address
if (isColon(netAddress)) {
netAddress = expandIP(netAddress, 8);
String[] strArray = StringUtils.split(netAddress, ":");
diff --git a/acl/src/main/java/org/apache/rocketmq/acl/plain/PlainPermissionManager.java b/acl/src/main/java/org/apache/rocketmq/acl/plain/PlainPermissionManager.java
index 6a1d826837c..2326a3395c4 100644
--- a/acl/src/main/java/org/apache/rocketmq/acl/plain/PlainPermissionManager.java
+++ b/acl/src/main/java/org/apache/rocketmq/acl/plain/PlainPermissionManager.java
@@ -95,7 +95,7 @@ public List getAllAclFiles(String path) {
List allAclFileFullPath = new ArrayList<>();
File file = new File(path);
File[] files = file.listFiles();
- for (int i = 0; i < files.length; i++) {
+ for (int i = 0; files != null && i < files.length; i++) {
String fileName = files[i].getAbsolutePath();
File f = new File(fileName);
if (fileName.equals(fileHome + MixAll.ACL_CONF_TOOLS_FILE)) {
@@ -127,10 +127,9 @@ public void load() {
fileList.add(defaultAclFile);
}
- for (int i = 0; i < fileList.size(); i++) {
- final String currentFile = MixAll.dealFilePath(fileList.get(i));
- PlainAccessData plainAclConfData = AclUtils.getYamlDataObject(currentFile,
- PlainAccessData.class);
+ for (String path : fileList) {
+ final String currentFile = MixAll.dealFilePath(path);
+ PlainAccessData plainAclConfData = AclUtils.getYamlDataObject(currentFile, PlainAccessData.class);
if (plainAclConfData == null) {
log.warn("No data in file {}", currentFile);
continue;
@@ -140,12 +139,11 @@ public void load() {
List globalWhiteRemoteAddressStrategyList = new ArrayList<>();
List globalWhiteRemoteAddressesList = plainAclConfData.getGlobalWhiteRemoteAddresses();
if (globalWhiteRemoteAddressesList != null && !globalWhiteRemoteAddressesList.isEmpty()) {
- for (int j = 0; j < globalWhiteRemoteAddressesList.size(); j++) {
- globalWhiteRemoteAddressStrategyList.add(remoteAddressStrategyFactory.
- getRemoteAddressStrategy(globalWhiteRemoteAddressesList.get(j)));
+ for (String address : globalWhiteRemoteAddressesList) {
+ globalWhiteRemoteAddressStrategyList.add(remoteAddressStrategyFactory.getRemoteAddressStrategy(address));
}
}
- if (globalWhiteRemoteAddressStrategyList.size() > 0) {
+ if (!globalWhiteRemoteAddressStrategyList.isEmpty()) {
globalWhiteRemoteAddressStrategyMap.put(currentFile, globalWhiteRemoteAddressStrategyList);
globalWhiteRemoteAddressStrategy.addAll(globalWhiteRemoteAddressStrategyList);
}
@@ -164,7 +162,7 @@ public void load() {
}
}
}
- if (plainAccessResourceMap.size() > 0) {
+ if (!plainAccessResourceMap.isEmpty()) {
aclPlainAccessResourceMap.put(currentFile, plainAccessResourceMap);
}
@@ -220,17 +218,16 @@ public void load(String aclFilePath) {
log.info("Broker plain acl conf data is : {}", plainAclConfData.toString());
List globalWhiteRemoteAddressesList = plainAclConfData.getGlobalWhiteRemoteAddresses();
if (globalWhiteRemoteAddressesList != null && !globalWhiteRemoteAddressesList.isEmpty()) {
- for (int i = 0; i < globalWhiteRemoteAddressesList.size(); i++) {
- globalWhiteRemoteAddressStrategy.add(remoteAddressStrategyFactory.
- getRemoteAddressStrategy(globalWhiteRemoteAddressesList.get(i)));
+ for (String address : globalWhiteRemoteAddressesList) {
+ globalWhiteRemoteAddressStrategy.add(remoteAddressStrategyFactory.getRemoteAddressStrategy(address));
}
}
this.globalWhiteRemoteAddressStrategy.addAll(globalWhiteRemoteAddressStrategy);
if (this.globalWhiteRemoteAddressStrategyMap.get(aclFilePath) != null) {
List remoteAddressStrategyList = this.globalWhiteRemoteAddressStrategyMap.get(aclFilePath);
- for (int i = 0; i < remoteAddressStrategyList.size(); i++) {
- this.globalWhiteRemoteAddressStrategy.remove(remoteAddressStrategyList.get(i));
+ for (RemoteAddressStrategy remoteAddressStrategy : remoteAddressStrategyList) {
+ this.globalWhiteRemoteAddressStrategy.remove(remoteAddressStrategy);
}
this.globalWhiteRemoteAddressStrategyMap.put(aclFilePath, globalWhiteRemoteAddressStrategy);
}
@@ -280,11 +277,9 @@ public PlainAccessData updateAclConfigFileVersion(String aclFileName, PlainAcces
List dataVersions = updateAclConfigMap.getDataVersion();
DataVersion dataVersion = new DataVersion();
- if (dataVersions != null) {
- if (dataVersions.size() > 0) {
- dataVersion.setTimestamp(dataVersions.get(0).getTimestamp());
- dataVersion.setCounter(new AtomicLong(dataVersions.get(0).getCounter()));
- }
+ if (dataVersions != null && !dataVersions.isEmpty()) {
+ dataVersion.setTimestamp(dataVersions.get(0).getTimestamp());
+ dataVersion.setCounter(new AtomicLong(dataVersions.get(0).getCounter()));
}
dataVersion.nextVersion();
List versionElement = new ArrayList<>();
@@ -337,7 +332,7 @@ public boolean updateAccessConfig(PlainAccessConfig plainAccessConfig) {
if (accountMap == null) {
accountMap = new HashMap<>(1);
accountMap.put(plainAccessConfig.getAccessKey(), buildPlainAccessResource(plainAccessConfig));
- } else if (accountMap.size() == 0) {
+ } else if (accountMap.isEmpty()) {
accountMap.put(plainAccessConfig.getAccessKey(), buildPlainAccessResource(plainAccessConfig));
} else {
for (Map.Entry entry : accountMap.entrySet()) {
@@ -470,7 +465,7 @@ public boolean updateGlobalWhiteAddrsConfig(List globalWhiteAddrsList) {
}
public boolean updateGlobalWhiteAddrsConfig(List globalWhiteAddrsList, String fileName) {
- if (fileName == null || fileName.equals("")) {
+ if (fileName == null || fileName.isEmpty()) {
fileName = this.defaultAclFile;
}
@@ -512,10 +507,8 @@ public AclConfig getAllAclConfig() {
List whiteAddrs = new ArrayList<>();
Set accessKeySets = new HashSet<>();
- for (int i = 0; i < fileList.size(); i++) {
- String path = fileList.get(i);
- PlainAccessData plainAclConfData = AclUtils.getYamlDataObject(path,
- PlainAccessData.class);
+ for (String path : fileList) {
+ PlainAccessData plainAclConfData = AclUtils.getYamlDataObject(path, PlainAccessData.class);
if (plainAclConfData == null) {
continue;
}
@@ -526,18 +519,18 @@ public AclConfig getAllAclConfig() {
List plainAccessConfigs = plainAclConfData.getAccounts();
if (plainAccessConfigs != null && !plainAccessConfigs.isEmpty()) {
- for (int j = 0; j < plainAccessConfigs.size(); j++) {
- if (!accessKeySets.contains(plainAccessConfigs.get(j).getAccessKey())) {
- accessKeySets.add(plainAccessConfigs.get(j).getAccessKey());
+ for (PlainAccessConfig accessConfig : plainAccessConfigs) {
+ if (!accessKeySets.contains(accessConfig.getAccessKey())) {
+ accessKeySets.add(accessConfig.getAccessKey());
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
- plainAccessConfig.setGroupPerms(plainAccessConfigs.get(j).getGroupPerms());
- plainAccessConfig.setDefaultTopicPerm(plainAccessConfigs.get(j).getDefaultTopicPerm());
- plainAccessConfig.setDefaultGroupPerm(plainAccessConfigs.get(j).getDefaultGroupPerm());
- plainAccessConfig.setAccessKey(plainAccessConfigs.get(j).getAccessKey());
- plainAccessConfig.setSecretKey(plainAccessConfigs.get(j).getSecretKey());
- plainAccessConfig.setAdmin(plainAccessConfigs.get(j).isAdmin());
- plainAccessConfig.setTopicPerms(plainAccessConfigs.get(j).getTopicPerms());
- plainAccessConfig.setWhiteRemoteAddress(plainAccessConfigs.get(j).getWhiteRemoteAddress());
+ plainAccessConfig.setGroupPerms(accessConfig.getGroupPerms());
+ plainAccessConfig.setDefaultTopicPerm(accessConfig.getDefaultTopicPerm());
+ plainAccessConfig.setDefaultGroupPerm(accessConfig.getDefaultGroupPerm());
+ plainAccessConfig.setAccessKey(accessConfig.getAccessKey());
+ plainAccessConfig.setSecretKey(accessConfig.getSecretKey());
+ plainAccessConfig.setAdmin(accessConfig.isAdmin());
+ plainAccessConfig.setTopicPerms(accessConfig.getTopicPerms());
+ plainAccessConfig.setWhiteRemoteAddress(accessConfig.getWhiteRemoteAddress());
configs.add(plainAccessConfig);
}
}
diff --git a/acl/src/test/java/org/apache/rocketmq/acl/common/AuthorizationHeaderTest.java b/acl/src/test/java/org/apache/rocketmq/acl/common/AuthorizationHeaderTest.java
new file mode 100644
index 00000000000..bb735f0a045
--- /dev/null
+++ b/acl/src/test/java/org/apache/rocketmq/acl/common/AuthorizationHeaderTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rocketmq.acl.common;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class AuthorizationHeaderTest {
+
+ private static final String AUTH_HEADER = "Signature Credential=1234567890/test, SignedHeaders=host, Signature=1234567890";
+ private AuthorizationHeader authorizationHeader;
+
+ @Before
+ public void setUp() throws Exception {
+ authorizationHeader = new AuthorizationHeader(AUTH_HEADER);
+ }
+
+ @Test
+ public void testGetMethod() {
+ Assert.assertEquals("Signature", authorizationHeader.getMethod());
+ }
+
+ @Test
+ public void testGetAccessKey() {
+ Assert.assertEquals("1234567890", authorizationHeader.getAccessKey());
+ }
+
+ @Test
+ public void testGetSignedHeaders() {
+ String[] expectedHeaders = {"host"};
+ Assert.assertArrayEquals(expectedHeaders, authorizationHeader.getSignedHeaders());
+ }
+
+ @Test
+ public void testGetSignature() {
+ Assert.assertEquals("EjRWeJA=", authorizationHeader.getSignature());
+ }
+
+ @Test(expected = Exception.class)
+ public void testInvalidAuthorizationHeader() throws Exception {
+ new AuthorizationHeader("Invalid Header");
+ }
+
+ @Test(expected = Exception.class)
+ public void testMalformedAuthorizationHeader() throws Exception {
+ new AuthorizationHeader("Malformed, Header");
+ }
+
+}
diff --git a/acl/src/test/java/org/apache/rocketmq/acl/plain/PlainPermissionCheckerTest.java b/acl/src/test/java/org/apache/rocketmq/acl/plain/PlainPermissionCheckerTest.java
new file mode 100644
index 00000000000..4df0ea5d2d6
--- /dev/null
+++ b/acl/src/test/java/org/apache/rocketmq/acl/plain/PlainPermissionCheckerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.rocketmq.acl.plain;
+
+import org.apache.rocketmq.acl.common.AclException;
+import org.apache.rocketmq.acl.common.Permission;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class PlainPermissionCheckerTest {
+
+ private PlainPermissionChecker permissionChecker;
+
+ @Before
+ public void setUp() {
+ permissionChecker = new PlainPermissionChecker();
+ }
+
+ @Test
+ public void testCheck_withAdminPermission_shouldPass() {
+ PlainAccessResource checkedAccess = new PlainAccessResource();
+ checkedAccess.setRequestCode(Permission.SUB);
+ checkedAccess.addResourceAndPerm("topic1", Permission.PUB);
+ PlainAccessResource ownedAccess = new PlainAccessResource();
+ ownedAccess.setAccessKey("adminUser");
+ ownedAccess.setAdmin(true);
+ try {
+ permissionChecker.check(checkedAccess, ownedAccess);
+ } catch (AclException e) {
+ Assert.fail("Should not throw any exception for admin user");
+ }
+ }
+
+ @Test(expected = AclException.class)
+ public void testCheck_withoutAdminPermissionAndNoDefaultPerm_shouldThrowAclException() {
+ PlainAccessResource checkedAccess = new PlainAccessResource();
+ checkedAccess.setRequestCode(Permission.SUB);
+ checkedAccess.addResourceAndPerm("topic1", Permission.PUB);
+ PlainAccessResource ownedAccess = new PlainAccessResource();
+ ownedAccess.setAccessKey("nonAdminUser");
+ ownedAccess.setAdmin(false);
+ permissionChecker.check(checkedAccess, ownedAccess);
+ }
+
+ @Test
+ public void testCheck_withDefaultPermissions_shouldPass() {
+ PlainAccessResource checkedAccess = new PlainAccessResource();
+ checkedAccess.setRequestCode(Permission.SUB);
+ checkedAccess.addResourceAndPerm("topic1", Permission.PUB);
+ PlainAccessResource ownedAccess = new PlainAccessResource();
+ ownedAccess.setAccessKey("nonAdminUser");
+ ownedAccess.setAdmin(false);
+ ownedAccess.setDefaultTopicPerm(Permission.PUB);
+ try {
+ permissionChecker.check(checkedAccess, ownedAccess);
+ } catch (AclException e) {
+ Assert.fail("Should not throw any exception for default permissions");
+ }
+ }
+
+ @Test(expected = AclException.class)
+ public void testCheck_withoutPermission_shouldThrowAclException() {
+ PlainAccessResource checkedAccess = new PlainAccessResource();
+ checkedAccess.setRequestCode(Permission.SUB);
+ checkedAccess.addResourceAndPerm("topic1", Permission.PUB);
+ PlainAccessResource ownedAccess = new PlainAccessResource();
+ ownedAccess.setAccessKey("nonAdminUser");
+ ownedAccess.setAdmin(false);
+ ownedAccess.setDefaultTopicPerm(Permission.SUB);
+ permissionChecker.check(checkedAccess, ownedAccess);
+ }
+
+}
diff --git a/auth/pom.xml b/auth/pom.xml
index 49f0fce7ab0..71b07c33750 100644
--- a/auth/pom.xml
+++ b/auth/pom.xml
@@ -13,7 +13,7 @@
org.apache.rocketmq
rocketmq-all
- 5.2.1-SNAPSHOT
+ 5.3.1-SNAPSHOT
rocketmq-auth
rocketmq-auth ${project.version}
diff --git a/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java b/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java
index 02d5df236f5..e69abdaf805 100644
--- a/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java
+++ b/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java
@@ -171,7 +171,7 @@ public List build(ChannelHandlerContext context, Re
subject = User.of(fields.get(SessionCredentials.ACCESS_KEY));
}
String remoteAddr = RemotingHelper.parseChannelRemoteAddr(context.channel());
- String sourceIp = StringUtils.substringBefore(remoteAddr, CommonConstants.COLON);
+ String sourceIp = StringUtils.substringBeforeLast(remoteAddr, CommonConstants.COLON);
Resource topic;
Resource group;
@@ -394,7 +394,7 @@ private List newContext(Metadata metadata, QueryRou
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
Resource resource = Resource.ofTopic(topic.getName());
- String sourceIp = StringUtils.substringBefore(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
+ String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, Arrays.asList(Action.PUB, Action.SUB), sourceIp);
return Collections.singletonList(context);
}
@@ -437,7 +437,7 @@ private static List newPubContext(Metadata metadata
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
Resource resource = Resource.ofTopic(topic.getName());
- String sourceIp = StringUtils.substringBefore(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
+ String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, Action.PUB, sourceIp);
return Collections.singletonList(context);
}
@@ -483,7 +483,7 @@ private static List newSubContexts(Metadata metadat
if (metadata.containsKey(GrpcConstants.AUTHORIZATION_AK)) {
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
- String sourceIp = StringUtils.substringBefore(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
+ String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
result.add(DefaultAuthorizationContext.of(subject, resource, Action.SUB, sourceIp));
return result;
}
diff --git a/auth/src/main/java/org/apache/rocketmq/auth/authorization/factory/AuthorizationFactory.java b/auth/src/main/java/org/apache/rocketmq/auth/authorization/factory/AuthorizationFactory.java
index f87a5304cb7..29748a9ed44 100644
--- a/auth/src/main/java/org/apache/rocketmq/auth/authorization/factory/AuthorizationFactory.java
+++ b/auth/src/main/java/org/apache/rocketmq/auth/authorization/factory/AuthorizationFactory.java
@@ -105,7 +105,7 @@ public static AuthorizationEvaluator getEvaluator(AuthConfig config, Supplier>
public static AuthorizationStrategy getStrategy(AuthConfig config, Supplier> metadataService) {
try {
Class extends AuthorizationStrategy> clazz = StatelessAuthorizationStrategy.class;
- if (StringUtils.isNotBlank(config.getAuthenticationStrategy())) {
+ if (StringUtils.isNotBlank(config.getAuthorizationStrategy())) {
clazz = (Class extends AuthorizationStrategy>) Class.forName(config.getAuthorizationStrategy());
}
return clazz.getDeclaredConstructor(AuthConfig.class, Supplier.class).newInstance(config, metadataService);
diff --git a/auth/src/test/java/org/apache/rocketmq/auth/authorization/strategy/StatefulAuthorizationStrategyTest.java b/auth/src/test/java/org/apache/rocketmq/auth/authorization/strategy/StatefulAuthorizationStrategyTest.java
new file mode 100644
index 00000000000..80e1f0b49e7
--- /dev/null
+++ b/auth/src/test/java/org/apache/rocketmq/auth/authorization/strategy/StatefulAuthorizationStrategyTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.rocketmq.auth.authorization.strategy;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.rocketmq.auth.authentication.model.Subject;
+import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
+import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
+import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
+import org.apache.rocketmq.auth.authorization.model.Resource;
+import org.apache.rocketmq.auth.config.AuthConfig;
+import org.apache.rocketmq.common.Pair;
+import org.apache.rocketmq.common.action.Action;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class StatefulAuthorizationStrategyTest {
+
+ @Mock
+ private AuthConfig authConfig;
+
+ private StatefulAuthorizationStrategy statefulAuthorizationStrategy;
+
+ @Before
+ public void setUp() {
+ when(authConfig.getStatefulAuthorizationCacheExpiredSecond()).thenReturn(60);
+ when(authConfig.getStatefulAuthorizationCacheMaxNum()).thenReturn(100);
+ Supplier> metadataService = mock(Supplier.class);
+ statefulAuthorizationStrategy = spy(new StatefulAuthorizationStrategy(authConfig, metadataService));
+ }
+
+ @Test
+ public void testEvaluateChannelIdBlankDoesNotUseCache() {
+ AuthorizationContext context = mock(AuthorizationContext.class);
+ when(context.getChannelId()).thenReturn(null);
+ statefulAuthorizationStrategy.evaluate(context);
+ verify(statefulAuthorizationStrategy, times(1)).doEvaluate(context);
+ }
+
+ @Test
+ public void testEvaluateChannelIdNotNullCacheHit() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
+ DefaultAuthorizationContext context = new DefaultAuthorizationContext();
+ context.setChannelId("channelId");
+ context.setSubject(Subject.of("User"));
+ context.setResource(Resource.of("Cluster"));
+ context.setActions(new ArrayList<>());
+ context.setSourceIp("sourceIp");
+ Pair pair = Pair.of(true, null);
+ Cache> authCache = Caffeine.newBuilder()
+ .expireAfterWrite(60, TimeUnit.SECONDS)
+ .maximumSize(100)
+ .build();
+ authCache.put(buildKey(context), pair);
+ statefulAuthorizationStrategy.authCache = authCache;
+ statefulAuthorizationStrategy.evaluate(context);
+ verify(statefulAuthorizationStrategy, never()).doEvaluate(context);
+ }
+
+ @Test
+ public void testEvaluateChannelIdNotNullCacheMiss() {
+ DefaultAuthorizationContext context = new DefaultAuthorizationContext();
+ context.setChannelId("channelId");
+ context.setSubject(Subject.of("User"));
+ context.setResource(Resource.of("Cluster"));
+ context.setActions(Collections.singletonList(Action.PUB));
+ context.setSourceIp("sourceIp");
+ statefulAuthorizationStrategy.authCache = Caffeine.newBuilder()
+ .expireAfterWrite(60, TimeUnit.SECONDS)
+ .maximumSize(100)
+ .build();
+ statefulAuthorizationStrategy.evaluate(context);
+ verify(statefulAuthorizationStrategy, times(1)).doEvaluate(context);
+ }
+
+ @Test
+ public void testEvaluateChannelIdNotNullCacheException() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
+ DefaultAuthorizationContext context = new DefaultAuthorizationContext();
+ context.setChannelId("channelId");
+ context.setSubject(Subject.of("subjectKey"));
+ context.setResource(Resource.of("resourceKey"));
+ context.setActions(Collections.singletonList(Action.PUB));
+ context.setSourceIp("sourceIp");
+ AuthorizationException exception = new AuthorizationException("test");
+ Pair pair = Pair.of(false, exception);
+ Cache> authCache = Caffeine.newBuilder()
+ .expireAfterWrite(60, TimeUnit.SECONDS)
+ .maximumSize(100)
+ .build();
+ authCache.put(buildKey(context), pair);
+ statefulAuthorizationStrategy.authCache = authCache;
+ try {
+ statefulAuthorizationStrategy.evaluate(context);
+ fail("Expected AuthorizationException to be thrown");
+ } catch (final AuthorizationException ex) {
+ assertEquals(exception, ex);
+ }
+ verify(statefulAuthorizationStrategy, never()).doEvaluate(context);
+ }
+
+ private String buildKey(AuthorizationContext context) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
+ return (String) MethodUtils.invokeMethod(statefulAuthorizationStrategy, true, "buildKey", context);
+ }
+}
diff --git a/auth/src/test/java/org/apache/rocketmq/auth/migration/AuthMigratorTest.java b/auth/src/test/java/org/apache/rocketmq/auth/migration/AuthMigratorTest.java
new file mode 100644
index 00000000000..7a2bd5b2c76
--- /dev/null
+++ b/auth/src/test/java/org/apache/rocketmq/auth/migration/AuthMigratorTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.rocketmq.auth.migration;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.rocketmq.acl.plain.PlainPermissionManager;
+import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
+import org.apache.rocketmq.auth.authentication.model.User;
+import org.apache.rocketmq.auth.authorization.manager.AuthorizationMetadataManager;
+import org.apache.rocketmq.auth.config.AuthConfig;
+import org.apache.rocketmq.common.AclConfig;
+import org.apache.rocketmq.common.PlainAccessConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AuthMigratorTest {
+
+ @Mock
+ private AuthenticationMetadataManager authenticationMetadataManager;
+
+ @Mock
+ private AuthorizationMetadataManager authorizationMetadataManager;
+
+ @Mock
+ private PlainPermissionManager plainPermissionManager;
+
+ @Mock
+ private AuthConfig authConfig;
+
+ private AuthMigrator authMigrator;
+
+ @Before
+ public void setUp() throws IllegalAccessException {
+ when(authConfig.isMigrateAuthFromV1Enabled()).thenReturn(true);
+ authMigrator = new AuthMigrator(authConfig);
+ FieldUtils.writeDeclaredField(authMigrator, "authenticationMetadataManager", authenticationMetadataManager, true);
+ FieldUtils.writeDeclaredField(authMigrator, "authorizationMetadataManager", authorizationMetadataManager, true);
+ FieldUtils.writeDeclaredField(authMigrator, "plainPermissionManager", plainPermissionManager, true);
+ }
+
+ @Test
+ public void testMigrateNoAclConfigDoesNothing() {
+ AclConfig aclConfig = mock(AclConfig.class);
+ when(aclConfig.getPlainAccessConfigs()).thenReturn(new ArrayList<>());
+ when(plainPermissionManager.getAllAclConfig()).thenReturn(aclConfig);
+ authMigrator.migrate();
+ verify(authConfig, times(1)).isMigrateAuthFromV1Enabled();
+ verify(plainPermissionManager, times(1)).getAllAclConfig();
+ verify(authenticationMetadataManager, never()).createUser(any());
+ verify(authorizationMetadataManager, never()).createAcl(any());
+ }
+
+ @Test
+ public void testMigrateWithAclConfigCreatesUserAndAcl() {
+ AclConfig aclConfig = mock(AclConfig.class);
+ List accessConfigs = new ArrayList<>();
+ accessConfigs.add(createPlainAccessConfig());
+ when(aclConfig.getPlainAccessConfigs()).thenReturn(accessConfigs);
+ when(plainPermissionManager.getAllAclConfig()).thenReturn(aclConfig);
+ when(authenticationMetadataManager.getUser(anyString()))
+ .thenReturn(CompletableFuture.completedFuture(null));
+ when(authenticationMetadataManager.createUser(any()))
+ .thenReturn(CompletableFuture.completedFuture(null));
+ authMigrator.migrate();
+ verify(authConfig, times(1)).isMigrateAuthFromV1Enabled();
+ verify(plainPermissionManager, times(1)).getAllAclConfig();
+ verify(authenticationMetadataManager, times(1)).createUser(any());
+ verify(authorizationMetadataManager, times(1)).createAcl(any());
+ }
+
+ @Test
+ public void testMigrateExceptionInMigrateLogsError() {
+ PlainAccessConfig accessConfig = mock(PlainAccessConfig.class);
+ when(accessConfig.getAccessKey()).thenReturn("testAk");
+ when(authenticationMetadataManager.createUser(any(User.class)))
+ .thenThrow(new RuntimeException("Test Exception"));
+ AclConfig aclConfig = mock(AclConfig.class);
+ List accessConfigs = new ArrayList<>();
+ accessConfigs.add(accessConfig);
+ when(aclConfig.getPlainAccessConfigs()).thenReturn(accessConfigs);
+ when(plainPermissionManager.getAllAclConfig()).thenReturn(aclConfig);
+ when(authenticationMetadataManager.getUser(anyString()))
+ .thenReturn(CompletableFuture.completedFuture(null));
+ try {
+ authMigrator.migrate();
+ verify(authConfig, times(1)).isMigrateAuthFromV1Enabled();
+ verify(plainPermissionManager, times(1)).getAllAclConfig();
+ verify(authenticationMetadataManager, times(1)).createUser(any());
+ verify(authorizationMetadataManager, never()).createAcl(any());
+ } catch (final RuntimeException ex) {
+ assertEquals("Test Exception", ex.getMessage());
+ }
+ }
+
+ private PlainAccessConfig createPlainAccessConfig() {
+ PlainAccessConfig result = mock(PlainAccessConfig.class);
+ when(result.getAccessKey()).thenReturn("testAk");
+ when(result.getSecretKey()).thenReturn("testSk");
+ when(result.isAdmin()).thenReturn(false);
+ when(result.getTopicPerms()).thenReturn(new ArrayList<>());
+ when(result.getGroupPerms()).thenReturn(new ArrayList<>());
+ when(result.getDefaultTopicPerm()).thenReturn("PUB");
+ when(result.getDefaultGroupPerm()).thenReturn(null);
+ return result;
+ }
+}
diff --git a/broker/BUILD.bazel b/broker/BUILD.bazel
index 0dbc85f9453..66e621e9301 100644
--- a/broker/BUILD.bazel
+++ b/broker/BUILD.bazel
@@ -100,6 +100,7 @@ GenTestRules(
exclude_tests = [
# These tests are extremely slow and flaky, exclude them before they are properly fixed.
"src/test/java/org/apache/rocketmq/broker/controller/ReplicasManagerRegisterTest",
+ "src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest",
],
deps = [
":tests",
diff --git a/broker/pom.xml b/broker/pom.xml
index ea9e35586dd..7f74059a969 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -13,7 +13,7 @@
org.apache.rocketmq
rocketmq-all
- 5.2.1-SNAPSHOT
+ 5.3.1-SNAPSHOT
4.0.0
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
index 76224db5cb5..22ac7fedf1c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
@@ -747,7 +747,7 @@ public void run() {
LOG.error("Failed to update nameServer address list", e);
}
}
- }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
+ }, 1000 * 10, this.brokerConfig.getUpdateNameServerAddrPeriod(), TimeUnit.MILLISECONDS);
} else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@@ -2519,4 +2519,6 @@ public ColdDataCgCtrService getColdDataCgCtrService() {
public void setColdDataCgCtrService(ColdDataCgCtrService coldDataCgCtrService) {
this.coldDataCgCtrService = coldDataCgCtrService;
}
+
+
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/config/RocksDBConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/RocksDBConfigManager.java
similarity index 63%
rename from common/src/main/java/org/apache/rocketmq/common/config/RocksDBConfigManager.java
rename to broker/src/main/java/org/apache/rocketmq/broker/RocksDBConfigManager.java
index d1ec894685f..20358c4707f 100644
--- a/common/src/main/java/org/apache/rocketmq/common/config/RocksDBConfigManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/RocksDBConfigManager.java
@@ -14,46 +14,66 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.common.config;
+package org.apache.rocketmq.broker;
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.common.config.ConfigRocksDBStorage;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
import org.rocksdb.FlushOptions;
import org.rocksdb.RocksIterator;
import org.rocksdb.Statistics;
import org.rocksdb.WriteBatch;
+import java.nio.charset.StandardCharsets;
import java.util.function.BiConsumer;
public class RocksDBConfigManager {
protected static final Logger BROKER_LOG = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
-
- protected volatile boolean isStop = false;
- protected ConfigRocksDBStorage configRocksDBStorage = null;
+ public volatile boolean isStop = false;
+ public ConfigRocksDBStorage configRocksDBStorage = null;
private FlushOptions flushOptions = null;
private volatile long lastFlushMemTableMicroSecond = 0;
+
+ private final String filePath;
private final long memTableFlushInterval;
+ private DataVersion kvDataVersion = new DataVersion();
+
- public RocksDBConfigManager(long memTableFlushInterval) {
+ public RocksDBConfigManager(String filePath, long memTableFlushInterval) {
+ this.filePath = filePath;
this.memTableFlushInterval = memTableFlushInterval;
}
- public boolean load(String configFilePath, BiConsumer biConsumer) {
+ public boolean init() {
this.isStop = false;
- this.configRocksDBStorage = new ConfigRocksDBStorage(configFilePath);
- if (!this.configRocksDBStorage.start()) {
- return false;
- }
- RocksIterator iterator = this.configRocksDBStorage.iterator();
+ this.configRocksDBStorage = new ConfigRocksDBStorage(filePath);
+ return this.configRocksDBStorage.start();
+ }
+ public boolean loadDataVersion() {
+ String currDataVersionString = null;
try {
+ byte[] dataVersion = this.configRocksDBStorage.getKvDataVersion();
+ if (dataVersion != null && dataVersion.length > 0) {
+ currDataVersionString = new String(dataVersion, StandardCharsets.UTF_8);
+ }
+ kvDataVersion = StringUtils.isNotBlank(currDataVersionString) ? JSON.parseObject(currDataVersionString, DataVersion.class) : new DataVersion();
+ return true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean loadData(BiConsumer biConsumer) {
+ try (RocksIterator iterator = this.configRocksDBStorage.iterator()) {
iterator.seekToFirst();
while (iterator.isValid()) {
biConsumer.accept(iterator.key(), iterator.value());
iterator.next();
}
- } finally {
- iterator.close();
}
this.flushOptions = new FlushOptions();
@@ -103,6 +123,20 @@ public void delete(final byte[] keyBytes) throws Exception {
this.configRocksDBStorage.delete(keyBytes);
}
+ public void updateKvDataVersion() throws Exception {
+ kvDataVersion.nextVersion();
+ this.configRocksDBStorage.updateKvDataVersion(JSON.toJSONString(kvDataVersion).getBytes(StandardCharsets.UTF_8));
+ }
+
+ public DataVersion getKvDataVersion() {
+ return kvDataVersion;
+ }
+
+ public void updateForbidden(String key, String value) throws Exception {
+ this.configRocksDBStorage.updateForbidden(key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8));
+ }
+
+
public void batchPutWithWal(final WriteBatch batch) throws Exception {
this.configRocksDBStorage.batchPutWithWal(batch);
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
index 42e71e7e997..b1057e2a8d4 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
@@ -145,8 +145,9 @@ public boolean doChannelCloseEvent(final String remoteAddr, final Channel channe
callConsumerIdsChangeListener(ConsumerGroupEvent.UNREGISTER, next.getKey());
}
}
-
- callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
+ if (!isBroadcastMode(info.getMessageModel())) {
+ callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
+ }
}
}
return removed;
@@ -178,8 +179,6 @@ public boolean registerConsumer(final String group, final ClientChannelInfo clie
long start = System.currentTimeMillis();
ConsumerGroupInfo consumerGroupInfo = this.consumerTable.get(group);
if (null == consumerGroupInfo) {
- callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_REGISTER, group, clientChannelInfo,
- subList.stream().map(SubscriptionData::getTopic).collect(Collectors.toSet()));
ConsumerGroupInfo tmp = new ConsumerGroupInfo(group, consumeType, messageModel, consumeFromWhere);
ConsumerGroupInfo prev = this.consumerTable.putIfAbsent(group, tmp);
consumerGroupInfo = prev != null ? prev : tmp;
@@ -188,13 +187,17 @@ public boolean registerConsumer(final String group, final ClientChannelInfo clie
boolean r1 =
consumerGroupInfo.updateChannel(clientChannelInfo, consumeType, messageModel,
consumeFromWhere);
+ if (r1) {
+ callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_REGISTER, group, clientChannelInfo,
+ subList.stream().map(SubscriptionData::getTopic).collect(Collectors.toSet()));
+ }
boolean r2 = false;
if (updateSubscription) {
r2 = consumerGroupInfo.updateSubscription(subList);
}
if (r1 || r2) {
- if (isNotifyConsumerIdsChangedEnable) {
+ if (isNotifyConsumerIdsChangedEnable && !isBroadcastMode(consumerGroupInfo.getMessageModel())) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
@@ -217,7 +220,7 @@ public boolean registerConsumerWithoutSub(final String group, final ClientChanne
consumerGroupInfo = prev != null ? prev : tmp;
}
boolean updateChannelRst = consumerGroupInfo.updateChannel(clientChannelInfo, consumeType, messageModel, consumeFromWhere);
- if (updateChannelRst && isNotifyConsumerIdsChangedEnable) {
+ if (updateChannelRst && isNotifyConsumerIdsChangedEnable && !isBroadcastMode(consumerGroupInfo.getMessageModel())) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
if (null != this.brokerStatsManager) {
@@ -242,7 +245,7 @@ public void unregisterConsumer(final String group, final ClientChannelInfo clien
callConsumerIdsChangeListener(ConsumerGroupEvent.UNREGISTER, group);
}
}
- if (isNotifyConsumerIdsChangedEnable) {
+ if (isNotifyConsumerIdsChangedEnable && !isBroadcastMode(consumerGroupInfo.getMessageModel())) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
@@ -332,4 +335,8 @@ protected void callConsumerIdsChangeListener(ConsumerGroupEvent event, String gr
}
}
}
+
+ private boolean isBroadcastMode(final MessageModel messageModel) {
+ return MessageModel.BROADCASTING.equals(messageModel);
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java b/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java
index ededaf2c65e..762d917d640 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java
@@ -25,7 +25,9 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.transaction.queue.TransactionalMessageUtil;
import org.apache.rocketmq.client.consumer.PullStatus;
@@ -34,7 +36,6 @@
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.MixAll;
-import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.ThreadFactoryImpl;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.message.MessageConst;
@@ -51,6 +52,7 @@
import org.apache.rocketmq.store.MessageStore;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.PutMessageStatus;
+import org.apache.rocketmq.tieredstore.TieredMessageStore;
public class EscapeBridge {
protected static final Logger LOG = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
@@ -99,7 +101,7 @@ public PutMessageResult putMessage(MessageExtBrokerInner messageExt) {
try {
messageExt.setWaitStoreMsgOK(false);
- final SendResult sendResult = putMessageToRemoteBroker(messageExt);
+ final SendResult sendResult = putMessageToRemoteBroker(messageExt, null);
return transformSendResult2PutResult(sendResult);
} catch (Exception e) {
LOG.error("sendMessageInFailover to remote failed", e);
@@ -112,7 +114,10 @@ public PutMessageResult putMessage(MessageExtBrokerInner messageExt) {
}
}
- private SendResult putMessageToRemoteBroker(MessageExtBrokerInner messageExt) {
+ public SendResult putMessageToRemoteBroker(MessageExtBrokerInner messageExt, String brokerNameToSend) {
+ if (this.brokerController.getBrokerConfig().getBrokerName().equals(brokerNameToSend)) { // not remote broker
+ return null;
+ }
final boolean isTransHalfMessage = TransactionalMessageUtil.buildHalfTopic().equals(messageExt.getTopic());
MessageExtBrokerInner messageToPut = messageExt;
if (isTransHalfMessage) {
@@ -125,12 +130,26 @@ private SendResult putMessageToRemoteBroker(MessageExtBrokerInner messageExt) {
return null;
}
- final MessageQueue mqSelected = topicPublishInfo.selectOneMessageQueue(this.brokerController.getBrokerConfig().getBrokerName());
-
- messageToPut.setQueueId(mqSelected.getQueueId());
+ final MessageQueue mqSelected;
+ if (StringUtils.isEmpty(brokerNameToSend)) {
+ mqSelected = topicPublishInfo.selectOneMessageQueue(this.brokerController.getBrokerConfig().getBrokerName());
+ messageToPut.setQueueId(mqSelected.getQueueId());
+ brokerNameToSend = mqSelected.getBrokerName();
+ if (this.brokerController.getBrokerConfig().getBrokerName().equals(brokerNameToSend)) {
+ LOG.warn("putMessageToRemoteBroker failed, remote broker not found. Topic: {}, MsgId: {}, Broker: {}",
+ messageExt.getTopic(), messageExt.getMsgId(), brokerNameToSend);
+ return null;
+ }
+ } else {
+ mqSelected = new MessageQueue(messageExt.getTopic(), brokerNameToSend, messageExt.getQueueId());
+ }
- final String brokerNameToSend = mqSelected.getBrokerName();
final String brokerAddrToSend = this.brokerController.getTopicRouteInfoManager().findBrokerAddressInPublish(brokerNameToSend);
+ if (null == brokerAddrToSend) {
+ LOG.warn("putMessageToRemoteBroker failed, remote broker address not found. Topic: {}, MsgId: {}, Broker: {}",
+ messageExt.getTopic(), messageExt.getMsgId(), brokerNameToSend);
+ return null;
+ }
final long beginTimestamp = System.currentTimeMillis();
try {
@@ -263,34 +282,33 @@ private PutMessageResult transformSendResult2PutResult(SendResult sendResult) {
}
}
- public Pair getMessage(String topic, long offset, int queueId, String brokerName, boolean deCompressBody) {
+ public Triple getMessage(String topic, long offset, int queueId, String brokerName, boolean deCompressBody) {
return getMessageAsync(topic, offset, queueId, brokerName, deCompressBody).join();
}
- public CompletableFuture> getMessageAsync(String topic, long offset, int queueId, String brokerName, boolean deCompressBody) {
+ // Triple, check info and retry if and only if MessageExt is null
+ public CompletableFuture> getMessageAsync(String topic, long offset, int queueId, String brokerName, boolean deCompressBody) {
MessageStore messageStore = brokerController.getMessageStoreByBrokerName(brokerName);
if (messageStore != null) {
return messageStore.getMessageAsync(innerConsumerGroupName, topic, queueId, offset, 1, null)
.thenApply(result -> {
if (result == null) {
LOG.warn("getMessageResult is null , innerConsumerGroupName {}, topic {}, offset {}, queueId {}", innerConsumerGroupName, topic, offset, queueId);
- return new Pair<>(GetMessageStatus.MESSAGE_WAS_REMOVING, null);
+ return Triple.of(null, "getMessageResult is null", false); // local store, so no retry
}
List list = decodeMsgList(result, deCompressBody);
if (list == null || list.isEmpty()) {
- LOG.warn("Can not get msg , topic {}, offset {}, queueId {}, result is {}", topic, offset, queueId, result);
- return new Pair<>(result.getStatus(), null);
+ // OFFSET_FOUND_NULL returned by TieredMessageStore indicates exception occurred
+ boolean needRetry = GetMessageStatus.OFFSET_FOUND_NULL.equals(result.getStatus())
+ && messageStore instanceof TieredMessageStore;
+ LOG.warn("Can not get msg , topic {}, offset {}, queueId {}, needRetry {}, result is {}",
+ topic, offset, queueId, needRetry, result);
+ return Triple.of(null, "Can not get msg", needRetry);
}
- return new Pair<>(result.getStatus(), list.get(0));
+ return Triple.of(list.get(0), "", false);
});
} else {
- return getMessageFromRemoteAsync(topic, offset, queueId, brokerName)
- .thenApply(msg -> {
- if (msg == null) {
- return new Pair<>(GetMessageStatus.MESSAGE_WAS_REMOVING, null);
- }
- return new Pair<>(GetMessageStatus.FOUND, msg);
- });
+ return getMessageFromRemoteAsync(topic, offset, queueId, brokerName);
}
}
@@ -322,11 +340,12 @@ protected List decodeMsgList(GetMessageResult getMessageResult, bool
return foundList;
}
- protected MessageExt getMessageFromRemote(String topic, long offset, int queueId, String brokerName) {
+ protected Triple getMessageFromRemote(String topic, long offset, int queueId, String brokerName) {
return getMessageFromRemoteAsync(topic, offset, queueId, brokerName).join();
}
- protected CompletableFuture getMessageFromRemoteAsync(String topic, long offset, int queueId, String brokerName) {
+ // Triple, check info and retry if and only if MessageExt is null
+ protected CompletableFuture> getMessageFromRemoteAsync(String topic, long offset, int queueId, String brokerName) {
try {
String brokerAddr = this.brokerController.getTopicRouteInfoManager().findBrokerAddressInSubscribe(brokerName, MixAll.MASTER_ID, false);
if (null == brokerAddr) {
@@ -334,23 +353,25 @@ protected CompletableFuture getMessageFromRemoteAsync(String topic,
brokerAddr = this.brokerController.getTopicRouteInfoManager().findBrokerAddressInSubscribe(brokerName, MixAll.MASTER_ID, false);
if (null == brokerAddr) {
- LOG.warn("can't find broker address for topic {}", topic);
- return CompletableFuture.completedFuture(null);
+ LOG.warn("can't find broker address for topic {}, {}", topic, brokerName);
+ return CompletableFuture.completedFuture(Triple.of(null, "brokerAddress not found", true)); // maybe offline temporarily, so need retry
}
}
return this.brokerController.getBrokerOuterAPI().pullMessageFromSpecificBrokerAsync(brokerName,
brokerAddr, this.innerConsumerGroupName, topic, queueId, offset, 1, DEFAULT_PULL_TIMEOUT_MILLIS)
.thenApply(pullResult -> {
- if (pullResult.getPullStatus().equals(PullStatus.FOUND) && !pullResult.getMsgFoundList().isEmpty()) {
- return pullResult.getMsgFoundList().get(0);
+ if (pullResult.getLeft() != null
+ && PullStatus.FOUND.equals(pullResult.getLeft().getPullStatus())
+ && CollectionUtils.isNotEmpty(pullResult.getLeft().getMsgFoundList())) {
+ return Triple.of(pullResult.getLeft().getMsgFoundList().get(0), "", false);
}
- return null;
+ return Triple.of(null, pullResult.getMiddle(), pullResult.getRight());
});
} catch (Exception e) {
- LOG.error("Get message from remote failed.", e);
+ LOG.error("Get message from remote failed. {}, {}, {}, {}", topic, offset, queueId, brokerName, e);
}
- return CompletableFuture.completedFuture(null);
+ return CompletableFuture.completedFuture(Triple.of(null, "Get message from remote failed", true)); // need retry
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java b/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
index 0135ac929a7..ce8fdd88579 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
@@ -16,11 +16,15 @@
*/
package org.apache.rocketmq.broker.latency;
+import java.util.List;
+import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.AbstractBrokerRunnable;
+import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.ThreadFactoryImpl;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.constant.LoggerName;
@@ -42,13 +46,26 @@ public class BrokerFastFailure {
private volatile long jstackTime = System.currentTimeMillis();
+ private final List, Supplier>> cleanExpiredRequestQueueList = new ArrayList<>();
+
public BrokerFastFailure(final BrokerController brokerController) {
this.brokerController = brokerController;
+ initCleanExpiredRequestQueueList();
this.scheduledExecutorService = ThreadUtils.newScheduledThreadPool(1,
new ThreadFactoryImpl("BrokerFastFailureScheduledThread", true,
brokerController == null ? null : brokerController.getBrokerConfig()));
}
+ private void initCleanExpiredRequestQueueList() {
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getSendThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInSendQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getPullThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInPullQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getLitePullThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInLitePullQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getHeartbeatThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInHeartbeatQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getEndTransactionThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInTransactionQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getAckThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInAckQueue()));
+ cleanExpiredRequestQueueList.add(new Pair<>(this.brokerController.getAdminBrokerThreadPoolQueue(), () -> this.brokerController.getBrokerConfig().getWaitTimeMillsInAdminBrokerQueue()));
+ }
+
public static RequestTask castRunnable(final Runnable runnable) {
try {
if (runnable instanceof FutureTaskExt) {
@@ -98,26 +115,9 @@ private void cleanExpiredRequest() {
}
}
- cleanExpiredRequestInQueue(this.brokerController.getSendThreadPoolQueue(),
- this.brokerController.getBrokerConfig().getWaitTimeMillsInSendQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getPullThreadPoolQueue(),
- this.brokerController.getBrokerConfig().getWaitTimeMillsInPullQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getLitePullThreadPoolQueue(),
- this.brokerController.getBrokerConfig().getWaitTimeMillsInLitePullQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getHeartbeatThreadPoolQueue(),
- this.brokerController.getBrokerConfig().getWaitTimeMillsInHeartbeatQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getEndTransactionThreadPoolQueue(), this
- .brokerController.getBrokerConfig().getWaitTimeMillsInTransactionQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getAckThreadPoolQueue(),
- brokerController.getBrokerConfig().getWaitTimeMillsInAckQueue());
-
- cleanExpiredRequestInQueue(this.brokerController.getAdminBrokerThreadPoolQueue(),
- brokerController.getBrokerConfig().getWaitTimeMillsInAdminBrokerQueue());
+ for (Pair, Supplier> pair : cleanExpiredRequestQueueList) {
+ cleanExpiredRequestInQueue(pair.getObject1(), pair.getObject2().get());
+ }
}
void cleanExpiredRequestInQueue(final BlockingQueue blockingQueue, final long maxWaitTimeMillsInQueue) {
@@ -154,6 +154,11 @@ void cleanExpiredRequestInQueue(final BlockingQueue blockingQueue, fin
}
}
+ public synchronized void addCleanExpiredRequestQueue(BlockingQueue cleanExpiredRequestQueue,
+ Supplier maxWaitTimeMillsInQueueSupplier) {
+ cleanExpiredRequestQueueList.add(new Pair<>(cleanExpiredRequestQueue, maxWaitTimeMillsInQueueSupplier));
+ }
+
public void shutdown() {
this.scheduledExecutorService.shutdown();
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/offset/RocksDBConsumerOffsetManager.java b/broker/src/main/java/org/apache/rocketmq/broker/offset/RocksDBConsumerOffsetManager.java
index 05b53b0bcf2..de293fc4992 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/offset/RocksDBConsumerOffsetManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/offset/RocksDBConsumerOffsetManager.java
@@ -22,7 +22,7 @@
import java.util.concurrent.ConcurrentMap;
import org.apache.rocketmq.broker.BrokerController;
-import org.apache.rocketmq.common.config.RocksDBConfigManager;
+import org.apache.rocketmq.broker.RocksDBConfigManager;
import org.apache.rocketmq.common.utils.DataConverter;
import org.rocksdb.WriteBatch;
@@ -31,14 +31,19 @@
public class RocksDBConsumerOffsetManager extends ConsumerOffsetManager {
+ protected RocksDBConfigManager rocksDBConfigManager;
+
public RocksDBConsumerOffsetManager(BrokerController brokerController) {
super(brokerController);
- this.rocksDBConfigManager = new RocksDBConfigManager(brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
+ this.rocksDBConfigManager = new RocksDBConfigManager(configFilePath(), brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
}
@Override
public boolean load() {
- return this.rocksDBConfigManager.load(configFilePath(), this::decode0);
+ if (!rocksDBConfigManager.init()) {
+ return false;
+ }
+ return this.rocksDBConfigManager.loadData(this::decodeOffset);
}
@Override
@@ -56,8 +61,7 @@ protected void removeConsumerOffset(String topicAtGroup) {
}
}
- @Override
- protected void decode0(final byte[] key, final byte[] body) {
+ protected void decodeOffset(final byte[] key, final byte[] body) {
String topicAtGroup = new String(key, DataConverter.CHARSET_UTF8);
RocksDBOffsetSerializeWrapper wrapper = JSON.parseObject(body, RocksDBOffsetSerializeWrapper.class);
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
index d1cdb297fed..83edd88408a 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
@@ -31,6 +31,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.auth.config.AuthConfig;
@@ -336,7 +337,7 @@ public void sendHeartbeatViaDataVersion(
final DataVersion dataVersion,
final boolean isInBrokerContainer) {
List nameServerAddressList = this.remotingClient.getAvailableNameSrvList();
- if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
+ if (nameServerAddressList != null && !nameServerAddressList.isEmpty()) {
final QueryDataVersionRequestHeader requestHeader = new QueryDataVersionRequestHeader();
requestHeader.setBrokerAddr(brokerAddr);
requestHeader.setBrokerName(brokerName);
@@ -405,7 +406,7 @@ public BrokerSyncInfo retrieveBrokerHaInfo(String masterBrokerAddr)
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
- ExchangeHAInfoResponseHeader responseHeader = (ExchangeHAInfoResponseHeader) response.decodeCommandCustomHeader(ExchangeHAInfoResponseHeader.class);
+ ExchangeHAInfoResponseHeader responseHeader = response.decodeCommandCustomHeader(ExchangeHAInfoResponseHeader.class);
return new BrokerSyncInfo(responseHeader.getMasterHaAddress(), responseHeader.getMasterFlushOffset(), responseHeader.getMasterAddress());
}
default:
@@ -574,8 +575,7 @@ private RegisterBrokerResult registerBroker(
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
- RegisterBrokerResponseHeader responseHeader =
- (RegisterBrokerResponseHeader) response.decodeCommandCustomHeader(RegisterBrokerResponseHeader.class);
+ RegisterBrokerResponseHeader responseHeader = response.decodeCommandCustomHeader(RegisterBrokerResponseHeader.class);
RegisterBrokerResult result = new RegisterBrokerResult();
result.setMasterAddr(responseHeader.getMasterAddr());
result.setHaServerAddr(responseHeader.getHaServerAddr());
@@ -725,7 +725,7 @@ public void run0() {
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
QueryDataVersionResponseHeader queryDataVersionResponseHeader =
- (QueryDataVersionResponseHeader) response.decodeCommandCustomHeader(QueryDataVersionResponseHeader.class);
+ response.decodeCommandCustomHeader(QueryDataVersionResponseHeader.class);
changed = queryDataVersionResponseHeader.getChanged();
byte[] body = response.getBody();
if (body != null) {
@@ -887,7 +887,7 @@ public long getMaxOffset(final String addr, final String topic, final int queueI
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
- GetMaxOffsetResponseHeader responseHeader = (GetMaxOffsetResponseHeader) response.decodeCommandCustomHeader(GetMaxOffsetResponseHeader.class);
+ GetMaxOffsetResponseHeader responseHeader = response.decodeCommandCustomHeader(GetMaxOffsetResponseHeader.class);
return responseHeader.getOffset();
}
@@ -909,7 +909,7 @@ public long getMinOffset(final String addr, final String topic, final int queueI
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
- GetMinOffsetResponseHeader responseHeader = (GetMinOffsetResponseHeader) response.decodeCommandCustomHeader(GetMinOffsetResponseHeader.class);
+ GetMinOffsetResponseHeader responseHeader = response.decodeCommandCustomHeader(GetMinOffsetResponseHeader.class);
return responseHeader.getOffset();
}
@@ -1096,8 +1096,7 @@ private SendResult processSendResponse(
break;
}
if (sendStatus != null) {
- SendMessageResponseHeader responseHeader =
- (SendMessageResponseHeader) response.decodeCommandCustomHeader(SendMessageResponseHeader.class);
+ SendMessageResponseHeader responseHeader = response.decodeCommandCustomHeader(SendMessageResponseHeader.class);
//If namespace not null , reset Topic without namespace.
String topic = msg.getTopic();
@@ -1270,7 +1269,7 @@ public Pair> brokerElect(String controllerA
// Only record success response.
case CONTROLLER_MASTER_STILL_EXIST:
case SUCCESS:
- final ElectMasterResponseHeader responseHeader = (ElectMasterResponseHeader) response.decodeCommandCustomHeader(ElectMasterResponseHeader.class);
+ final ElectMasterResponseHeader responseHeader = response.decodeCommandCustomHeader(ElectMasterResponseHeader.class);
final ElectMasterResponseBody responseBody = RemotingSerializable.decode(response.getBody(), ElectMasterResponseBody.class);
return new Pair<>(responseHeader, responseBody.getSyncStateSet());
}
@@ -1285,7 +1284,7 @@ public GetNextBrokerIdResponseHeader getNextBrokerId(final String clusterName, f
final RemotingCommand response = this.remotingClient.invokeSync(controllerAddress, request, 3000);
assert response != null;
if (response.getCode() == SUCCESS) {
- return (GetNextBrokerIdResponseHeader) response.decodeCommandCustomHeader(GetNextBrokerIdResponseHeader.class);
+ return response.decodeCommandCustomHeader(GetNextBrokerIdResponseHeader.class);
}
throw new MQBrokerException(response.getCode(), response.getRemark());
}
@@ -1297,7 +1296,7 @@ public ApplyBrokerIdResponseHeader applyBrokerId(final String clusterName, final
final RemotingCommand response = this.remotingClient.invokeSync(controllerAddress, request, 3000);
assert response != null;
if (response.getCode() == SUCCESS) {
- return (ApplyBrokerIdResponseHeader) response.decodeCommandCustomHeader(ApplyBrokerIdResponseHeader.class);
+ return response.decodeCommandCustomHeader(ApplyBrokerIdResponseHeader.class);
}
throw new MQBrokerException(response.getCode(), response.getRemark());
}
@@ -1310,7 +1309,7 @@ public Pair> registerBrokerT
final RemotingCommand response = this.remotingClient.invokeSync(controllerAddress, request, 3000);
assert response != null;
if (response.getCode() == SUCCESS) {
- RegisterBrokerToControllerResponseHeader responseHeader = (RegisterBrokerToControllerResponseHeader) response.decodeCommandCustomHeader(RegisterBrokerToControllerResponseHeader.class);
+ RegisterBrokerToControllerResponseHeader responseHeader = response.decodeCommandCustomHeader(RegisterBrokerToControllerResponseHeader.class);
Set syncStateSet = RemotingSerializable.decode(response.getBody(), SyncStateSet.class).getSyncStateSet();
return new Pair<>(responseHeader, syncStateSet);
}
@@ -1328,7 +1327,7 @@ public Pair getReplicaInfo(final Str
assert response != null;
switch (response.getCode()) {
case SUCCESS: {
- final GetReplicaInfoResponseHeader header = (GetReplicaInfoResponseHeader) response.decodeCommandCustomHeader(GetReplicaInfoResponseHeader.class);
+ final GetReplicaInfoResponseHeader header = response.decodeCommandCustomHeader(GetReplicaInfoResponseHeader.class);
assert response.getBody() != null;
final SyncStateSet stateSet = RemotingSerializable.decode(response.getBody(), SyncStateSet.class);
return new Pair<>(header, stateSet);
@@ -1380,7 +1379,8 @@ public void run0() {
});
}
- public CompletableFuture pullMessageFromSpecificBrokerAsync(String brokerName, String brokerAddr,
+ // Triple, should check info and retry if and only if PullResult is null
+ public CompletableFuture> pullMessageFromSpecificBrokerAsync(String brokerName, String brokerAddr,
String consumerGroup, String topic, int queueId, long offset,
int maxNums, long timeoutMillis) throws RemotingException, InterruptedException {
PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
@@ -1399,7 +1399,7 @@ public CompletableFuture pullMessageFromSpecificBrokerAsync(String b
requestHeader.setBrokerName(brokerName);
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PULL_MESSAGE, requestHeader);
- CompletableFuture pullResultFuture = new CompletableFuture<>();
+ CompletableFuture> pullResultFuture = new CompletableFuture<>();
this.remotingClient.invokeAsync(brokerAddr, request, timeoutMillis, new InvokeCallback() {
@Override
public void operationComplete(ResponseFuture responseFuture) {
@@ -1411,15 +1411,16 @@ public void operationSucceed(RemotingCommand response) {
try {
PullResultExt pullResultExt = processPullResponse(response, brokerAddr);
processPullResult(pullResultExt, brokerName, queueId);
- pullResultFuture.complete(pullResultExt);
+ pullResultFuture.complete(Triple.of(pullResultExt, pullResultExt.getPullStatus().name(), false)); // found or not found really, so no retry
} catch (Exception e) {
- pullResultFuture.complete(new PullResult(PullStatus.NO_MATCHED_MSG, -1, -1, -1, new ArrayList<>()));
+ // retry when NO_PERMISSION, SUBSCRIPTION_GROUP_NOT_EXIST etc. even when TOPIC_NOT_EXIST
+ pullResultFuture.complete(Triple.of(null, "Response Code:" + response.getCode(), true));
}
}
@Override
public void operationFail(Throwable throwable) {
- pullResultFuture.complete(new PullResult(PullStatus.NO_MATCHED_MSG, -1, -1, -1, new ArrayList<>()));
+ pullResultFuture.complete(Triple.of(null, throwable.getMessage(), true));
}
});
return pullResultFuture;
@@ -1447,8 +1448,7 @@ private PullResultExt processPullResponse(
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
- PullMessageResponseHeader responseHeader =
- (PullMessageResponseHeader) response.decodeCommandCustomHeader(PullMessageResponseHeader.class);
+ PullMessageResponseHeader responseHeader = response.decodeCommandCustomHeader(PullMessageResponseHeader.class);
return new PullResultExt(pullStatus, responseHeader.getNextBeginOffset(), responseHeader.getMinOffset(),
responseHeader.getMaxOffset(), null, responseHeader.getSuggestWhichBrokerId(), response.getBody(), responseHeader.getOffsetDelta());
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
index 1b29ff173cc..28bd2549145 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
@@ -131,6 +131,7 @@
import org.apache.rocketmq.remoting.protocol.body.QuerySubscriptionResponseBody;
import org.apache.rocketmq.remoting.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.remoting.protocol.body.ResetOffsetBody;
+import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupList;
import org.apache.rocketmq.remoting.protocol.body.SyncStateSet;
import org.apache.rocketmq.remoting.protocol.body.TopicConfigAndMappingSerializeWrapper;
import org.apache.rocketmq.remoting.protocol.body.TopicList;
@@ -282,6 +283,8 @@ public RemotingCommand processRequest(ChannelHandlerContext ctx,
return this.unlockBatchMQ(ctx, request);
case RequestCode.UPDATE_AND_CREATE_SUBSCRIPTIONGROUP:
return this.updateAndCreateSubscriptionGroup(ctx, request);
+ case RequestCode.UPDATE_AND_CREATE_SUBSCRIPTIONGROUP_LIST:
+ return this.updateAndCreateSubscriptionGroupList(ctx, request);
case RequestCode.GET_ALL_SUBSCRIPTIONGROUP_CONFIG:
return this.getAllSubscriptionGroup(ctx, request);
case RequestCode.DELETE_SUBSCRIPTIONGROUP:
@@ -1571,6 +1574,41 @@ private RemotingCommand updateAndCreateSubscriptionGroup(ChannelHandlerContext c
return response;
}
+ private RemotingCommand updateAndCreateSubscriptionGroupList(ChannelHandlerContext ctx, RemotingCommand request) {
+ final long startTime = System.nanoTime();
+
+ final SubscriptionGroupList subscriptionGroupList = SubscriptionGroupList.decode(request.getBody(), SubscriptionGroupList.class);
+ final List groupConfigList = subscriptionGroupList.getGroupConfigList();
+
+ final StringBuilder builder = new StringBuilder();
+ for (SubscriptionGroupConfig config : groupConfigList) {
+ builder.append(config.getGroupName()).append(";");
+ }
+ final String groupNames = builder.toString();
+ LOGGER.info("AdminBrokerProcessor#updateAndCreateSubscriptionGroupList: groupNames: {}, called by {}",
+ groupNames,
+ RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+
+ final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ try {
+ this.brokerController.getSubscriptionGroupManager().updateSubscriptionGroupConfigList(groupConfigList);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setRemark(null);
+ } finally {
+ long executionTime = (System.nanoTime() - startTime) / 1000000L;
+ LOGGER.info("executionTime of create updateAndCreateSubscriptionGroupList: {} is {} ms", groupNames, executionTime);
+ InvocationStatus status = response.getCode() == ResponseCode.SUCCESS ?
+ InvocationStatus.SUCCESS : InvocationStatus.FAILURE;
+ Attributes attributes = BrokerMetricsManager.newAttributesBuilder()
+ .put(LABEL_INVOCATION_STATUS, status.getName())
+ .build();
+ BrokerMetricsManager.consumerGroupCreateExecuteTime.record(executionTime, attributes);
+ }
+
+ return response;
+ }
+
+
private void initConsumerOffset(String clientHost, String groupName, int mode, TopicConfig topicConfig) {
String topic = topicConfig.getTopicName();
for (int queueId = 0; queueId < topicConfig.getReadQueueNums(); queueId++) {
@@ -1941,7 +1979,7 @@ private RemotingCommand getAllMessageRequestMode(ChannelHandlerContext ctx, Remo
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
String content = this.brokerController.getQueryAssignmentProcessor().getMessageRequestModeManager().encode();
- if (content != null && content.length() > 0) {
+ if (content != null && !content.isEmpty()) {
try {
response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
} catch (UnsupportedEncodingException e) {
@@ -2024,7 +2062,7 @@ private RemotingCommand resetOffsetInner(String topic, String group, int queueId
Map queueOffsetMap = new HashMap<>();
// Reset offset for all queues belonging to the specified topic
- TopicConfig topicConfig = brokerController.getTopicConfigManager().getTopicConfigTable().get(topic);
+ TopicConfig topicConfig = brokerController.getTopicConfigManager().selectTopicConfig(topic);
if (null == topicConfig) {
response.setCode(ResponseCode.TOPIC_NOT_EXIST);
response.setRemark("Topic " + topic + " does not exist");
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
index 0304a5dab08..2d76c5a3caa 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
@@ -373,7 +373,7 @@ public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingC
// considered the same type because they share the same retry flag in previous fields.
// Therefore, needRetryV1 is designed as a subset of needRetry, and within a single request,
// only one type of retry topic is able to call popMsgFromQueue.
- boolean needRetry = randomQ % 5 == 0;
+ boolean needRetry = randomQ < brokerConfig.getPopFromRetryProbability();
boolean needRetryV1 = false;
if (brokerConfig.isEnableRetryTopicV2() && brokerConfig.isRetrieveMessageFromPopRetryTopicV1()) {
needRetryV1 = randomQ % 2 == 0;
@@ -431,6 +431,11 @@ public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingC
PollingResult pollingResult = popLongPollingService.polling(
ctx, request, new PollingHeader(requestHeader), finalSubscriptionData, finalMessageFilter);
if (PollingResult.POLLING_SUC == pollingResult) {
+ if (restNum > 0) {
+ popLongPollingService.notifyMessageArriving(
+ requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getConsumerGroup(),
+ null, 0L, null, null);
+ }
return null;
} else if (PollingResult.POLLING_FULL == pollingResult) {
finalResponse.setCode(ResponseCode.POLLING_FULL);
@@ -535,6 +540,7 @@ private CompletableFuture popMsgFromQueue(String topic, String attemptId,
return future;
}
+ future.whenComplete((result, throwable) -> queueLockManager.unLock(lockKey));
if (isPopShouldStop(topic, requestHeader.getConsumerGroup(), queueId)) {
POP_LOGGER.warn("Too much msgs unacked, then stop poping. topic={}, group={}, queueId={}", topic, requestHeader.getConsumerGroup(), queueId);
restNum = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum;
@@ -543,21 +549,26 @@ private CompletableFuture popMsgFromQueue(String topic, String attemptId,
}
try {
- future.whenComplete((result, throwable) -> queueLockManager.unLock(lockKey));
offset = getPopOffset(topic, requestHeader.getConsumerGroup(), queueId, requestHeader.getInitMode(),
true, lockKey, true);
- if (isOrder && brokerController.getConsumerOrderInfoManager().checkBlock(attemptId, topic,
- requestHeader.getConsumerGroup(), queueId, requestHeader.getInvisibleTime())) {
- future.complete(this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum);
- return future;
- }
+ // Current requests would calculate the total number of messages
+ // waiting to be filtered for new message arrival notifications in
+ // the long-polling service, need disregarding the backlog in order
+ // consumption scenario. If rest message num including the blocked
+ // queue accumulation would lead to frequent unnecessary wake-ups
+ // of long-polling requests, resulting unnecessary CPU usage.
+ // When client ack message, long-polling request would be notifications
+ // by AckMessageProcessor.ackOrderly() and message will not be delayed.
if (isOrder) {
+ if (brokerController.getConsumerOrderInfoManager().checkBlock(
+ attemptId, topic, requestHeader.getConsumerGroup(), queueId, requestHeader.getInvisibleTime())) {
+ // should not add accumulation(max offset - consumer offset) here
+ future.complete(restNum);
+ return future;
+ }
this.brokerController.getPopInflightMessageCounter().clearInFlightMessageNum(
- topic,
- requestHeader.getConsumerGroup(),
- queueId
- );
+ topic, requestHeader.getConsumerGroup(), queueId);
}
if (getMessageResult.getMessageMapedList().size() >= requestHeader.getMaxMsgNums()) {
@@ -723,8 +734,8 @@ private long getPopOffset(String topic, String group, int queueId, int initMode,
private long getInitOffset(String topic, String group, int queueId, int initMode, boolean init) {
long offset;
- if (ConsumeInitMode.MIN == initMode) {
- return this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
+ if (ConsumeInitMode.MIN == initMode || topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
+ offset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
} else {
if (this.brokerController.getBrokerConfig().isInitPopOffsetByCheckMsgInMem() &&
this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId) <= 0 &&
@@ -738,10 +749,10 @@ private long getInitOffset(String topic, String group, int queueId, int initMode
offset = 0;
}
}
- if (init) {
- this.brokerController.getConsumerOffsetManager().commitOffset(
+ }
+ if (init) { // whichever initMode
+ this.brokerController.getConsumerOffsetManager().commitOffset(
"getPopOffset", group, topic, queueId, offset);
- }
}
return offset;
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
index e3ba492f280..4b141d29102 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
@@ -27,6 +27,7 @@
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.metrics.BrokerMetricsManager;
import org.apache.rocketmq.broker.metrics.PopMetricsManager;
@@ -34,6 +35,7 @@
import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.common.KeyBuilder;
import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.PopAckConstants;
import org.apache.rocketmq.common.ServiceThread;
@@ -51,7 +53,6 @@
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
import org.apache.rocketmq.store.AppendMessageStatus;
import org.apache.rocketmq.store.GetMessageResult;
-import org.apache.rocketmq.store.GetMessageStatus;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.pop.AckMsg;
import org.apache.rocketmq.store.pop.BatchAckMsg;
@@ -63,6 +64,7 @@
public class PopReviveService extends ServiceThread {
private static final Logger POP_LOGGER = LoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
+ private final int[] ckRewriteIntervalsInSeconds = new int[] { 10, 20, 30, 60, 120, 180, 240, 300, 360, 420, 480, 540, 600, 1200, 1800, 3600, 7200 };
private int queueId;
private BrokerController brokerController;
@@ -125,7 +127,7 @@ private boolean reviveRetry(PopCheckPoint popCheckPoint, MessageExt messageExt)
msgInner.getProperties().put(MessageConst.PROPERTY_FIRST_POP_TIME, String.valueOf(popCheckPoint.getPopTime()));
}
msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
- addRetryTopicIfNoExit(msgInner.getTopic(), popCheckPoint.getCId());
+ addRetryTopicIfNotExist(msgInner.getTopic(), popCheckPoint.getCId());
PutMessageResult putMessageResult = brokerController.getEscapeBridge().putMessageToSpecificQueue(msgInner);
PopMetricsManager.incPopReviveRetryMessageCount(popCheckPoint, putMessageResult.getPutMessageStatus());
if (brokerController.getBrokerConfig().isEnablePopLog()) {
@@ -153,7 +155,7 @@ private void initPopRetryOffset(String topic, String consumerGroup) {
}
}
- private void addRetryTopicIfNoExit(String topic, String consumerGroup) {
+ public void addRetryTopicIfNotExist(String topic, String consumerGroup) {
if (brokerController != null) {
TopicConfig topicConfig = brokerController.getTopicConfigManager().selectTopicConfig(topic);
if (topicConfig != null) {
@@ -196,9 +198,9 @@ private boolean reachTail(PullResult pullResult, long offset) {
|| pullResult.getPullStatus() == PullStatus.OFFSET_ILLEGAL && offset == pullResult.getMaxOffset();
}
- private CompletableFuture> getBizMessage(String topic, long offset, int queueId,
- String brokerName) {
- return this.brokerController.getEscapeBridge().getMessageAsync(topic, offset, queueId, brokerName, false);
+ // Triple
+ public CompletableFuture> getBizMessage(PopCheckPoint popCheckPoint, long offset) {
+ return this.brokerController.getEscapeBridge().getMessageAsync(popCheckPoint.getTopic(), offset, popCheckPoint.getQueueId(), popCheckPoint.getBrokerName(), false);
}
public PullResult getMessage(String group, String topic, int queueId, long offset, int nums,
@@ -355,7 +357,7 @@ protected void consumeReviveMessage(ConsumeReviveObj consumeReviveObj) {
if (point.getTopic() == null || point.getCId() == null) {
continue;
}
- map.put(point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime(), point);
+ map.put(point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime() + point.getBrokerName(), point);
PopMetricsManager.incPopReviveCkGetCount(point, queueId);
point.setReviveOffset(messageExt.getQueueOffset());
if (firstRt == 0) {
@@ -368,7 +370,7 @@ protected void consumeReviveMessage(ConsumeReviveObj consumeReviveObj) {
}
AckMsg ackMsg = JSON.parseObject(raw, AckMsg.class);
PopMetricsManager.incPopReviveAckGetCount(ackMsg, queueId);
- String mergeKey = ackMsg.getTopic() + ackMsg.getConsumerGroup() + ackMsg.getQueueId() + ackMsg.getStartOffset() + ackMsg.getPopTime();
+ String mergeKey = ackMsg.getTopic() + ackMsg.getConsumerGroup() + ackMsg.getQueueId() + ackMsg.getStartOffset() + ackMsg.getPopTime() + ackMsg.getBrokerName();
PopCheckPoint point = map.get(mergeKey);
if (point == null) {
if (!brokerController.getBrokerConfig().isEnableSkipLongAwaitingAck()) {
@@ -393,7 +395,7 @@ protected void consumeReviveMessage(ConsumeReviveObj consumeReviveObj) {
BatchAckMsg bAckMsg = JSON.parseObject(raw, BatchAckMsg.class);
PopMetricsManager.incPopReviveAckGetCount(bAckMsg, queueId);
- String mergeKey = bAckMsg.getTopic() + bAckMsg.getConsumerGroup() + bAckMsg.getQueueId() + bAckMsg.getStartOffset() + bAckMsg.getPopTime();
+ String mergeKey = bAckMsg.getTopic() + bAckMsg.getConsumerGroup() + bAckMsg.getQueueId() + bAckMsg.getStartOffset() + bAckMsg.getPopTime() + bAckMsg.getBrokerName();
PopCheckPoint point = map.get(mergeKey);
if (point == null) {
if (!brokerController.getBrokerConfig().isEnableSkipLongAwaitingAck()) {
@@ -491,6 +493,8 @@ protected void mergeAndRevive(ConsumeReviveObj consumeReviveObj) throws Throwabl
PopCheckPoint oldCK = inflightReviveRequestMap.firstKey();
rePutCK(oldCK, pair);
inflightReviveRequestMap.remove(oldCK);
+ POP_LOGGER.warn("stay too long, remove from reviveRequestMap, {}, {}, {}, {}", popCheckPoint.getTopic(),
+ popCheckPoint.getBrokerName(), popCheckPoint.getQueueId(), popCheckPoint.getStartOffset());
}
}
@@ -523,23 +527,13 @@ private void reviveMsgFromCk(PopCheckPoint popCheckPoint) {
// retry msg
long msgOffset = popCheckPoint.ackOffsetByIndex((byte) j);
- CompletableFuture> future = getBizMessage(popCheckPoint.getTopic(), msgOffset, popCheckPoint.getQueueId(), popCheckPoint.getBrokerName())
- .thenApply(resultPair -> {
- GetMessageStatus getMessageStatus = resultPair.getObject1();
- MessageExt message = resultPair.getObject2();
+ CompletableFuture> future = getBizMessage(popCheckPoint, msgOffset)
+ .thenApply(rst -> {
+ MessageExt message = rst.getLeft();
if (message == null) {
- POP_LOGGER.debug("reviveQueueId={}, can not get biz msg topic is {}, offset is {}, then continue",
- queueId, popCheckPoint.getTopic(), msgOffset);
- switch (getMessageStatus) {
- case MESSAGE_WAS_REMOVING:
- case OFFSET_TOO_SMALL:
- case NO_MATCHED_LOGIC_QUEUE:
- case NO_MESSAGE_IN_QUEUE:
- return new Pair<>(msgOffset, true);
- default:
- return new Pair<>(msgOffset, false);
-
- }
+ POP_LOGGER.info("reviveQueueId={}, can not get biz msg, topic:{}, qid:{}, offset:{}, brokerName:{}, info:{}, retry:{}, then continue",
+ queueId, popCheckPoint.getTopic(), popCheckPoint.getQueueId(), msgOffset, popCheckPoint.getBrokerName(), UtilAll.frontStringAtLeast(rst.getMiddle(), 60), rst.getRight());
+ return new Pair<>(msgOffset, !rst.getRight()); // Pair.object2 means OK or not, Triple.right value means needRetry
}
boolean result = reviveRetry(popCheckPoint, message);
return new Pair<>(msgOffset, result);
@@ -572,6 +566,13 @@ private void reviveMsgFromCk(PopCheckPoint popCheckPoint) {
}
private void rePutCK(PopCheckPoint oldCK, Pair pair) {
+ int rePutTimes = oldCK.parseRePutTimes();
+ if (rePutTimes >= ckRewriteIntervalsInSeconds.length && brokerController.getBrokerConfig().isSkipWhenCKRePutReachMaxTimes()) {
+ POP_LOGGER.warn("rePut CK reach max times, drop it. {}, {}, {}, {}-{}, {}, {}, {}", oldCK.getTopic(), oldCK.getCId(),
+ oldCK.getBrokerName(), oldCK.getQueueId(), pair.getObject1(), oldCK.getPopTime(), oldCK.getInvisibleTime(), rePutTimes);
+ return;
+ }
+
PopCheckPoint newCk = new PopCheckPoint();
newCk.setBitMap(0);
newCk.setNum((byte) 1);
@@ -583,6 +584,12 @@ private void rePutCK(PopCheckPoint oldCK, Pair pair) {
newCk.setQueueId(oldCK.getQueueId());
newCk.setBrokerName(oldCK.getBrokerName());
newCk.addDiff(0);
+ newCk.setRePutTimes(String.valueOf(rePutTimes + 1)); // always increment even if removed from reviveRequestMap
+ if (oldCK.getReviveTime() <= System.currentTimeMillis()) {
+ // never expect an ACK matched in the future, we just use it to rewrite CK and try to revive retry message next time
+ int intervalIndex = rePutTimes >= ckRewriteIntervalsInSeconds.length ? ckRewriteIntervalsInSeconds.length - 1 : rePutTimes;
+ newCk.setInvisibleTime(oldCK.getInvisibleTime() + ckRewriteIntervalsInSeconds[intervalIndex] * 1000);
+ }
MessageExtBrokerInner ckMsg = brokerController.getPopMessageProcessor().buildCkMsg(newCk, queueId);
brokerController.getMessageStore().putMessage(ckMsg);
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessor.java
index d55f1b5b7fb..2f4cb7b15f8 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessor.java
@@ -174,7 +174,14 @@ private Set doLoadBalance(final String topic, final String consume
break;
}
case CLUSTERING: {
- Set mqSet = topicRouteInfoManager.getTopicSubscribeInfo(topic);
+ Set mqSet;
+ if (MixAll.isLmq(topic)) {
+ mqSet = new HashSet<>();
+ mqSet.add(new MessageQueue(
+ topic, brokerController.getBrokerConfig().getBrokerName(), (int)MixAll.LMQ_QUEUE_ID));
+ } else {
+ mqSet = topicRouteInfoManager.getTopicSubscribeInfo(topic);
+ }
if (null == mqSet) {
if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
log.warn("QueryLoad: no assignment for group[{}], the topic[{}] does not exist.", consumerGroup, topic);
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/subscription/LmqSubscriptionGroupManager.java b/broker/src/main/java/org/apache/rocketmq/broker/subscription/LmqSubscriptionGroupManager.java
index 018083811e8..69e59fd8e7f 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/subscription/LmqSubscriptionGroupManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/subscription/LmqSubscriptionGroupManager.java
@@ -43,4 +43,13 @@ public void updateSubscriptionGroupConfig(final SubscriptionGroupConfig config)
}
super.updateSubscriptionGroupConfig(config);
}
+
+ @Override
+ public boolean containsSubscriptionGroup(String group) {
+ if (MixAll.isLmq(group)) {
+ return true;
+ } else {
+ return super.containsSubscriptionGroup(group);
+ }
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBLmqSubscriptionGroupManager.java b/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBLmqSubscriptionGroupManager.java
index 8c05d0bd98f..e4de25756bf 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBLmqSubscriptionGroupManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBLmqSubscriptionGroupManager.java
@@ -43,4 +43,13 @@ public void updateSubscriptionGroupConfig(final SubscriptionGroupConfig config)
}
super.updateSubscriptionGroupConfig(config);
}
+
+ @Override
+ public boolean containsSubscriptionGroup(String group) {
+ if (MixAll.isLmq(group)) {
+ return true;
+ } else {
+ return super.containsSubscriptionGroup(group);
+ }
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBSubscriptionGroupManager.java b/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBSubscriptionGroupManager.java
index e9a81a8d686..7df72dbe686 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBSubscriptionGroupManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/subscription/RocksDBSubscriptionGroupManager.java
@@ -16,39 +16,116 @@
*/
package org.apache.rocketmq.broker.subscription;
-import java.io.File;
-
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.rocketmq.broker.BrokerController;
-import org.apache.rocketmq.common.config.RocksDBConfigManager;
+import org.apache.rocketmq.broker.RocksDBConfigManager;
+import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.utils.DataConverter;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.rocksdb.RocksIterator;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.serializer.SerializerFeature;
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.BiConsumer;
public class RocksDBSubscriptionGroupManager extends SubscriptionGroupManager {
+ protected RocksDBConfigManager rocksDBConfigManager;
+
public RocksDBSubscriptionGroupManager(BrokerController brokerController) {
super(brokerController, false);
- this.rocksDBConfigManager = new RocksDBConfigManager(brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
+ this.rocksDBConfigManager = new RocksDBConfigManager(rocksdbConfigFilePath(), brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
}
@Override
public boolean load() {
- if (!this.rocksDBConfigManager.load(configFilePath(), this::decode0)) {
+ if (!rocksDBConfigManager.init()) {
+ return false;
+ }
+ if (!loadDataVersion() || !loadSubscriptionGroupAndForbidden()) {
return false;
}
this.init();
return true;
}
+ public boolean loadDataVersion() {
+ return this.rocksDBConfigManager.loadDataVersion();
+ }
+
+ public boolean loadSubscriptionGroupAndForbidden() {
+ return this.rocksDBConfigManager.loadData(this::decodeSubscriptionGroup)
+ && this.loadForbidden(this::decodeForbidden)
+ && merge();
+ }
+
+ public boolean loadForbidden(BiConsumer biConsumer) {
+ try (RocksIterator iterator = this.rocksDBConfigManager.configRocksDBStorage.forbiddenIterator()) {
+ iterator.seekToFirst();
+ while (iterator.isValid()) {
+ biConsumer.accept(iterator.key(), iterator.value());
+ iterator.next();
+ }
+ }
+ return true;
+ }
+
+
+ private boolean merge() {
+ if (!brokerController.getMessageStoreConfig().isTransferMetadataJsonToRocksdb()) {
+ log.info("The switch is off, no merge operation is needed.");
+ return true;
+ }
+ if (!UtilAll.isPathExists(this.configFilePath()) && !UtilAll.isPathExists(this.configFilePath() + ".bak")) {
+ log.info("json file and json back file not exist, so skip merge");
+ return true;
+ }
+
+ if (!super.load()) {
+ log.error("load group and forbidden info from json file error, startup will exit");
+ return false;
+ }
+
+ final ConcurrentMap groupTable = this.getSubscriptionGroupTable();
+ final ConcurrentMap> forbiddenTable = this.getForbiddenTable();
+ final DataVersion dataVersion = super.getDataVersion();
+ final DataVersion kvDataVersion = this.getDataVersion();
+ if (dataVersion.getCounter().get() > kvDataVersion.getCounter().get()) {
+ for (Map.Entry entry : groupTable.entrySet()) {
+ putSubscriptionGroupConfig(entry.getValue());
+ log.info("import subscription config to rocksdb, group={}", entry.getValue());
+ }
+ for (Map.Entry> entry : forbiddenTable.entrySet()) {
+ try {
+ this.rocksDBConfigManager.updateForbidden(entry.getKey(), JSON.toJSONString(entry.getValue()));
+ log.info("import forbidden config to rocksdb, group={}", entry.getValue());
+ } catch (Exception e) {
+ log.error("import forbidden config to rocksdb failed, group={}", entry.getValue());
+ return false;
+ }
+ }
+ this.rocksDBConfigManager.getKvDataVersion().assignNewOne(dataVersion);
+ updateDataVersion();
+ }
+ log.info("finish marge subscription config from json file and merge to rocksdb");
+ this.persist();
+
+ return true;
+ }
+
@Override
public boolean stop() {
return this.rocksDBConfigManager.stop();
}
@Override
- protected SubscriptionGroupConfig putSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
+ public SubscriptionGroupConfig putSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
String groupName = subscriptionGroupConfig.getGroupName();
SubscriptionGroupConfig oldConfig = this.subscriptionGroupTable.put(groupName, subscriptionGroupConfig);
@@ -89,8 +166,8 @@ protected SubscriptionGroupConfig removeSubscriptionGroupConfig(String groupName
return subscriptionGroupConfig;
}
- @Override
- protected void decode0(byte[] key, byte[] body) {
+
+ protected void decodeSubscriptionGroup(byte[] key, byte[] body) {
String groupName = new String(key, DataConverter.CHARSET_UTF8);
SubscriptionGroupConfig subscriptionGroupConfig = JSON.parseObject(body, SubscriptionGroupConfig.class);
@@ -105,8 +182,63 @@ public synchronized void persist() {
}
}
- @Override
- public String configFilePath() {
+ public String rocksdbConfigFilePath() {
return this.brokerController.getMessageStoreConfig().getStorePathRootDir() + File.separator + "config" + File.separator + "subscriptionGroups" + File.separator;
}
+
+ @Override
+ public DataVersion getDataVersion() {
+ return rocksDBConfigManager.getKvDataVersion();
+ }
+
+ @Override
+ public void updateDataVersion() {
+ try {
+ rocksDBConfigManager.updateKvDataVersion();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected void decodeForbidden(byte[] key, byte[] body) {
+ String forbiddenGroupName = new String(key, DataConverter.CHARSET_UTF8);
+ JSONObject jsonObject = JSON.parseObject(new String(body, DataConverter.CHARSET_UTF8));
+ Set> entries = jsonObject.entrySet();
+ ConcurrentMap forbiddenGroup = new ConcurrentHashMap<>(entries.size());
+ for (Map.Entry entry : entries) {
+ forbiddenGroup.put(entry.getKey(), (Integer) entry.getValue());
+ }
+ this.getForbiddenTable().put(forbiddenGroupName, forbiddenGroup);
+ log.info("load forbidden,{} value {}", forbiddenGroupName, forbiddenGroup.toString());
+ }
+
+ @Override
+ public void updateForbidden(String group, String topic, int forbiddenIndex, boolean setOrClear) {
+ try {
+ super.updateForbidden(group, topic, forbiddenIndex, setOrClear);
+ this.rocksDBConfigManager.updateForbidden(group, JSON.toJSONString(this.getForbiddenTable().get(group)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void setForbidden(String group, String topic, int forbiddenIndex) {
+ try {
+ super.setForbidden(group, topic, forbiddenIndex);
+ this.rocksDBConfigManager.updateForbidden(group, JSON.toJSONString(this.getForbiddenTable().get(group)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void clearForbidden(String group, String topic, int forbiddenIndex) {
+ try {
+ super.clearForbidden(group, topic, forbiddenIndex);
+ this.rocksDBConfigManager.updateForbidden(group, JSON.toJSONString(this.getForbiddenTable().get(group)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManager.java b/broker/src/main/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManager.java
index e63b9305868..f2a7e0482b1 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManager.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -121,7 +122,7 @@ protected void init() {
}
}
- protected SubscriptionGroupConfig putSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
+ public SubscriptionGroupConfig putSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
return this.subscriptionGroupTable.put(subscriptionGroupConfig.getGroupName(), subscriptionGroupConfig);
}
@@ -138,6 +139,11 @@ protected SubscriptionGroupConfig removeSubscriptionGroupConfig(String groupName
}
public void updateSubscriptionGroupConfig(final SubscriptionGroupConfig config) {
+ updateSubscriptionGroupConfigWithoutPersist(config);
+ this.persist();
+ }
+
+ private void updateSubscriptionGroupConfigWithoutPersist(SubscriptionGroupConfig config) {
Map newAttributes = request(config);
Map currentAttributes = current(config.getGroupName());
@@ -156,9 +162,14 @@ public void updateSubscriptionGroupConfig(final SubscriptionGroupConfig config)
log.info("create new subscription group, {}", config);
}
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
+ }
+ public void updateSubscriptionGroupConfigList(List configList) {
+ if (null == configList || configList.isEmpty()) {
+ return;
+ }
+ configList.forEach(this::updateSubscriptionGroupConfigWithoutPersist);
this.persist();
}
@@ -214,7 +225,7 @@ public int getForbidden(String group, String topic) {
return topicForbidden;
}
- private void updateForbiddenValue(String group, String topic, Integer forbidden) {
+ protected void updateForbiddenValue(String group, String topic, Integer forbidden) {
if (forbidden == null || forbidden <= 0) {
this.forbiddenTable.remove(group);
log.info("clear group forbidden, {}@{} ", group, topic);
@@ -233,8 +244,7 @@ private void updateForbiddenValue(String group, String topic, Integer forbidden)
log.info("set group forbidden, {}@{} old: {} new: {}", group, topic, 0, forbidden);
}
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
}
@@ -243,8 +253,7 @@ public void disableConsume(final String groupName) {
SubscriptionGroupConfig old = getSubscriptionGroupConfig(groupName);
if (old != null) {
old.setConsumeEnable(false);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
}
}
@@ -261,8 +270,7 @@ public SubscriptionGroupConfig findSubscriptionGroupConfig(final String group) {
if (null == preConfig) {
log.info("auto create a subscription group, {}", subscriptionGroupConfig.toString());
}
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
}
}
@@ -331,8 +339,7 @@ public void deleteSubscriptionGroupConfig(final String groupName) {
this.forbiddenTable.remove(groupName);
if (old != null) {
log.info("delete subscription group OK, subscription group:{}", old);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
} else {
log.warn("delete subscription group failed, subscription groupName: {} not exist", groupName);
@@ -369,4 +376,14 @@ private Map current(String groupName) {
}
}
}
+
+ public void setDataVersion(DataVersion dataVersion) {
+ this.dataVersion.assignNewOne(dataVersion);
+ }
+
+ public void updateDataVersion() {
+ long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
+ dataVersion.nextVersion(stateMachineVersion);
+ }
+
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/topic/RocksDBTopicConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/topic/RocksDBTopicConfigManager.java
index fddecf2d92a..2a89dd7e024 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/topic/RocksDBTopicConfigManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/topic/RocksDBTopicConfigManager.java
@@ -16,39 +16,86 @@
*/
package org.apache.rocketmq.broker.topic;
-import java.io.File;
-
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.RocksDBConfigManager;
import org.apache.rocketmq.common.TopicConfig;
-import org.apache.rocketmq.common.config.RocksDBConfigManager;
+import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.utils.DataConverter;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.serializer.SerializerFeature;
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
public class RocksDBTopicConfigManager extends TopicConfigManager {
+ protected RocksDBConfigManager rocksDBConfigManager;
+
public RocksDBTopicConfigManager(BrokerController brokerController) {
super(brokerController, false);
- this.rocksDBConfigManager = new RocksDBConfigManager(brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
+ this.rocksDBConfigManager = new RocksDBConfigManager(rocksdbConfigFilePath(), brokerController.getMessageStoreConfig().getMemTableFlushIntervalMs());
}
@Override
public boolean load() {
- if (!this.rocksDBConfigManager.load(configFilePath(), this::decode0)) {
+ if (!rocksDBConfigManager.init()) {
+ return false;
+ }
+ if (!loadDataVersion() || !loadTopicConfig()) {
return false;
}
this.init();
return true;
}
+ public boolean loadTopicConfig() {
+ return this.rocksDBConfigManager.loadData(this::decodeTopicConfig) && merge();
+ }
+
+ public boolean loadDataVersion() {
+ return this.rocksDBConfigManager.loadDataVersion();
+ }
+
+ private boolean merge() {
+ if (!brokerController.getMessageStoreConfig().isTransferMetadataJsonToRocksdb()) {
+ log.info("The switch is off, no merge operation is needed.");
+ return true;
+ }
+ if (!UtilAll.isPathExists(this.configFilePath()) && !UtilAll.isPathExists(this.configFilePath() + ".bak")) {
+ log.info("json file and json back file not exist, so skip merge");
+ return true;
+ }
+
+ if (!super.load()) {
+ log.error("load topic config from json file error, startup will exit");
+ return false;
+ }
+
+ final ConcurrentMap topicConfigTable = this.getTopicConfigTable();
+ final DataVersion dataVersion = super.getDataVersion();
+ final DataVersion kvDataVersion = this.getDataVersion();
+ if (dataVersion.getCounter().get() > kvDataVersion.getCounter().get()) {
+ for (Map.Entry entry : topicConfigTable.entrySet()) {
+ putTopicConfig(entry.getValue());
+ log.info("import topic config to rocksdb, topic={}", entry.getValue());
+ }
+ this.rocksDBConfigManager.getKvDataVersion().assignNewOne(dataVersion);
+ updateDataVersion();
+ }
+ log.info("finish read topic config from json file and merge to rocksdb");
+ this.persist();
+ return true;
+ }
+
+
@Override
public boolean stop() {
return this.rocksDBConfigManager.stop();
}
- @Override
- protected void decode0(byte[] key, byte[] body) {
+ protected void decodeTopicConfig(byte[] key, byte[] body) {
String topicName = new String(key, DataConverter.CHARSET_UTF8);
TopicConfig topicConfig = JSON.parseObject(body, TopicConfig.class);
@@ -57,12 +104,7 @@ protected void decode0(byte[] key, byte[] body) {
}
@Override
- public String configFilePath() {
- return this.brokerController.getMessageStoreConfig().getStorePathRootDir() + File.separator + "config" + File.separator + "topics" + File.separator;
- }
-
- @Override
- protected TopicConfig putTopicConfig(TopicConfig topicConfig) {
+ public TopicConfig putTopicConfig(TopicConfig topicConfig) {
String topicName = topicConfig.getTopicName();
TopicConfig oldTopicConfig = this.topicConfigTable.put(topicName, topicConfig);
try {
@@ -92,4 +134,23 @@ public synchronized void persist() {
this.rocksDBConfigManager.flushWAL();
}
}
+
+ public String rocksdbConfigFilePath() {
+ return this.brokerController.getMessageStoreConfig().getStorePathRootDir() + File.separator + "config" + File.separator + "topics" + File.separator;
+ }
+
+
+ @Override
+ public DataVersion getDataVersion() {
+ return rocksDBConfigManager.getKvDataVersion();
+ }
+
+ @Override
+ public void updateDataVersion() {
+ try {
+ rocksDBConfigManager.updateKvDataVersion();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java
index d7c06180e9e..eab2896b001 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java
@@ -52,6 +52,7 @@
import org.apache.rocketmq.remoting.protocol.body.TopicConfigAndMappingSerializeWrapper;
import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingInfo;
+import org.apache.rocketmq.store.timer.TimerMessageStore;
import org.apache.rocketmq.tieredstore.TieredMessageStore;
import org.apache.rocketmq.tieredstore.metadata.MetadataStore;
import org.apache.rocketmq.tieredstore.metadata.entity.TopicMetadata;
@@ -211,9 +212,20 @@ protected void init() {
topicConfig.setWriteQueueNums(1);
putTopicConfig(topicConfig);
}
+
+ {
+ if (this.brokerController.getMessageStoreConfig().isTimerWheelEnable()) {
+ String topic = TimerMessageStore.TIMER_TOPIC;
+ TopicConfig topicConfig = new TopicConfig(topic);
+ TopicValidator.addSystemTopic(topic);
+ topicConfig.setReadQueueNums(1);
+ topicConfig.setWriteQueueNums(1);
+ this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
+ }
+ }
}
- protected TopicConfig putTopicConfig(TopicConfig topicConfig) {
+ public TopicConfig putTopicConfig(TopicConfig topicConfig) {
return this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
}
@@ -281,8 +293,7 @@ public TopicConfig createTopicInSendMessageMethod(final String topic, final Stri
putTopicConfig(topicConfig);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
createNew = true;
@@ -325,8 +336,7 @@ public TopicConfig createTopicIfAbsent(TopicConfig topicConfig, boolean register
}
log.info("Create new topic [{}] config:[{}]", topicConfig.getTopicName(), topicConfig);
putTopicConfig(topicConfig);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
createNew = true;
this.persist();
} finally {
@@ -385,8 +395,7 @@ public TopicConfig createTopicInSendMessageBackMethod(
log.info("create new topic {}", topicConfig);
putTopicConfig(topicConfig);
createNew = true;
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
} finally {
this.topicConfigTableLock.unlock();
@@ -426,8 +435,7 @@ public TopicConfig createTopicOfTranCheckMaxTime(final int clientDefaultTopicQue
log.info("create new topic {}", topicConfig);
putTopicConfig(topicConfig);
createNew = true;
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
} finally {
this.topicConfigTableLock.unlock();
@@ -460,8 +468,7 @@ public void updateTopicUnitFlag(final String topic, final boolean unit) {
putTopicConfig(topicConfig);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
registerBrokerData(topicConfig);
@@ -483,8 +490,7 @@ public void updateTopicUnitSubFlag(final String topic, final boolean hasUnitSub)
putTopicConfig(topicConfig);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
registerBrokerData(topicConfig);
@@ -497,7 +503,6 @@ private void updateSingleTopicConfigWithoutPersist(final TopicConfig topicConfig
Map newAttributes = request(topicConfig);
Map currentAttributes = current(topicConfig.getTopicName());
-
Map finalAttributes = AttributeUtil.alterCurrentAttributes(
this.topicConfigTable.get(topicConfig.getTopicName()) == null,
TopicAttributes.ALL,
@@ -514,8 +519,7 @@ private void updateSingleTopicConfigWithoutPersist(final TopicConfig topicConfig
log.info("create new topic [{}]", topicConfig);
}
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
}
public void updateTopicConfig(final TopicConfig topicConfig) {
@@ -569,25 +573,8 @@ public void updateOrderTopicConfig(final KVTable orderKVTableFromNs) {
}
}
- // We don't have a mandatory rule to maintain the validity of order conf in NameServer,
- // so we may overwrite the order field mistakenly.
- // To avoid the above case, we comment the below codes, please use mqadmin API to update
- // the order filed.
- /*for (Map.Entry entry : this.topicConfigTable.entrySet()) {
- String topic = entry.getKey();
- if (!orderTopics.contains(topic)) {
- TopicConfig topicConfig = entry.getValue();
- if (topicConfig.isOrder()) {
- topicConfig.setOrder(false);
- isChange = true;
- log.info("update order topic config, topic={}, order={}", topic, false);
- }
- }
- }*/
-
if (isChange) {
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
}
}
@@ -611,8 +598,7 @@ public void deleteTopicConfig(final String topic) {
TopicConfig old = removeTopicConfig(topic);
if (old != null) {
log.info("delete topic config OK, topic: {}", old);
- long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
- dataVersion.nextVersion(stateMachineVersion);
+ updateDataVersion();
this.persist();
} else {
log.warn("delete topic config failed, topic: {} not exists", topic);
@@ -727,5 +713,11 @@ public boolean containsTopic(String topic) {
return topicConfigTable.containsKey(topic);
}
+ public void updateDataVersion() {
+ long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
+ dataVersion.nextVersion(stateMachineVersion);
+ }
+
+
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java b/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java
index 8f89c14ae95..1d12acd4a98 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java
@@ -20,29 +20,43 @@
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import io.netty.channel.DefaultChannelPromise;
+import io.netty.util.concurrent.DefaultEventExecutor;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
+import org.apache.rocketmq.client.consumer.PullResult;
+import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.BrokerIdentity;
import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.remoting.common.SemaphoreReleaseOnlyOnce;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyRemotingClient;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.netty.ResponseFuture;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
+import org.apache.rocketmq.remoting.protocol.header.PullMessageResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.namesrv.QueryDataVersionResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.namesrv.RegisterBrokerResponseHeader;
import org.apache.rocketmq.remoting.protocol.namesrv.RegisterBrokerResult;
@@ -56,9 +70,12 @@
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Spy;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
-import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -69,9 +86,11 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.AdditionalMatchers.or;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(NettyRemotingClient.class)
public class BrokerOuterAPITest {
@Mock
private ChannelHandlerContext handlerContext;
@@ -117,6 +136,9 @@ public void test_needRegister_normal() throws Exception {
@Test
public void test_needRegister_timeout() throws Exception {
+ if (MixAll.isMac()) {
+ return;
+ }
init();
brokerOuterAPI.start();
@@ -251,4 +273,154 @@ public void testLookupAddressByDomain() throws Exception {
});
Assert.assertTrue(result.get());
}
+
+ @Test
+ public void testPullMessageFromSpecificBrokerAsync_createChannel_null() throws Exception {
+ NettyRemotingClient mockClient = PowerMockito.spy(new NettyRemotingClient(new NettyClientConfig()));
+ PowerMockito.when(mockClient, "getAndCreateChannelAsync", any()).thenReturn(null);
+ BrokerOuterAPI api = new BrokerOuterAPI(new NettyClientConfig(), new AuthConfig());
+ Field field = BrokerOuterAPI.class.getDeclaredField("remotingClient");
+ field.setAccessible(true);
+ field.set(api, mockClient);
+
+ Triple rst = api.pullMessageFromSpecificBrokerAsync("", "", "", "", 1, 1, 1, 3000L).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertTrue(rst.getMiddle().contains("connect"));
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ @Test
+ public void testPullMessageFromSpecificBrokerAsync_createChannel_future_notSuccess() throws Exception {
+ NettyRemotingClient mockClient = PowerMockito.spy(new NettyRemotingClient(new NettyClientConfig()));
+ DefaultChannelPromise promise = PowerMockito.spy(new DefaultChannelPromise(PowerMockito.mock(Channel.class), new DefaultEventExecutor()));
+ PowerMockito.when(mockClient, "getAndCreateChannelAsync", any()).thenReturn(promise);
+ BrokerOuterAPI api = new BrokerOuterAPI(new NettyClientConfig(), new AuthConfig());
+ Field field = BrokerOuterAPI.class.getDeclaredField("remotingClient");
+ field.setAccessible(true);
+ field.set(api, mockClient);
+
+ promise.tryFailure(new Throwable());
+ Triple rst
+ = api.pullMessageFromSpecificBrokerAsync("", "", "", "", 1, 1, 1, 3000L).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertTrue(rst.getMiddle().contains("connect"));
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ // skip other future status test
+
+ @Test
+ public void testPullMessageFromSpecificBrokerAsync_timeout() throws Exception {
+ Channel channel = Mockito.mock(Channel.class);
+ when(channel.isActive()).thenReturn(true);
+ NettyRemotingClient mockClient = PowerMockito.spy(new NettyRemotingClient(new NettyClientConfig()));
+ DefaultChannelPromise promise = PowerMockito.spy(new DefaultChannelPromise(PowerMockito.mock(Channel.class), new DefaultEventExecutor()));
+ PowerMockito.when(mockClient, "getAndCreateChannelAsync", any()).thenReturn(promise);
+ when(promise.channel()).thenReturn(channel);
+ BrokerOuterAPI api = new BrokerOuterAPI(new NettyClientConfig(), new AuthConfig());
+ Field field = BrokerOuterAPI.class.getDeclaredField("remotingClient");
+ field.setAccessible(true);
+ field.set(api, mockClient);
+
+ CompletableFuture future = new CompletableFuture<>();
+ doReturn(future).when(mockClient).invokeImpl(any(Channel.class), any(RemotingCommand.class), anyLong());
+ promise.trySuccess(null);
+ future.completeExceptionally(new RemotingTimeoutException("wait response on the channel timeout"));
+ Triple rst = api.pullMessageFromSpecificBrokerAsync("", "", "", "", 1, 1, 1, 3000L).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertTrue(rst.getMiddle().contains("timeout"));
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ @Test
+ public void testPullMessageFromSpecificBrokerAsync_brokerReturn_pullStatusCode() throws Exception {
+ Channel channel = Mockito.mock(Channel.class);
+ when(channel.isActive()).thenReturn(true);
+ NettyRemotingClient mockClient = PowerMockito.spy(new NettyRemotingClient(new NettyClientConfig()));
+ DefaultChannelPromise promise = PowerMockito.spy(new DefaultChannelPromise(PowerMockito.mock(Channel.class), new DefaultEventExecutor()));
+ PowerMockito.when(mockClient, "getAndCreateChannelAsync", any()).thenReturn(promise);
+ when(promise.channel()).thenReturn(channel);
+ BrokerOuterAPI api = new BrokerOuterAPI(new NettyClientConfig(), new AuthConfig());
+ Field field = BrokerOuterAPI.class.getDeclaredField("remotingClient");
+ field.setAccessible(true);
+ field.set(api, mockClient);
+
+ int[] respCodes = new int[] {ResponseCode.SUCCESS, ResponseCode.PULL_NOT_FOUND, ResponseCode.PULL_RETRY_IMMEDIATELY, ResponseCode.PULL_OFFSET_MOVED};
+ PullStatus[] respStatus = new PullStatus[] {PullStatus.FOUND, PullStatus.NO_NEW_MSG, PullStatus.NO_MATCHED_MSG, PullStatus.OFFSET_ILLEGAL};
+ for (int i = 0; i < respCodes.length; i++) {
+ CompletableFuture future = new CompletableFuture<>();
+ doReturn(future).when(mockClient).invokeImpl(any(Channel.class), any(RemotingCommand.class), anyLong());
+ RemotingCommand response = mockPullMessageResponse(respCodes[i]);
+ ResponseFuture responseFuture = new ResponseFuture(channel, 0, null, 1000,
+ resp -> { }, new SemaphoreReleaseOnlyOnce(new Semaphore(1)));
+ responseFuture.setResponseCommand(response);
+ promise.trySuccess(null);
+ future.complete(responseFuture);
+
+ Triple rst = api.pullMessageFromSpecificBrokerAsync("", "", "", "", 1, 1, 1, 3000L).join();
+ Assert.assertEquals(respStatus[i], rst.getLeft().getPullStatus());
+ if (ResponseCode.SUCCESS == respCodes[i]) {
+ Assert.assertEquals(1, rst.getLeft().getMsgFoundList().size());
+ } else {
+ Assert.assertNull(rst.getLeft().getMsgFoundList());
+ }
+ Assert.assertEquals(respStatus[i].name(), rst.getMiddle());
+ Assert.assertFalse(rst.getRight()); // no retry
+ }
+ }
+
+ @Test
+ public void testPullMessageFromSpecificBrokerAsync_brokerReturn_allOtherResponseCode() throws Exception {
+ Channel channel = Mockito.mock(Channel.class);
+ when(channel.isActive()).thenReturn(true);
+ NettyRemotingClient mockClient = PowerMockito.spy(new NettyRemotingClient(new NettyClientConfig()));
+ DefaultChannelPromise promise = PowerMockito.spy(new DefaultChannelPromise(PowerMockito.mock(Channel.class), new DefaultEventExecutor()));
+ PowerMockito.when(mockClient, "getAndCreateChannelAsync", any()).thenReturn(promise);
+ when(promise.channel()).thenReturn(channel);
+ BrokerOuterAPI api = new BrokerOuterAPI(new NettyClientConfig(), new AuthConfig());
+ Field field = BrokerOuterAPI.class.getDeclaredField("remotingClient");
+ field.setAccessible(true);
+ field.set(api, mockClient);
+
+ CompletableFuture future = new CompletableFuture<>();
+ doReturn(future).when(mockClient).invokeImpl(any(Channel.class), any(RemotingCommand.class), anyLong());
+ // test one code here, skip others
+ RemotingCommand response = mockPullMessageResponse(ResponseCode.SUBSCRIPTION_NOT_EXIST);
+ ResponseFuture responseFuture = new ResponseFuture(channel, 0, null, 1000,
+ resp -> { }, new SemaphoreReleaseOnlyOnce(new Semaphore(1)));
+ responseFuture.setResponseCommand(response);
+ promise.trySuccess(null);
+ future.complete(responseFuture);
+
+ Triple rst = api.pullMessageFromSpecificBrokerAsync("", "", "", "", 1, 1, 1, 3000L).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertTrue(rst.getMiddle().contains(ResponseCode.SUBSCRIPTION_NOT_EXIST + ""));
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ private RemotingCommand mockPullMessageResponse(int responseCode) throws Exception {
+ RemotingCommand response = RemotingCommand.createResponseCommand(PullMessageResponseHeader.class);
+ response.setCode(responseCode);
+ if (responseCode == ResponseCode.SUCCESS) {
+ MessageExt msg = new MessageExt();
+ msg.setBody("HW".getBytes());
+ msg.setTopic("topic");
+ msg.setBornHost(new InetSocketAddress("127.0.0.1", 9000));
+ msg.setStoreHost(new InetSocketAddress("127.0.0.1", 9000));
+ byte[] encode = MessageDecoder.encode(msg, false);
+ response.setBody(encode);
+ }
+ PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
+ responseHeader.setNextBeginOffset(0L);
+ responseHeader.setMaxOffset(0L);
+ responseHeader.setMinOffset(0L);
+ responseHeader.setOffsetDelta(0L);
+ responseHeader.setTopicSysFlag(0);
+ responseHeader.setGroupSysFlag(0);
+ responseHeader.setSuggestWhichBrokerId(0L);
+ responseHeader.setForbiddenType(0);
+ response.makeCustomHeaderToNet();
+ return response;
+ }
+
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java
index 8c909824348..a23ad20037c 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java
@@ -18,10 +18,7 @@
package org.apache.rocketmq.broker.client;
import io.netty.channel.Channel;
-import java.util.HashSet;
-import java.util.Set;
import org.apache.rocketmq.broker.BrokerController;
-import org.apache.rocketmq.broker.client.net.Broker2Client;
import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
@@ -30,13 +27,25 @@
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
-import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -49,19 +58,10 @@ public class ConsumerManagerTest {
private ConsumerManager consumerManager;
- private DefaultConsumerIdsChangeListener defaultConsumerIdsChangeListener;
-
@Mock
private BrokerController brokerController;
- @Mock
- private ConsumerFilterManager consumerFilterManager;
-
- private BrokerConfig brokerConfig = new BrokerConfig();
-
- private Broker2Client broker2Client;
-
- private BrokerStatsManager brokerStatsManager;
+ private final BrokerConfig brokerConfig = new BrokerConfig();
private static final String GROUP = "DEFAULT_GROUP";
@@ -74,40 +74,38 @@ public class ConsumerManagerTest {
@Before
public void before() {
clientChannelInfo = new ClientChannelInfo(channel, CLIENT_ID, LanguageCode.JAVA, VERSION);
- defaultConsumerIdsChangeListener = new DefaultConsumerIdsChangeListener(brokerController);
- brokerStatsManager = new BrokerStatsManager(brokerConfig);
- consumerManager = new ConsumerManager(defaultConsumerIdsChangeListener, brokerStatsManager, brokerConfig);
- broker2Client = new Broker2Client(brokerController);
+ DefaultConsumerIdsChangeListener defaultConsumerIdsChangeListener = new DefaultConsumerIdsChangeListener(brokerController);
+ BrokerStatsManager brokerStatsManager = new BrokerStatsManager(brokerConfig);
+ consumerManager = spy(new ConsumerManager(defaultConsumerIdsChangeListener, brokerStatsManager, brokerConfig));
+ ConsumerFilterManager consumerFilterManager = mock(ConsumerFilterManager.class);
when(brokerController.getConsumerFilterManager()).thenReturn(consumerFilterManager);
- when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
- when(brokerController.getBroker2Client()).thenReturn(broker2Client);
}
@Test
public void compensateBasicConsumerInfoTest() {
ConsumerGroupInfo consumerGroupInfo = consumerManager.getConsumerGroupInfo(GROUP, true);
- Assertions.assertThat(consumerGroupInfo).isNull();
+ assertThat(consumerGroupInfo).isNull();
consumerManager.compensateBasicConsumerInfo(GROUP, ConsumeType.CONSUME_ACTIVELY, MessageModel.BROADCASTING);
consumerGroupInfo = consumerManager.getConsumerGroupInfo(GROUP, true);
- Assertions.assertThat(consumerGroupInfo).isNotNull();
- Assertions.assertThat(consumerGroupInfo.getConsumeType()).isEqualTo(ConsumeType.CONSUME_ACTIVELY);
- Assertions.assertThat(consumerGroupInfo.getMessageModel()).isEqualTo(MessageModel.BROADCASTING);
+ assertThat(consumerGroupInfo).isNotNull();
+ assertThat(consumerGroupInfo.getConsumeType()).isEqualTo(ConsumeType.CONSUME_ACTIVELY);
+ assertThat(consumerGroupInfo.getMessageModel()).isEqualTo(MessageModel.BROADCASTING);
}
@Test
public void compensateSubscribeDataTest() {
ConsumerGroupInfo consumerGroupInfo = consumerManager.getConsumerGroupInfo(GROUP, true);
- Assertions.assertThat(consumerGroupInfo).isNull();
+ assertThat(consumerGroupInfo).isNull();
consumerManager.compensateSubscribeData(GROUP, TOPIC, new SubscriptionData(TOPIC, SubscriptionData.SUB_ALL));
consumerGroupInfo = consumerManager.getConsumerGroupInfo(GROUP, true);
- Assertions.assertThat(consumerGroupInfo).isNotNull();
- Assertions.assertThat(consumerGroupInfo.getSubscriptionTable().size()).isEqualTo(1);
+ assertThat(consumerGroupInfo).isNotNull();
+ assertThat(consumerGroupInfo.getSubscriptionTable().size()).isEqualTo(1);
SubscriptionData subscriptionData = consumerGroupInfo.getSubscriptionTable().get(TOPIC);
- Assertions.assertThat(subscriptionData).isNotNull();
- Assertions.assertThat(subscriptionData.getTopic()).isEqualTo(TOPIC);
- Assertions.assertThat(subscriptionData.getSubString()).isEqualTo(SubscriptionData.SUB_ALL);
+ assertThat(subscriptionData).isNotNull();
+ assertThat(subscriptionData.getTopic()).isEqualTo(TOPIC);
+ assertThat(subscriptionData.getSubString()).isEqualTo(SubscriptionData.SUB_ALL);
}
@Test
@@ -118,7 +116,8 @@ public void registerConsumerTest() {
subList.add(subscriptionData);
consumerManager.registerConsumer(GROUP, clientChannelInfo, ConsumeType.CONSUME_PASSIVELY,
MessageModel.BROADCASTING, ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET, subList, true);
- Assertions.assertThat(consumerManager.getConsumerTable().get(GROUP)).isNotNull();
+ verify(consumerManager, never()).callConsumerIdsChangeListener(eq(ConsumerGroupEvent.CHANGE), any(), any());
+ assertThat(consumerManager.getConsumerTable().get(GROUP)).isNotNull();
}
@Test
@@ -128,63 +127,65 @@ public void unregisterConsumerTest() {
// unregister
consumerManager.unregisterConsumer(GROUP, clientChannelInfo, true);
- Assertions.assertThat(consumerManager.getConsumerTable().get(GROUP)).isNull();
+ verify(consumerManager, never()).callConsumerIdsChangeListener(eq(ConsumerGroupEvent.CHANGE), any(), any());
+ assertThat(consumerManager.getConsumerTable().get(GROUP)).isNull();
}
@Test
public void findChannelTest() {
register();
final ClientChannelInfo consumerManagerChannel = consumerManager.findChannel(GROUP, CLIENT_ID);
- Assertions.assertThat(consumerManagerChannel).isNotNull();
+ assertThat(consumerManagerChannel).isNotNull();
}
@Test
public void findSubscriptionDataTest() {
register();
final SubscriptionData subscriptionData = consumerManager.findSubscriptionData(GROUP, TOPIC);
- Assertions.assertThat(subscriptionData).isNotNull();
+ assertThat(subscriptionData).isNotNull();
}
@Test
public void findSubscriptionDataCountTest() {
register();
final int count = consumerManager.findSubscriptionDataCount(GROUP);
- assert count > 0;
+ assertTrue(count > 0);
}
@Test
public void findSubscriptionTest() {
SubscriptionData subscriptionData = consumerManager.findSubscriptionData(GROUP, TOPIC, true);
- Assertions.assertThat(subscriptionData).isNull();
+ assertThat(subscriptionData).isNull();
consumerManager.compensateSubscribeData(GROUP, TOPIC, new SubscriptionData(TOPIC, SubscriptionData.SUB_ALL));
subscriptionData = consumerManager.findSubscriptionData(GROUP, TOPIC, true);
- Assertions.assertThat(subscriptionData).isNotNull();
- Assertions.assertThat(subscriptionData.getTopic()).isEqualTo(TOPIC);
- Assertions.assertThat(subscriptionData.getSubString()).isEqualTo(SubscriptionData.SUB_ALL);
+ assertThat(subscriptionData).isNotNull();
+ assertThat(subscriptionData.getTopic()).isEqualTo(TOPIC);
+ assertThat(subscriptionData.getSubString()).isEqualTo(SubscriptionData.SUB_ALL);
subscriptionData = consumerManager.findSubscriptionData(GROUP, TOPIC, false);
- Assertions.assertThat(subscriptionData).isNull();
+ assertThat(subscriptionData).isNull();
}
@Test
public void scanNotActiveChannelTest() {
clientChannelInfo.setLastUpdateTimestamp(System.currentTimeMillis() - brokerConfig.getChannelExpiredTimeout() * 2);
consumerManager.scanNotActiveChannel();
- Assertions.assertThat(consumerManager.getConsumerTable().size()).isEqualTo(0);
+ assertThat(consumerManager.getConsumerTable().size()).isEqualTo(0);
}
@Test
public void queryTopicConsumeByWhoTest() {
register();
final HashSet consumeGroup = consumerManager.queryTopicConsumeByWho(TOPIC);
- assert consumeGroup.size() > 0;
+ assertFalse(consumeGroup.isEmpty());
}
@Test
public void doChannelCloseEventTest() {
consumerManager.doChannelCloseEvent("127.0.0.1", channel);
- assert consumerManager.getConsumerTable().size() == 0;
+ verify(consumerManager, never()).callConsumerIdsChangeListener(eq(ConsumerGroupEvent.CHANGE), any(), any());
+ assertEquals(0, consumerManager.getConsumerTable().size());
}
private void register() {
@@ -203,8 +204,8 @@ public void removeExpireConsumerGroupInfo() {
consumerManager.compensateSubscribeData(GROUP, TOPIC, subscriptionData);
consumerManager.compensateSubscribeData(GROUP, TOPIC + "_1", new SubscriptionData(TOPIC, SubscriptionData.SUB_ALL));
consumerManager.removeExpireConsumerGroupInfo();
- Assertions.assertThat(consumerManager.getConsumerGroupInfo(GROUP, true)).isNotNull();
- Assertions.assertThat(consumerManager.findSubscriptionData(GROUP, TOPIC)).isNull();
- Assertions.assertThat(consumerManager.findSubscriptionData(GROUP, TOPIC + "_1")).isNotNull();
+ assertThat(consumerManager.getConsumerGroupInfo(GROUP, true)).isNotNull();
+ assertThat(consumerManager.findSubscriptionData(GROUP, TOPIC)).isNull();
+ assertThat(consumerManager.findSubscriptionData(GROUP, TOPIC + "_1")).isNotNull();
}
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/client/net/Broker2ClientTest.java b/broker/src/test/java/org/apache/rocketmq/broker/client/net/Broker2ClientTest.java
new file mode 100644
index 00000000000..865e7b608ea
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/client/net/Broker2ClientTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.rocketmq.broker.client.net;
+
+import io.netty.channel.Channel;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.client.ClientChannelInfo;
+import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
+import org.apache.rocketmq.broker.client.ConsumerManager;
+import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
+import org.apache.rocketmq.broker.topic.TopicConfigManager;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MQVersion;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.remoting.RemotingServer;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.body.GetConsumerStatusBody;
+import org.apache.rocketmq.remoting.protocol.header.CheckTransactionStateRequestHeader;
+import org.apache.rocketmq.store.MessageStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class Broker2ClientTest {
+
+ @Mock
+ private BrokerController brokerController;
+
+ @Mock
+ private RemotingServer remotingServer;
+
+ @Mock
+ private ConsumerManager consumerManager;
+
+ @Mock
+ private TopicConfigManager topicConfigManager;
+
+ @Mock
+ private ConsumerOffsetManager consumerOffsetManager;
+
+ @Mock
+ private Channel channel;
+
+ @Mock
+ private ConsumerGroupInfo consumerGroupInfo;
+
+ private Broker2Client broker2Client;
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBroker = "defaultBroker";
+
+ private final String defaultGroup = "defaultGroup";
+
+ private final long timestamp = System.currentTimeMillis();
+
+ private final boolean isForce = true;
+
+ @Before
+ public void init() {
+ broker2Client = new Broker2Client(brokerController);
+ when(brokerController.getRemotingServer()).thenReturn(remotingServer);
+ when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager);
+ when(brokerController.getConsumerManager()).thenReturn(consumerManager);
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(brokerController.getBrokerConfig()).thenReturn(mock(BrokerConfig.class));
+ when(brokerController.getMessageStore()).thenReturn(mock(MessageStore.class));
+ when(consumerManager.getConsumerGroupInfo(any())).thenReturn(consumerGroupInfo);
+ }
+
+ @Test
+ public void testCheckProducerTransactionState() throws Exception {
+ CheckTransactionStateRequestHeader requestHeader = new CheckTransactionStateRequestHeader();
+ broker2Client.checkProducerTransactionState("group", channel, requestHeader, createMessageExt());
+ verify(remotingServer).invokeOneway(eq(channel), any(RemotingCommand.class), eq(10L));
+ }
+
+ @Test
+ public void testCheckProducerTransactionStateException() throws Exception {
+ CheckTransactionStateRequestHeader requestHeader = new CheckTransactionStateRequestHeader();
+ MessageExt messageExt = createMessageExt();
+ doThrow(new RuntimeException("Test Exception"))
+ .when(remotingServer)
+ .invokeOneway(any(Channel.class),
+ any(RemotingCommand.class),
+ anyLong());
+ broker2Client.checkProducerTransactionState("group", channel, requestHeader, messageExt);
+ verify(brokerController.getRemotingServer()).invokeOneway(eq(channel), any(RemotingCommand.class), eq(10L));
+ }
+
+ @Test
+ public void testResetOffsetNoTopicConfig() {
+ when(topicConfigManager.selectTopicConfig(defaultTopic)).thenReturn(null);
+ RemotingCommand response = broker2Client.resetOffset(defaultTopic, defaultGroup, timestamp, isForce);
+ assertEquals(ResponseCode.SYSTEM_ERROR, response.getCode());
+ }
+
+ @Test
+ public void testResetOffsetNoConsumerGroupInfo() {
+ TopicConfig topicConfig = mock(TopicConfig.class);
+ when(topicConfigManager.selectTopicConfig(defaultTopic)).thenReturn(topicConfig);
+ when(topicConfig.getWriteQueueNums()).thenReturn(1);
+ when(consumerOffsetManager.queryOffset(defaultGroup, defaultTopic, 0)).thenReturn(0L);
+ RemotingCommand response = broker2Client.resetOffset(defaultTopic, defaultGroup, timestamp, isForce);
+ assertEquals(ResponseCode.CONSUMER_NOT_ONLINE, response.getCode());
+ }
+
+ @Test
+ public void testResetOffset() {
+ TopicConfig topicConfig = mock(TopicConfig.class);
+ when(topicConfigManager.selectTopicConfig(defaultTopic)).thenReturn(topicConfig);
+ when(topicConfig.getWriteQueueNums()).thenReturn(1);
+ when(brokerController.getConsumerOffsetManager().queryOffset(defaultGroup, defaultTopic, 0)).thenReturn(0L);
+ BrokerConfig brokerConfig = mock(BrokerConfig.class);
+ when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
+ when(brokerConfig.getBrokerName()).thenReturn(defaultBroker);
+ ConsumerGroupInfo consumerGroupInfo = mock(ConsumerGroupInfo.class);
+ when(consumerManager.getConsumerGroupInfo(defaultGroup)).thenReturn(consumerGroupInfo);
+ RemotingCommand response = broker2Client.resetOffset(defaultTopic, defaultGroup, timestamp, isForce);
+ assertEquals(ResponseCode.CONSUMER_NOT_ONLINE, response.getCode());
+ }
+
+ @Test
+ public void testGetConsumeStatusNoConsumerOnline() {
+ when(consumerGroupInfo.getChannelInfoTable()).thenReturn(new ConcurrentHashMap<>());
+ RemotingCommand response = broker2Client.getConsumeStatus(defaultTopic, defaultGroup, "");
+ assertEquals(ResponseCode.SYSTEM_ERROR, response.getCode());
+ }
+
+ @Test
+ public void testGetConsumeStatusClientDoesNotSupportFeature() {
+ ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, "defaultClientId", null, MQVersion.Version.V3_0_6.ordinal());
+ ConcurrentMap channelInfoTable = new ConcurrentHashMap<>();
+ channelInfoTable.put(channel, clientChannelInfo);
+ when(consumerGroupInfo.getChannelInfoTable()).thenReturn(channelInfoTable);
+ RemotingCommand response = broker2Client.getConsumeStatus(defaultTopic, defaultGroup, "");
+ assertEquals(ResponseCode.SYSTEM_ERROR, response.getCode());
+ }
+
+ @Test
+ public void testGetConsumeStatus() throws Exception {
+ ConcurrentMap channelInfoTable = new ConcurrentHashMap<>();
+ ClientChannelInfo clientChannelInfo = mock(ClientChannelInfo.class);
+ when(clientChannelInfo.getVersion()).thenReturn(MQVersion.CURRENT_VERSION);
+ channelInfoTable.put(channel, clientChannelInfo);
+ when(consumerGroupInfo.getChannelInfoTable()).thenReturn(channelInfoTable);
+ RemotingCommand responseMock = mock(RemotingCommand.class);
+ when(responseMock.getCode()).thenReturn(ResponseCode.SUCCESS);
+ when(responseMock.getBody()).thenReturn("{\"consumerTable\":{}}".getBytes(StandardCharsets.UTF_8));
+ when(remotingServer.invokeSync(any(Channel.class), any(RemotingCommand.class), anyLong())).thenReturn(responseMock);
+ RemotingCommand response = broker2Client.getConsumeStatus(defaultTopic, defaultGroup, "");
+ assertEquals(ResponseCode.SUCCESS, response.getCode());
+ GetConsumerStatusBody body = RemotingSerializable.decode(response.getBody(), GetConsumerStatusBody.class);
+ assertEquals(1, body.getConsumerTable().size());
+ }
+
+ private MessageExt createMessageExt() {
+ MessageExt result = new MessageExt();
+ result.setBody("body".getBytes(StandardCharsets.UTF_8));
+ result.setTopic(defaultTopic);
+ result.setBrokerName(defaultBroker);
+ result.putUserProperty("key", "value");
+ result.getProperties().put(MessageConst.PROPERTY_PRODUCER_GROUP, defaultGroup);
+ result.getProperties().put(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, "TX1");
+ result.setKeys("keys");
+ SocketAddress bornHost = new InetSocketAddress("127.0.0.1", 12911);
+ SocketAddress storeHost = new InetSocketAddress("127.0.0.1", 10911);
+ result.setStoreHost(storeHost);
+ result.setBornHost(bornHost);
+ return result;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManagerTest.java
new file mode 100644
index 00000000000..e231d61b6a7
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManagerTest.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.rocketmq.broker.client.rebalance;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RebalanceLockManagerTest {
+
+ @Mock
+ private RebalanceLockManager.LockEntry lockEntry;
+
+ private final RebalanceLockManager rebalanceLockManager = new RebalanceLockManager();
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBroker = "defaultBroker";
+
+ private final String defaultGroup = "defaultGroup";
+
+ private final String defaultClientId = "defaultClientId";
+
+ @Test
+ public void testIsLockAllExpiredGroupNotExist() {
+ assertTrue(rebalanceLockManager.isLockAllExpired(defaultGroup));
+ }
+
+ @Test
+ public void testIsLockAllExpiredGroupExist() throws IllegalAccessException {
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ when(lockEntry.isExpired()).thenReturn(false);
+ assertFalse(rebalanceLockManager.isLockAllExpired(defaultGroup));
+ }
+
+ @Test
+ public void testIsLockAllExpiredGroupExistSomeExpired() throws IllegalAccessException {
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ when(lockEntry.isExpired()).thenReturn(true).thenReturn(false);
+ assertFalse(rebalanceLockManager.isLockAllExpired(defaultGroup));
+ }
+
+ @Test
+ public void testTryLockNotLocked() {
+ assertTrue(rebalanceLockManager.tryLock(defaultGroup, createDefaultMessageQueue(), defaultClientId));
+ }
+
+ @Test
+ public void testTryLockSameClient() throws IllegalAccessException {
+ when(lockEntry.isLocked(defaultClientId)).thenReturn(true);
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ assertTrue(rebalanceLockManager.tryLock(defaultGroup, createDefaultMessageQueue(), defaultClientId));
+ }
+
+ @Test
+ public void testTryLockDifferentClient() throws Exception {
+ when(lockEntry.isLocked(defaultClientId)).thenReturn(false);
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ assertFalse(rebalanceLockManager.tryLock(defaultGroup, createDefaultMessageQueue(), defaultClientId));
+ }
+
+ @Test
+ public void testTryLockButExpired() throws IllegalAccessException {
+ when(lockEntry.isExpired()).thenReturn(true);
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ assertTrue(rebalanceLockManager.tryLock(defaultGroup, createDefaultMessageQueue(), defaultClientId));
+ }
+
+ @Test
+ public void testTryLockBatchAllLocked() {
+ Set mqs = createMessageQueue(2);
+ Set actual = rebalanceLockManager.tryLockBatch(defaultGroup, mqs, defaultClientId);
+ assertEquals(mqs, actual);
+ }
+
+ @Test
+ public void testTryLockBatchNoneLocked() throws IllegalAccessException {
+ when(lockEntry.isLocked(defaultClientId)).thenReturn(false);
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ Set actual = rebalanceLockManager.tryLockBatch(defaultGroup, createMessageQueue(2), defaultClientId);
+ assertTrue(actual.isEmpty());
+ }
+
+ @Test
+ public void testTryLockBatchSomeLocked() throws IllegalAccessException {
+ Set mqs = new HashSet<>();
+ MessageQueue mq1 = new MessageQueue(defaultTopic, defaultBroker, 0);
+ MessageQueue mq2 = new MessageQueue(defaultTopic, defaultBroker, 1);
+ mqs.add(mq1);
+ mqs.add(mq2);
+ when(lockEntry.isLocked(defaultClientId)).thenReturn(true).thenReturn(false);
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", createMQLockTable(), true);
+ Set actual = rebalanceLockManager.tryLockBatch(defaultGroup, mqs, defaultClientId);
+ Set expected = new HashSet<>();
+ expected.add(mq2);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testUnlockBatch() throws IllegalAccessException {
+ when(lockEntry.getClientId()).thenReturn(defaultClientId);
+ ConcurrentMap> mqLockTable = createMQLockTable();
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", mqLockTable, true);
+ rebalanceLockManager.unlockBatch(defaultGroup, createMessageQueue(1), defaultClientId);
+ assertEquals(1, mqLockTable.get(defaultGroup).values().size());
+ }
+
+ @Test
+ public void testUnlockBatchByOtherClient() throws IllegalAccessException {
+ when(lockEntry.getClientId()).thenReturn("otherClientId");
+ ConcurrentMap> mqLockTable = createMQLockTable();
+ FieldUtils.writeDeclaredField(rebalanceLockManager, "mqLockTable", mqLockTable, true);
+ rebalanceLockManager.unlockBatch(defaultGroup, createMessageQueue(1), defaultClientId);
+ assertEquals(2, mqLockTable.get(defaultGroup).values().size());
+ }
+
+ private MessageQueue createDefaultMessageQueue() {
+ return createMessageQueue(1).iterator().next();
+ }
+
+ private Set createMessageQueue(final int count) {
+ Set result = new HashSet<>();
+ for (int i = 0; i < count; i++) {
+ result.add(new MessageQueue(defaultTopic, defaultBroker, i));
+ }
+ return result;
+ }
+
+ private ConcurrentMap> createMQLockTable() {
+ MessageQueue messageQueue1 = new MessageQueue(defaultTopic, defaultBroker, 0);
+ MessageQueue messageQueue2 = new MessageQueue(defaultTopic, defaultBroker, 1);
+ ConcurrentHashMap lockEntryMap = new ConcurrentHashMap<>();
+ lockEntryMap.put(messageQueue1, lockEntry);
+ lockEntryMap.put(messageQueue2, lockEntry);
+ ConcurrentMap> result = new ConcurrentHashMap<>();
+ result.put(defaultGroup, lockEntryMap);
+ return result;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/controller/ReplicasManagerRegisterTest.java b/broker/src/test/java/org/apache/rocketmq/broker/controller/ReplicasManagerRegisterTest.java
index d01a6f76f5e..39ec0d8d94f 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/controller/ReplicasManagerRegisterTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/controller/ReplicasManagerRegisterTest.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.broker.controller;
+import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.broker.slave.SlaveSynchronize;
@@ -36,29 +37,31 @@
import org.apache.rocketmq.store.ha.autoswitch.BrokerMetadata;
import org.apache.rocketmq.store.ha.autoswitch.TempBrokerMetadata;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.time.Duration;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.UUID;
import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(ReplicasManager.class)
+@RunWith(MockitoJUnitRunner.class)
public class ReplicasManagerRegisterTest {
public static final String STORE_BASE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "ReplicasManagerRegisterTest";
@@ -74,7 +77,14 @@ public class ReplicasManagerRegisterTest {
public static final String CONTROLLER_ADDR = "127.0.0.1:8888";
public static final BrokerConfig BROKER_CONFIG;
- private final HashSet syncStateSet = new HashSet<>(Arrays.asList(1L));
+
+ private final HashSet syncStateSet = new HashSet<>(Collections.singletonList(1L));
+
+ @Mock
+ private BrokerMetadata brokerMetadata;
+
+ @Mock
+ private TempBrokerMetadata tempBrokerMetadata;
static {
BROKER_CONFIG = new BrokerConfig();
@@ -133,18 +143,19 @@ public void testBrokerRegisterSuccess() throws Exception {
when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 1L));
when(mockedBrokerOuterAPI.applyBrokerId(any(), any(), anyLong(), any(), any())).thenReturn(new ApplyBrokerIdResponseHeader());
when(mockedBrokerOuterAPI.registerBrokerToController(any(), any(), anyLong(), any(), any())).thenReturn(new Pair<>(new RegisterBrokerToControllerResponseHeader(), syncStateSet));
- when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
+ when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong()))
+ .thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
ReplicasManager replicasManager0 = new ReplicasManager(mockedBrokerController);
replicasManager0.start();
await().atMost(Duration.ofMillis(1000)).until(() ->
replicasManager0.getState() == ReplicasManager.State.RUNNING
);
- Assert.assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager0.getRegisterState());
- Assert.assertEquals(1L, replicasManager0.getBrokerControllerId().longValue());
+ assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager0.getRegisterState());
+ assertEquals(1L, replicasManager0.getBrokerControllerId().longValue());
checkMetadataFile(replicasManager0.getBrokerMetadata(), 1L);
- Assert.assertFalse(replicasManager0.getTempBrokerMetadata().isLoaded());
- Assert.assertFalse(replicasManager0.getTempBrokerMetadata().fileExists());
+ assertFalse(replicasManager0.getTempBrokerMetadata().isLoaded());
+ assertFalse(replicasManager0.getTempBrokerMetadata().fileExists());
replicasManager0.shutdown();
}
@@ -160,18 +171,18 @@ public void testBrokerRegisterSuccessAndRestartWithChangedBrokerConfig() throws
await().atMost(Duration.ofMillis(1000)).until(() ->
replicasManager0.getState() == ReplicasManager.State.RUNNING
);
- Assert.assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager0.getRegisterState());
- Assert.assertEquals(1L, replicasManager0.getBrokerControllerId().longValue());
+ assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager0.getRegisterState());
+ assertEquals(1L, replicasManager0.getBrokerControllerId().longValue());
checkMetadataFile(replicasManager0.getBrokerMetadata(), 1L);
- Assert.assertFalse(replicasManager0.getTempBrokerMetadata().isLoaded());
- Assert.assertFalse(replicasManager0.getTempBrokerMetadata().fileExists());
+ assertFalse(replicasManager0.getTempBrokerMetadata().isLoaded());
+ assertFalse(replicasManager0.getTempBrokerMetadata().fileExists());
replicasManager0.shutdown();
// change broker name in broker config
mockedBrokerController.getBrokerConfig().setBrokerName(BROKER_NAME + "1");
ReplicasManager replicasManagerRestart = new ReplicasManager(mockedBrokerController);
replicasManagerRestart.start();
- Assert.assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManagerRestart.getRegisterState());
+ assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManagerRestart.getRegisterState());
mockedBrokerController.getBrokerConfig().setBrokerName(BROKER_NAME);
replicasManagerRestart.shutdown();
@@ -179,7 +190,7 @@ public void testBrokerRegisterSuccessAndRestartWithChangedBrokerConfig() throws
mockedBrokerController.getBrokerConfig().setBrokerClusterName(CLUSTER_NAME + "1");
replicasManagerRestart = new ReplicasManager(mockedBrokerController);
replicasManagerRestart.start();
- Assert.assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManagerRestart.getRegisterState());
+ assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManagerRestart.getRegisterState());
mockedBrokerController.getBrokerConfig().setBrokerClusterName(CLUSTER_NAME);
replicasManagerRestart.shutdown();
}
@@ -190,32 +201,29 @@ public void testRegisterFailedAtGetNextBrokerId() throws Exception {
when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenThrow(new RuntimeException());
replicasManager.start();
-
- Assert.assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.INITIAL, replicasManager.getRegisterState());
- Assert.assertFalse(replicasManager.getTempBrokerMetadata().fileExists());
- Assert.assertFalse(replicasManager.getBrokerMetadata().fileExists());
- Assert.assertNull(replicasManager.getBrokerControllerId());
+
+ assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
+ assertEquals(ReplicasManager.RegisterState.INITIAL, replicasManager.getRegisterState());
+ assertFalse(replicasManager.getTempBrokerMetadata().fileExists());
+ assertFalse(replicasManager.getBrokerMetadata().fileExists());
+ assertNull(replicasManager.getBrokerControllerId());
replicasManager.shutdown();
}
@Test
public void testRegisterFailedAtCreateTempFile() throws Exception {
- ReplicasManager replicasManager = new ReplicasManager(mockedBrokerController);
+ ReplicasManager spyReplicasManager = new ReplicasManager(mockedBrokerController);
when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 1L));
- when(mockedBrokerOuterAPI.applyBrokerId(any(), any(), anyLong(), any(), any())).thenReturn(new ApplyBrokerIdResponseHeader());
- when(mockedBrokerOuterAPI.registerBrokerToController(any(), any(), anyLong(), any(), any())).thenReturn(new Pair<>(new RegisterBrokerToControllerResponseHeader(), syncStateSet));
- when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
- ReplicasManager spyReplicasManager = PowerMockito.spy(replicasManager);
- PowerMockito.doReturn(false).when(spyReplicasManager, "createTempMetadataFile", anyLong());
+ FieldUtils.writeDeclaredField(spyReplicasManager, "tempBrokerMetadata", tempBrokerMetadata, true);
+ doThrow(new RuntimeException("Test exception")).when(tempBrokerMetadata).updateAndPersist(any(), any(), anyLong(), any());
spyReplicasManager.start();
-
- Assert.assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, spyReplicasManager.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.INITIAL, spyReplicasManager.getRegisterState());
- Assert.assertFalse(spyReplicasManager.getTempBrokerMetadata().fileExists());
- Assert.assertFalse(spyReplicasManager.getBrokerMetadata().fileExists());
- Assert.assertNull(spyReplicasManager.getBrokerControllerId());
+
+ assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, spyReplicasManager.getState());
+ assertEquals(ReplicasManager.RegisterState.INITIAL, spyReplicasManager.getRegisterState());
+ assertFalse(spyReplicasManager.getTempBrokerMetadata().fileExists());
+ assertFalse(spyReplicasManager.getBrokerMetadata().fileExists());
+ assertNull(spyReplicasManager.getBrokerControllerId());
spyReplicasManager.shutdown();
}
@@ -224,61 +232,57 @@ public void testRegisterFailedAtApplyBrokerIdFailed() throws Exception {
ReplicasManager replicasManager = new ReplicasManager(mockedBrokerController);
when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 1L));
when(mockedBrokerOuterAPI.applyBrokerId(any(), any(), anyLong(), any(), any())).thenThrow(new RuntimeException());
- when(mockedBrokerOuterAPI.registerBrokerToController(any(), any(), anyLong(), any(), any())).thenReturn(new Pair<>(new RegisterBrokerToControllerResponseHeader(), syncStateSet));
- when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
replicasManager.start();
-
- Assert.assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
- Assert.assertNotEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManager.getRegisterState());
- Assert.assertNotEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager.getRegisterState());
+
+ assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
+ assertNotEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManager.getRegisterState());
+ assertNotEquals(ReplicasManager.RegisterState.REGISTERED, replicasManager.getRegisterState());
replicasManager.shutdown();
-
- Assert.assertFalse(replicasManager.getBrokerMetadata().fileExists());
- Assert.assertNull(replicasManager.getBrokerControllerId());
+
+ assertFalse(replicasManager.getBrokerMetadata().fileExists());
+ assertNull(replicasManager.getBrokerControllerId());
}
@Test
public void testRegisterFailedAtCreateMetadataFileAndDeleteTemp() throws Exception {
- ReplicasManager replicasManager = new ReplicasManager(mockedBrokerController);
+ ReplicasManager spyReplicasManager = new ReplicasManager(mockedBrokerController);
when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 1L));
when(mockedBrokerOuterAPI.applyBrokerId(any(), any(), anyLong(), any(), any())).thenReturn(new ApplyBrokerIdResponseHeader());
when(mockedBrokerOuterAPI.registerBrokerToController(any(), any(), anyLong(), any(), any())).thenReturn(new Pair<>(new RegisterBrokerToControllerResponseHeader(), syncStateSet));
when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
-
- ReplicasManager spyReplicasManager = PowerMockito.spy(replicasManager);
- PowerMockito.doReturn(false).when(spyReplicasManager, "createMetadataFileAndDeleteTemp");
+
+ FieldUtils.writeDeclaredField(spyReplicasManager, "brokerMetadata", brokerMetadata, true);
+ doThrow(new RuntimeException("Test exception")).when(brokerMetadata).updateAndPersist(any(), any(), anyLong());
spyReplicasManager.start();
-
- Assert.assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, spyReplicasManager.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.CREATE_TEMP_METADATA_FILE_DONE, spyReplicasManager.getRegisterState());
+
+ assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, spyReplicasManager.getState());
+ assertEquals(ReplicasManager.RegisterState.CREATE_TEMP_METADATA_FILE_DONE, spyReplicasManager.getRegisterState());
TempBrokerMetadata tempBrokerMetadata = spyReplicasManager.getTempBrokerMetadata();
- Assert.assertTrue(tempBrokerMetadata.fileExists());
- Assert.assertTrue(tempBrokerMetadata.isLoaded());
- Assert.assertFalse(spyReplicasManager.getBrokerMetadata().fileExists());
- Assert.assertNull(spyReplicasManager.getBrokerControllerId());
+ assertTrue(tempBrokerMetadata.fileExists());
+ assertTrue(tempBrokerMetadata.isLoaded());
+ assertFalse(spyReplicasManager.getBrokerMetadata().fileExists());
+ assertNull(spyReplicasManager.getBrokerControllerId());
spyReplicasManager.shutdown();
// restart, we expect that this replicasManager still keep the tempMetadata and still try to finish its registering
ReplicasManager replicasManagerNew = new ReplicasManager(mockedBrokerController);
- // because apply brokerId: 1 has succeeded, so now next broker id is 2
- when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 2L));
replicasManagerNew.start();
-
- Assert.assertEquals(ReplicasManager.State.RUNNING, replicasManagerNew.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManagerNew.getRegisterState());
+
+ assertEquals(ReplicasManager.State.RUNNING, replicasManagerNew.getState());
+ assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManagerNew.getRegisterState());
// tempMetadata has been cleared
- Assert.assertFalse(replicasManagerNew.getTempBrokerMetadata().fileExists());
- Assert.assertFalse(replicasManagerNew.getTempBrokerMetadata().isLoaded());
+ assertFalse(replicasManagerNew.getTempBrokerMetadata().fileExists());
+ assertFalse(replicasManagerNew.getTempBrokerMetadata().isLoaded());
// metadata has been persisted
- Assert.assertTrue(replicasManagerNew.getBrokerMetadata().fileExists());
- Assert.assertTrue(replicasManagerNew.getBrokerMetadata().isLoaded());
- Assert.assertEquals(1L, replicasManagerNew.getBrokerMetadata().getBrokerId().longValue());
- Assert.assertEquals(1L, replicasManagerNew.getBrokerControllerId().longValue());
+ assertTrue(replicasManagerNew.getBrokerMetadata().fileExists());
+ assertTrue(replicasManagerNew.getBrokerMetadata().isLoaded());
+ assertEquals(1L, replicasManagerNew.getBrokerMetadata().getBrokerId().longValue());
+ assertEquals(1L, replicasManagerNew.getBrokerControllerId().longValue());
replicasManagerNew.shutdown();
}
@@ -291,62 +295,57 @@ public void testRegisterFailedAtRegisterSuccess() throws Exception {
when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
replicasManager.start();
-
- Assert.assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManager.getRegisterState());
+
+ assertEquals(ReplicasManager.State.FIRST_TIME_SYNC_CONTROLLER_METADATA_DONE, replicasManager.getState());
+ assertEquals(ReplicasManager.RegisterState.CREATE_METADATA_FILE_DONE, replicasManager.getRegisterState());
TempBrokerMetadata tempBrokerMetadata = replicasManager.getTempBrokerMetadata();
// temp metadata has been cleared
- Assert.assertFalse(tempBrokerMetadata.fileExists());
- Assert.assertFalse(tempBrokerMetadata.isLoaded());
+ assertFalse(tempBrokerMetadata.fileExists());
+ assertFalse(tempBrokerMetadata.isLoaded());
// metadata has been persisted
- Assert.assertTrue(replicasManager.getBrokerMetadata().fileExists());
- Assert.assertTrue(replicasManager.getBrokerMetadata().isLoaded());
- Assert.assertEquals(1L, replicasManager.getBrokerMetadata().getBrokerId().longValue());
- Assert.assertEquals(1L, replicasManager.getBrokerControllerId().longValue());
+ assertTrue(replicasManager.getBrokerMetadata().fileExists());
+ assertTrue(replicasManager.getBrokerMetadata().isLoaded());
+ assertEquals(1L, replicasManager.getBrokerMetadata().getBrokerId().longValue());
+ assertEquals(1L, replicasManager.getBrokerControllerId().longValue());
replicasManager.shutdown();
Mockito.reset(mockedBrokerOuterAPI);
- when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong())).thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
+ when(mockedBrokerOuterAPI.brokerElect(any(), any(), any(), anyLong()))
+ .thenReturn(new Pair<>(new ElectMasterResponseHeader(1L, "127.0.0.1:13131", 1, 1), syncStateSet));
when(mockedBrokerOuterAPI.getControllerMetaData(any())).thenReturn(
new GetMetaDataResponseHeader("default-group", "dledger-a", CONTROLLER_ADDR, true, CONTROLLER_ADDR));
when(mockedBrokerOuterAPI.checkAddressReachable(any())).thenReturn(true);
// restart, we expect that this replicasManager still keep the metadata and still try to finish its registering
ReplicasManager replicasManagerNew = new ReplicasManager(mockedBrokerController);
- // because apply brokerId: 1 has succeeded, so now next broker id is 2
- when(mockedBrokerOuterAPI.getNextBrokerId(any(), any(), any())).thenReturn(new GetNextBrokerIdResponseHeader(CLUSTER_NAME, BROKER_NAME, 2L));
- // because apply brokerId: 1 has succeeded, so next request which try to apply brokerId: 1 will be failed
- when(mockedBrokerOuterAPI.applyBrokerId(any(), any(), eq(1L), any(), any())).thenThrow(new RuntimeException());
when(mockedBrokerOuterAPI.registerBrokerToController(any(), any(), anyLong(), any(), any())).thenReturn(new Pair<>(new RegisterBrokerToControllerResponseHeader(), syncStateSet));
replicasManagerNew.start();
-
- Assert.assertEquals(ReplicasManager.State.RUNNING, replicasManagerNew.getState());
- Assert.assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManagerNew.getRegisterState());
+
+ assertEquals(ReplicasManager.State.RUNNING, replicasManagerNew.getState());
+ assertEquals(ReplicasManager.RegisterState.REGISTERED, replicasManagerNew.getRegisterState());
// tempMetadata has been cleared
- Assert.assertFalse(replicasManagerNew.getTempBrokerMetadata().fileExists());
- Assert.assertFalse(replicasManagerNew.getTempBrokerMetadata().isLoaded());
+ assertFalse(replicasManagerNew.getTempBrokerMetadata().fileExists());
+ assertFalse(replicasManagerNew.getTempBrokerMetadata().isLoaded());
// metadata has been persisted
- Assert.assertTrue(replicasManagerNew.getBrokerMetadata().fileExists());
- Assert.assertTrue(replicasManagerNew.getBrokerMetadata().isLoaded());
- Assert.assertEquals(1L, replicasManagerNew.getBrokerMetadata().getBrokerId().longValue());
- Assert.assertEquals(1L, replicasManagerNew.getBrokerControllerId().longValue());
+ assertTrue(replicasManagerNew.getBrokerMetadata().fileExists());
+ assertTrue(replicasManagerNew.getBrokerMetadata().isLoaded());
+ assertEquals(1L, replicasManagerNew.getBrokerMetadata().getBrokerId().longValue());
+ assertEquals(1L, replicasManagerNew.getBrokerControllerId().longValue());
replicasManagerNew.shutdown();
}
private void checkMetadataFile(BrokerMetadata brokerMetadata0 ,Long brokerId) throws Exception {
- Assert.assertEquals(brokerId, brokerMetadata0.getBrokerId());
- Assert.assertTrue(brokerMetadata0.fileExists());
+ assertEquals(brokerId, brokerMetadata0.getBrokerId());
+ assertTrue(brokerMetadata0.fileExists());
BrokerMetadata brokerMetadata = new BrokerMetadata(brokerMetadata0.getFilePath());
brokerMetadata.readFromFile();
- Assert.assertEquals(brokerMetadata0, brokerMetadata);
+ assertEquals(brokerMetadata0, brokerMetadata);
}
@After
public void clear() {
UtilAll.deleteFile(new File(STORE_BASE_PATH));
}
-
-
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/failover/EscapeBridgeTest.java b/broker/src/test/java/org/apache/rocketmq/broker/failover/EscapeBridgeTest.java
index d7bd753d776..27fc37dbec8 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/failover/EscapeBridgeTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/failover/EscapeBridgeTest.java
@@ -17,27 +17,39 @@
package org.apache.rocketmq.broker.failover;
+import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.broker.topic.TopicRouteInfoManager;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus;
+import org.apache.rocketmq.client.impl.producer.TopicPublishInfo;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.store.DefaultMessageStore;
import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.store.GetMessageStatus;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.PutMessageStatus;
import org.apache.rocketmq.store.SelectMappedBufferResult;
import org.apache.rocketmq.store.logfile.DefaultMappedFile;
import org.apache.rocketmq.store.logfile.MappedFile;
+import org.apache.rocketmq.tieredstore.TieredMessageStore;
import org.assertj.core.api.Assertions;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,8 +61,10 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class EscapeBridgeTest {
@@ -68,11 +82,20 @@ public class EscapeBridgeTest {
@Mock
private DefaultMessageStore defaultMessageStore;
+ @Mock
+ private TieredMessageStore tieredMessageStore;
+
private GetMessageResult getMessageResult;
@Mock
private DefaultMQProducer defaultMQProducer;
+ @Mock
+ private TopicRouteInfoManager topicRouteInfoManager;
+
+ @Mock
+ private BrokerOuterAPI brokerOuterAPI;
+
private static final String BROKER_NAME = "broker_a";
private static final String TEST_TOPIC = "TEST_TOPIC";
@@ -92,14 +115,10 @@ public void before() throws Exception {
when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
when(defaultMessageStore.getMessageAsync(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any())).thenReturn(CompletableFuture.completedFuture(getMessageResult));
- TopicRouteInfoManager topicRouteInfoManager = mock(TopicRouteInfoManager.class);
when(brokerController.getTopicRouteInfoManager()).thenReturn(topicRouteInfoManager);
when(topicRouteInfoManager.findBrokerAddressInSubscribe(anyString(), anyLong(), anyBoolean())).thenReturn("");
- BrokerOuterAPI brokerOuterAPI = mock(BrokerOuterAPI.class);
when(brokerController.getBrokerOuterAPI()).thenReturn(brokerOuterAPI);
- when(brokerOuterAPI.pullMessageFromSpecificBrokerAsync(anyString(), anyString(), anyString(), anyString(), anyInt(), anyLong(), anyInt(), anyLong()))
- .thenReturn(CompletableFuture.completedFuture(new PullResult(PullStatus.FOUND, -1, -1, -1, new ArrayList<>())));
brokerConfig.setEnableSlaveActingMaster(true);
brokerConfig.setEnableRemoteEscape(true);
@@ -179,6 +198,75 @@ public void getMessageAsyncTest() {
Assertions.assertThatCode(() -> escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false)).doesNotThrowAnyException();
}
+ @Test
+ public void getMessageAsyncTest_localStore_getMessageAsync_null() {
+ when(brokerController.getMessageStoreByBrokerName(any())).thenReturn(defaultMessageStore);
+ when(defaultMessageStore.getMessageAsync(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(null));
+ Triple rst = escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("getMessageResult is null", rst.getMiddle());
+ Assert.assertFalse(rst.getRight()); // no retry
+ }
+
+ @Test
+ public void getMessageAsyncTest_localStore_decodeNothing_DefaultMessageStore() throws Exception {
+ when(brokerController.getMessageStoreByBrokerName(any())).thenReturn(defaultMessageStore);
+ for (GetMessageStatus status : GetMessageStatus.values()) {
+ GetMessageResult getMessageResult = mockGetMessageResult(0, TEST_TOPIC, null);
+ getMessageResult.setStatus(status);
+ when(defaultMessageStore.getMessageAsync(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(getMessageResult));
+ Triple rst = escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("Can not get msg", rst.getMiddle());
+ Assert.assertFalse(rst.getRight()); // DefaultMessageStore, no retry
+ }
+ }
+
+ @Test
+ public void getMessageAsyncTest_localStore_decodeNothing_TieredMessageStore() throws Exception {
+ when(brokerController.getMessageStoreByBrokerName(any())).thenReturn(tieredMessageStore);
+ for (GetMessageStatus status : GetMessageStatus.values()) {
+ GetMessageResult getMessageResult = new GetMessageResult();
+ getMessageResult.setStatus(status);
+ when(tieredMessageStore.getMessageAsync(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(getMessageResult));
+ Triple rst = escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("Can not get msg", rst.getMiddle());
+ if (GetMessageStatus.OFFSET_FOUND_NULL.equals(status)) {
+ Assert.assertTrue(rst.getRight()); // TieredMessageStore returns OFFSET_FOUND_NULL, need retry
+ } else {
+ Assert.assertFalse(rst.getRight()); // other status, like DefaultMessageStore, no retry
+ }
+ }
+ }
+
+ @Test
+ public void getMessageAsyncTest_localStore_message_found() throws Exception {
+ when(brokerController.getMessageStoreByBrokerName(any())).thenReturn(defaultMessageStore);
+ when(defaultMessageStore.getMessageAsync(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(mockGetMessageResult(2, TEST_TOPIC, "HW".getBytes())));
+ Triple rst = escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false).join();
+ Assert.assertNotNull(rst.getLeft());
+ Assert.assertEquals(0, rst.getLeft().getQueueOffset());
+ Assert.assertTrue(Arrays.equals("HW".getBytes(), rst.getLeft().getBody()));
+ Assert.assertFalse(rst.getRight());
+ }
+
+ @Test
+ public void getMessageAsyncTest_remoteStore_addressNotFound() throws Exception {
+ when(brokerController.getMessageStoreByBrokerName(any())).thenReturn(null);
+
+ // just test address not found, since we have complete tests of getMessageFromRemoteAsync()
+ when(topicRouteInfoManager.findBrokerAddressInSubscribe(anyString(), anyLong(), anyBoolean())).thenReturn(null);
+ Triple rst = escapeBridge.getMessageAsync(TEST_TOPIC, 0, DEFAULT_QUEUE_ID, BROKER_NAME, false).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("brokerAddress not found", rst.getMiddle());
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
@Test
public void getMessageFromRemoteTest() {
Assertions.assertThatCode(() -> escapeBridge.getMessageFromRemote(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME)).doesNotThrowAnyException();
@@ -189,6 +277,54 @@ public void getMessageFromRemoteAsyncTest() {
Assertions.assertThatCode(() -> escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME)).doesNotThrowAnyException();
}
+ @Test
+ public void getMessageFromRemoteAsyncTest_exception_caught() throws Exception {
+ when(brokerOuterAPI.pullMessageFromSpecificBrokerAsync(anyString(), anyString(), anyString(), anyString(), anyInt(), anyLong(), anyInt(), anyLong()))
+ .thenThrow(new RemotingException("mock remoting exception"));
+ Triple rst = escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("Get message from remote failed", rst.getMiddle());
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ @Test
+ public void getMessageFromRemoteAsyncTest_brokerAddressNotFound() throws Exception {
+ when(topicRouteInfoManager.findBrokerAddressInSubscribe(anyString(), anyLong(), anyBoolean())).thenReturn(null);
+ Triple rst = escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("brokerAddress not found", rst.getMiddle());
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
+ @Test
+ public void getMessageFromRemoteAsyncTest_message_found() throws Exception {
+ PullResult pullResult = new PullResult(PullStatus.FOUND, 1, 1, 1, Arrays.asList(new MessageExt()));
+ when(brokerOuterAPI.pullMessageFromSpecificBrokerAsync(anyString(), anyString(), anyString(), anyString(), anyInt(), anyLong(), anyInt(), anyLong()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(pullResult, "", false))); // right value is ignored
+ Triple rst = escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME).join();
+ Assert.assertNotNull(rst.getLeft());
+ Assert.assertTrue(StringUtils.isEmpty(rst.getMiddle()));
+ Assert.assertFalse(rst.getRight()); // no retry
+ }
+
+ @Test
+ public void getMessageFromRemoteAsyncTest_message_notFound() throws Exception {
+ PullResult pullResult = new PullResult(PullStatus.NO_MATCHED_MSG, 1, 1, 1, null);
+ when(brokerOuterAPI.pullMessageFromSpecificBrokerAsync(anyString(), anyString(), anyString(), anyString(), anyInt(), anyLong(), anyInt(), anyLong()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(pullResult, "no msg", false)));
+ Triple rst = escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("no msg", rst.getMiddle());
+ Assert.assertFalse(rst.getRight()); // no retry
+
+ when(brokerOuterAPI.pullMessageFromSpecificBrokerAsync(anyString(), anyString(), anyString(), anyString(), anyInt(), anyLong(), anyInt(), anyLong()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(null, "other resp code", true)));
+ rst = escapeBridge.getMessageFromRemoteAsync(TEST_TOPIC, 1, DEFAULT_QUEUE_ID, BROKER_NAME).join();
+ Assert.assertNull(rst.getLeft());
+ Assert.assertEquals("other resp code", rst.getMiddle());
+ Assert.assertTrue(rst.getRight()); // need retry
+ }
+
@Test
public void decodeMsgListTest() {
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
@@ -199,4 +335,98 @@ public void decodeMsgListTest() {
Assertions.assertThatCode(() -> escapeBridge.decodeMsgList(getMessageResult, false)).doesNotThrowAnyException();
}
+ @Test
+ public void decodeMsgListTest_messageNotNull() throws Exception {
+ MessageExt msg = new MessageExt();
+ msg.setBody("HW".getBytes());
+ msg.setTopic("topic");
+ msg.setBornHost(new InetSocketAddress("127.0.0.1", 9000));
+ msg.setStoreHost(new InetSocketAddress("127.0.0.1", 9000));
+ ByteBuffer byteBuffer = ByteBuffer.wrap(MessageDecoder.encode(msg, false));
+ SelectMappedBufferResult result = new SelectMappedBufferResult(0, byteBuffer, 10, new DefaultMappedFile());
+
+
+ getMessageResult.addMessage(result);
+ getMessageResult.getMessageQueueOffset().add(0L);
+ List list = escapeBridge.decodeMsgList(getMessageResult, false); // skip deCompressBody test
+ Assert.assertEquals(1, list.size());
+ Assert.assertTrue(Arrays.equals(msg.getBody(), list.get(0).getBody()));
+ }
+
+ @Test
+ public void testPutMessageToRemoteBroker_noSpecificBrokerName_hasRemoteBroker() throws Exception {
+ MessageExtBrokerInner message = new MessageExtBrokerInner();
+ message.setTopic(TEST_TOPIC);
+ String anotherBrokerName = "broker_b";
+ TopicPublishInfo publishInfo = mockTopicPublishInfo(BROKER_NAME, anotherBrokerName);
+ when(topicRouteInfoManager.tryToFindTopicPublishInfo(anyString())).thenReturn(publishInfo);
+ when(topicRouteInfoManager.findBrokerAddressInPublish(anotherBrokerName)).thenReturn("127.0.0.1");
+ escapeBridge.putMessageToRemoteBroker(message, null);
+ verify(brokerOuterAPI).sendMessageToSpecificBroker(eq("127.0.0.1"), eq(anotherBrokerName), any(MessageExtBrokerInner.class), anyString(), anyLong());
+ }
+
+ @Test
+ public void testPutMessageToRemoteBroker_noSpecificBrokerName_noRemoteBroker() throws Exception {
+ MessageExtBrokerInner message = new MessageExtBrokerInner();
+ message.setTopic(TEST_TOPIC);
+ TopicPublishInfo publishInfo = mockTopicPublishInfo(BROKER_NAME);
+ when(topicRouteInfoManager.tryToFindTopicPublishInfo(anyString())).thenReturn(publishInfo);
+ escapeBridge.putMessageToRemoteBroker(message, null);
+ verify(topicRouteInfoManager, times(0)).findBrokerAddressInPublish(anyString());
+ }
+
+ @Test
+ public void testPutMessageToRemoteBroker_specificBrokerName_equals() throws Exception {
+ escapeBridge.putMessageToRemoteBroker(new MessageExtBrokerInner(), BROKER_NAME);
+ verify(topicRouteInfoManager, times(0)).tryToFindTopicPublishInfo(anyString());
+ }
+
+ @Test
+ public void testPutMessageToRemoteBroker_specificBrokerName_addressNotFound() throws Exception {
+ MessageExtBrokerInner message = new MessageExtBrokerInner();
+ message.setTopic(TEST_TOPIC);
+ TopicPublishInfo publishInfo = mockTopicPublishInfo(BROKER_NAME);
+ when(topicRouteInfoManager.tryToFindTopicPublishInfo(anyString())).thenReturn(publishInfo);
+ escapeBridge.putMessageToRemoteBroker(message, "whatever");
+ verify(topicRouteInfoManager).findBrokerAddressInPublish(eq("whatever"));
+ verify(brokerOuterAPI, times(0)).sendMessageToSpecificBroker(anyString(), anyString(), any(MessageExtBrokerInner.class), anyString(), anyLong());
+ }
+
+ @Test
+ public void testPutMessageToRemoteBroker_specificBrokerName_addressFound() throws Exception {
+ MessageExtBrokerInner message = new MessageExtBrokerInner();
+ message.setTopic(TEST_TOPIC);
+ String anotherBrokerName = "broker_b";
+ TopicPublishInfo publishInfo = mockTopicPublishInfo(BROKER_NAME, anotherBrokerName);
+ when(topicRouteInfoManager.tryToFindTopicPublishInfo(anyString())).thenReturn(publishInfo);
+ when(topicRouteInfoManager.findBrokerAddressInPublish(anotherBrokerName)).thenReturn("127.0.0.1");
+ escapeBridge.putMessageToRemoteBroker(message, anotherBrokerName);
+ verify(brokerOuterAPI).sendMessageToSpecificBroker(eq("127.0.0.1"), eq(anotherBrokerName), any(MessageExtBrokerInner.class), anyString(), anyLong());
+ }
+
+ private GetMessageResult mockGetMessageResult(int count, String topic, byte[] body) throws Exception {
+ GetMessageResult result = new GetMessageResult();
+ for (int i = 0; i < count; i++) {
+ MessageExt msg = new MessageExt();
+ msg.setBody(body);
+ msg.setTopic(topic);
+ msg.setBornHost(new InetSocketAddress("127.0.0.1", 9000));
+ msg.setStoreHost(new InetSocketAddress("127.0.0.1", 9000));
+ ByteBuffer byteBuffer = ByteBuffer.wrap(MessageDecoder.encode(msg, false));
+ SelectMappedBufferResult bufferResult = new SelectMappedBufferResult(0, byteBuffer, body.length, new DefaultMappedFile());
+
+ result.addMessage(bufferResult);
+ result.getMessageQueueOffset().add(i + 0L);
+ }
+ return result;
+ }
+
+ private TopicPublishInfo mockTopicPublishInfo(String... brokerNames) {
+ TopicPublishInfo topicPublishInfo = new TopicPublishInfo();
+ for (String brokerName : brokerNames) {
+ topicPublishInfo.getMessageQueueList().add(new MessageQueue(TEST_TOPIC, brokerName, 0));
+ }
+ return topicPublishInfo;
+ }
+
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/latency/BrokerFastFailureTest.java b/broker/src/test/java/org/apache/rocketmq/broker/latency/BrokerFastFailureTest.java
index 31b547cf1be..2216a1d50c1 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/latency/BrokerFastFailureTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/latency/BrokerFastFailureTest.java
@@ -19,16 +19,46 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.future.FutureTaskExt;
import org.apache.rocketmq.remoting.netty.RequestTask;
+import org.apache.rocketmq.store.DefaultMessageStore;
+import org.apache.rocketmq.store.MessageStore;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
import static org.assertj.core.api.Assertions.assertThat;
public class BrokerFastFailureTest {
+
+ private BrokerController brokerController;
+
+ private final BrokerConfig brokerConfig = new BrokerConfig();
+
+ private MessageStore messageStore;
+
+ @Before
+ public void setUp() {
+ brokerController = Mockito.mock(BrokerController.class);
+ messageStore = Mockito.mock(DefaultMessageStore.class);
+ BlockingQueue queue = new LinkedBlockingQueue<>();
+ Mockito.when(brokerController.getSendThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getPullThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getLitePullThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getHeartbeatThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getEndTransactionThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getAdminBrokerThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getAckThreadPoolQueue()).thenReturn(queue);
+ Mockito.when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
+ Mockito.when(messageStore.isOSPageCacheBusy()).thenReturn(false);
+ Mockito.when(brokerController.getMessageStore()).thenReturn(messageStore);
+ }
+
@Test
public void testCleanExpiredRequestInQueue() throws Exception {
- BrokerFastFailure brokerFastFailure = new BrokerFastFailure(null);
+ BrokerFastFailure brokerFastFailure = new BrokerFastFailure(brokerController);
BlockingQueue queue = new LinkedBlockingQueue<>();
brokerFastFailure.cleanExpiredRequestInQueue(queue, 1);
@@ -63,4 +93,40 @@ public void run() {
assertThat(((FutureTaskExt) queue.peek()).getRunnable()).isEqualTo(requestTask);
}
+ @Test
+ public void testCleanExpiredCustomRequestInQueue() throws Exception {
+ BrokerFastFailure brokerFastFailure = new BrokerFastFailure(brokerController);
+ brokerFastFailure.start();
+ brokerConfig.setWaitTimeMillsInAckQueue(10);
+ BlockingQueue customThreadPoolQueue = new LinkedBlockingQueue<>();
+ brokerFastFailure.addCleanExpiredRequestQueue(customThreadPoolQueue, () -> brokerConfig.getWaitTimeMillsInAckQueue());
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+
+ }
+ };
+ RequestTask requestTask = new RequestTask(runnable, null, null);
+ customThreadPoolQueue.add(new FutureTaskExt<>(requestTask, null));
+
+ Thread.sleep(2000);
+
+ assertThat(customThreadPoolQueue.size()).isEqualTo(0);
+ assertThat(requestTask.isStopRun()).isEqualTo(true);
+
+ brokerConfig.setWaitTimeMillsInAckQueue(10000);
+
+ RequestTask requestTask2 = new RequestTask(runnable, null, null);
+ customThreadPoolQueue.add(new FutureTaskExt<>(requestTask2, null));
+
+ Thread.sleep(1000);
+
+ assertThat(customThreadPoolQueue.size()).isEqualTo(1);
+ assertThat(((FutureTaskExt) customThreadPoolQueue.peek()).getRunnable()).isEqualTo(requestTask2);
+
+ brokerFastFailure.shutdown();
+
+ }
+
}
\ No newline at end of file
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/metrics/BrokerMetricsManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/metrics/BrokerMetricsManagerTest.java
index 11f7ae8215a..9264eb4b56b 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/metrics/BrokerMetricsManagerTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/metrics/BrokerMetricsManagerTest.java
@@ -20,8 +20,25 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.attribute.TopicMessageType;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.metrics.MetricsExporterType;
+import org.apache.rocketmq.common.topic.TopicValidator;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeader;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.junit.Test;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
import static org.assertj.core.api.Assertions.assertThat;
public class BrokerMetricsManagerTest {
@@ -29,7 +46,7 @@ public class BrokerMetricsManagerTest {
@Test
public void testNewAttributesBuilder() {
Attributes attributes = BrokerMetricsManager.newAttributesBuilder().put("a", "b")
- .build();
+ .build();
assertThat(attributes.get(AttributeKey.stringKey("a"))).isEqualTo("b");
}
@@ -37,6 +54,7 @@ public void testNewAttributesBuilder() {
public void testCustomizedAttributesBuilder() {
BrokerMetricsManager.attributesBuilderSupplier = () -> new AttributesBuilder() {
private AttributesBuilder attributesBuilder = Attributes.builder();
+
@Override
public Attributes build() {
return attributesBuilder.put("customized", "value").build();
@@ -61,8 +79,263 @@ public AttributesBuilder putAll(Attributes attributes) {
}
};
Attributes attributes = BrokerMetricsManager.newAttributesBuilder().put("a", "b")
- .build();
+ .build();
assertThat(attributes.get(AttributeKey.stringKey("a"))).isEqualTo("b");
assertThat(attributes.get(AttributeKey.stringKey("customized"))).isEqualTo("value");
}
+
+
+ @Test
+ public void testIsRetryOrDlqTopicWithRetryTopic() {
+ String topic = MixAll.RETRY_GROUP_TOPIC_PREFIX + "TestTopic";
+ boolean result = BrokerMetricsManager.isRetryOrDlqTopic(topic);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void testIsRetryOrDlqTopicWithDlqTopic() {
+ String topic = MixAll.DLQ_GROUP_TOPIC_PREFIX + "TestTopic";
+ boolean result = BrokerMetricsManager.isRetryOrDlqTopic(topic);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void testIsRetryOrDlqTopicWithNonRetryOrDlqTopic() {
+ String topic = "NormalTopic";
+ boolean result = BrokerMetricsManager.isRetryOrDlqTopic(topic);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsRetryOrDlqTopicWithEmptyTopic() {
+ String topic = "";
+ boolean result = BrokerMetricsManager.isRetryOrDlqTopic(topic);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsRetryOrDlqTopicWithNullTopic() {
+ String topic = null;
+ boolean result = BrokerMetricsManager.isRetryOrDlqTopic(topic);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsSystemGroup_SystemGroup_ReturnsTrue() {
+ String group = "FooGroup";
+ String systemGroup = MixAll.CID_RMQ_SYS_PREFIX + group;
+ boolean result = BrokerMetricsManager.isSystemGroup(systemGroup);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void testIsSystemGroup_NonSystemGroup_ReturnsFalse() {
+ String group = "FooGroup";
+ boolean result = BrokerMetricsManager.isSystemGroup(group);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsSystemGroup_EmptyGroup_ReturnsFalse() {
+ String group = "";
+ boolean result = BrokerMetricsManager.isSystemGroup(group);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsSystemGroup_NullGroup_ReturnsFalse() {
+ String group = null;
+ boolean result = BrokerMetricsManager.isSystemGroup(group);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsSystem_SystemTopicOrSystemGroup_ReturnsTrue() {
+ String topic = "FooTopic";
+ String group = "FooGroup";
+ String systemTopic = TopicValidator.RMQ_SYS_TRANS_HALF_TOPIC;
+ String systemGroup = MixAll.CID_RMQ_SYS_PREFIX + group;
+
+ boolean resultTopic = BrokerMetricsManager.isSystem(systemTopic, group);
+ assertThat(resultTopic).isTrue();
+
+ boolean resultGroup = BrokerMetricsManager.isSystem(topic, systemGroup);
+ assertThat(resultGroup).isTrue();
+ }
+
+ @Test
+ public void testIsSystem_NonSystemTopicAndGroup_ReturnsFalse() {
+ String topic = "FooTopic";
+ String group = "FooGroup";
+ boolean result = BrokerMetricsManager.isSystem(topic, group);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsSystem_EmptyTopicAndGroup_ReturnsFalse() {
+ String topic = "";
+ String group = "";
+ boolean result = BrokerMetricsManager.isSystem(topic, group);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testGetMessageTypeAsNormal() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ requestHeader.setProperties("");
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.NORMAL).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsTransaction() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.TRANSACTION).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsFifo() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_SHARDING_KEY, "shardingKey");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.FIFO).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsDelayLevel() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "1");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.DELAY).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsDeliverMS() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_TIMER_DELIVER_MS, "10");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.DELAY).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsDelaySEC() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_TIMER_DELAY_SEC, "1");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.DELAY).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeAsDelayMS() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_TIMER_DELAY_MS, "10");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.DELAY).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeWithUnknownProperty() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put("unknownProperty", "unknownValue");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.NORMAL).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeWithMultipleProperties() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "1");
+ map.put(MessageConst.PROPERTY_SHARDING_KEY, "shardingKey");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.FIFO).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeWithTransactionFlagButOtherPropertiesPresent() {
+ SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+ Map map = new HashMap<>();
+ map.put(MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
+ map.put(MessageConst.PROPERTY_SHARDING_KEY, "shardingKey");
+ requestHeader.setProperties(MessageDecoder.messageProperties2String(map));
+
+ TopicMessageType result = BrokerMetricsManager.getMessageType(requestHeader);
+ assertThat(TopicMessageType.TRANSACTION).isEqualTo(result);
+ }
+
+ @Test
+ public void testGetMessageTypeWithEmptyProperties() {
+ TopicMessageType result = BrokerMetricsManager.getMessageType(new SendMessageRequestHeader());
+ assertThat(TopicMessageType.NORMAL).isEqualTo(result);
+ }
+
+ @Test
+ public void testCreateMetricsManager() {
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ String storePathRootDir = System.getProperty("java.io.tmpdir") + File.separator + "store-"
+ + UUID.randomUUID();
+ messageStoreConfig.setStorePathRootDir(storePathRootDir);
+ BrokerConfig brokerConfig = new BrokerConfig();
+
+ NettyServerConfig nettyServerConfig = new NettyServerConfig();
+ nettyServerConfig.setListenPort(0);
+
+ BrokerController brokerController = new BrokerController(brokerConfig, nettyServerConfig,
+ new NettyClientConfig(), messageStoreConfig);
+
+ BrokerMetricsManager metricsManager = new BrokerMetricsManager(brokerController);
+
+ assertThat(metricsManager.getBrokerMeter()).isNull();
+ }
+
+ @Test
+ public void testCreateMetricsManagerLogType() throws CloneNotSupportedException {
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setMetricsExporterType(MetricsExporterType.LOG);
+ brokerConfig.setMetricsLabel("label1:value1;label2:value2");
+ brokerConfig.setMetricsOtelCardinalityLimit(1);
+
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ String storePathRootDir = System.getProperty("java.io.tmpdir") + File.separator + "store-"
+ + UUID.randomUUID();
+ messageStoreConfig.setStorePathRootDir(storePathRootDir);
+
+ NettyServerConfig nettyServerConfig = new NettyServerConfig();
+ nettyServerConfig.setListenPort(0);
+
+ BrokerController brokerController = new BrokerController(brokerConfig, nettyServerConfig,
+ new NettyClientConfig(), messageStoreConfig);
+ brokerController.initialize();
+
+ BrokerMetricsManager metricsManager = new BrokerMetricsManager(brokerController);
+
+ assertThat(metricsManager.getBrokerMeter()).isNotNull();
+ }
}
\ No newline at end of file
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBLmqConsumerOffsetManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBLmqConsumerOffsetManagerTest.java
new file mode 100644
index 00000000000..ea6528546dc
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBLmqConsumerOffsetManagerTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.rocketmq.broker.offset;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+public class RocksDBLmqConsumerOffsetManagerTest {
+ private static final String LMQ_GROUP = MixAll.LMQ_PREFIX + "FooBarGroup";
+ private static final String NON_LMQ_GROUP = "nonLmqGroup";
+ private static final String TOPIC = "FooBarTopic";
+ private static final int QUEUE_ID = 0;
+ private static final long OFFSET = 12345;
+
+ private BrokerController brokerController;
+
+ private RocksDBLmqConsumerOffsetManager offsetManager;
+
+ @Before
+ public void setUp() {
+ brokerController = Mockito.mock(BrokerController.class);
+ when(brokerController.getMessageStoreConfig()).thenReturn(Mockito.mock(MessageStoreConfig.class));
+ when(brokerController.getBrokerConfig()).thenReturn(Mockito.mock(BrokerConfig.class));
+ offsetManager = new RocksDBLmqConsumerOffsetManager(brokerController);
+ }
+
+ @Test
+ public void testQueryOffsetForLmq() {
+ // Setup
+ offsetManager.getLmqOffsetTable().put(getKey(), OFFSET);
+ // Execute
+ long actualOffset = offsetManager.queryOffset(LMQ_GROUP, TOPIC, QUEUE_ID);
+ // Verify
+ assertEquals("Offset should match the expected value.", OFFSET, actualOffset);
+ }
+
+ @Test
+ public void testQueryOffsetForNonLmq() {
+ long actualOffset = offsetManager.queryOffset(NON_LMQ_GROUP, TOPIC, QUEUE_ID);
+ // Verify
+ assertEquals("Offset should not be null.", -1, actualOffset);
+ }
+
+
+ @Test
+ public void testQueryOffsetForLmqGroupWithExistingOffset() {
+ offsetManager.getLmqOffsetTable().put(getKey(), OFFSET);
+
+ // Act
+ Map actualOffsets = offsetManager.queryOffset(LMQ_GROUP, TOPIC);
+
+ // Assert
+ assertNotNull(actualOffsets);
+ assertEquals(1, actualOffsets.size());
+ assertEquals(OFFSET, (long) actualOffsets.get(0));
+ }
+
+ @Test
+ public void testQueryOffsetForLmqGroupWithoutExistingOffset() {
+ // Act
+ Map actualOffsets = offsetManager.queryOffset(LMQ_GROUP, "nonExistingTopic");
+
+ // Assert
+ assertNotNull(actualOffsets);
+ assertTrue("The map should be empty for non-existing offsets", actualOffsets.isEmpty());
+ }
+
+ @Test
+ public void testQueryOffsetForNonLmqGroup() {
+ when(brokerController.getBrokerConfig().getConsumerOffsetUpdateVersionStep()).thenReturn(1L);
+ // Arrange
+ Map mockOffsets = new HashMap<>();
+ mockOffsets.put(QUEUE_ID, OFFSET);
+
+ offsetManager.commitOffset("clientHost", NON_LMQ_GROUP, TOPIC, QUEUE_ID, OFFSET);
+
+ // Act
+ Map actualOffsets = offsetManager.queryOffset(NON_LMQ_GROUP, TOPIC);
+
+ // Assert
+ assertNotNull(actualOffsets);
+ assertEquals("Offsets should match the mocked return value for non-LMQ groups", mockOffsets, actualOffsets);
+ }
+
+ @Test
+ public void testCommitOffsetForLmq() {
+ // Execute
+ offsetManager.commitOffset("clientHost", LMQ_GROUP, TOPIC, QUEUE_ID, OFFSET);
+ // Verify
+ Long expectedOffset = offsetManager.getLmqOffsetTable().get(getKey());
+ assertEquals("Offset should be updated correctly.", OFFSET, expectedOffset.longValue());
+ }
+
+ @Test
+ public void testEncode() {
+ offsetManager.setLmqOffsetTable(new ConcurrentHashMap<>(512));
+ offsetManager.getLmqOffsetTable().put(getKey(), OFFSET);
+ String encodedData = offsetManager.encode();
+ assertTrue(encodedData.contains(String.valueOf(OFFSET)));
+ }
+
+ private String getKey() {
+ return TOPIC + "@" + LMQ_GROUP;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBOffsetSerializeWrapperTest.java b/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBOffsetSerializeWrapperTest.java
new file mode 100644
index 00000000000..dde0401e8ae
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/offset/RocksDBOffsetSerializeWrapperTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.rocketmq.broker.offset;
+
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class RocksDBOffsetSerializeWrapperTest {
+
+ private RocksDBOffsetSerializeWrapper wrapper;
+
+ @Before
+ public void setUp() {
+ wrapper = new RocksDBOffsetSerializeWrapper();
+ }
+
+ @Test
+ public void testGetOffsetTable_ShouldReturnConcurrentHashMap() {
+ ConcurrentMap offsetTable = wrapper.getOffsetTable();
+ assertNotNull("The offsetTable should not be null", offsetTable);
+ }
+
+ @Test
+ public void testSetOffsetTable_ShouldSetTheOffsetTableCorrectly() {
+ ConcurrentMap newOffsetTable = new ConcurrentHashMap<>();
+ wrapper.setOffsetTable(newOffsetTable);
+ ConcurrentMap offsetTable = wrapper.getOffsetTable();
+ assertEquals("The offsetTable should be the same as the one set", newOffsetTable, offsetTable);
+ }
+
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java
index e66703e5653..04324043fb8 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java
@@ -20,21 +20,6 @@
import com.google.common.collect.Sets;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.LongAdder;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
import org.apache.rocketmq.auth.authentication.model.Subject;
@@ -45,8 +30,10 @@
import org.apache.rocketmq.auth.authorization.model.Environment;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.client.ClientChannelInfo;
import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
import org.apache.rocketmq.broker.client.ConsumerManager;
+import org.apache.rocketmq.broker.client.net.Broker2Client;
import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
import org.apache.rocketmq.broker.schedule.ScheduleMessageService;
import org.apache.rocketmq.broker.subscription.RocksDBSubscriptionGroupManager;
@@ -55,11 +42,13 @@
import org.apache.rocketmq.common.BoundaryType;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.KeyBuilder;
+import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.TopicFilterType;
import org.apache.rocketmq.common.TopicQueueId;
import org.apache.rocketmq.common.action.Action;
+import org.apache.rocketmq.common.constant.FIleReadaheadMode;
import org.apache.rocketmq.common.constant.PermName;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageAccessor;
@@ -67,13 +56,20 @@
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.topic.TopicValidator;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
+import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.AclInfo;
+import org.apache.rocketmq.remoting.protocol.body.CreateTopicListRequestBody;
+import org.apache.rocketmq.remoting.protocol.body.GroupList;
+import org.apache.rocketmq.remoting.protocol.body.HARuntimeInfo;
import org.apache.rocketmq.remoting.protocol.body.LockBatchRequestBody;
+import org.apache.rocketmq.remoting.protocol.body.QueryCorrectionOffsetBody;
import org.apache.rocketmq.remoting.protocol.body.UnlockBatchRequestBody;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.apache.rocketmq.remoting.protocol.header.CreateAclRequestHeader;
@@ -82,8 +78,11 @@
import org.apache.rocketmq.remoting.protocol.header.DeleteAclRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.DeleteTopicRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.DeleteUserRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ExchangeHAInfoResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.GetAclRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetAllTopicConfigResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerRunningInfoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerStatusRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetEarliestMsgStoretimeRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetMaxOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetMinOffsetRequestHeader;
@@ -91,6 +90,13 @@
import org.apache.rocketmq.remoting.protocol.header.GetUserRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ListAclsRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ListUsersRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.NotifyMinBrokerIdChangeRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryCorrectionOffsetHeader;
+import org.apache.rocketmq.remoting.protocol.header.QuerySubscriptionByConsumerRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryTopicConsumeByWhoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryTopicsByConsumerRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ResetMasterFlushOffsetHeader;
+import org.apache.rocketmq.remoting.protocol.header.ResetOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ResumeCheckHalfMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.SearchOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateAclRequestHeader;
@@ -98,12 +104,17 @@
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.store.CommitLog;
import org.apache.rocketmq.store.DefaultMessageStore;
import org.apache.rocketmq.store.MessageStore;
import org.apache.rocketmq.store.SelectMappedBufferResult;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.apache.rocketmq.store.logfile.DefaultMappedFile;
import org.apache.rocketmq.store.stats.BrokerStats;
+import org.apache.rocketmq.store.timer.TimerCheckpoint;
+import org.apache.rocketmq.store.timer.TimerMessageStore;
+import org.apache.rocketmq.store.timer.TimerMetrics;
+import org.apache.rocketmq.store.util.LibC;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -112,6 +123,26 @@
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.LongAdder;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -170,11 +201,32 @@ public class AdminBrokerProcessorTest {
@Mock
private AuthorizationMetadataManager authorizationMetadataManager;
+ @Mock
+ private TimerMessageStore timerMessageStore;
+
+ @Mock
+ private TimerMetrics timerMetrics;
+
+ @Mock
+ private MessageStoreConfig messageStoreConfig;
+
+ @Mock
+ private CommitLog commitLog;
+
+ @Mock
+ private Broker2Client broker2Client;
+
+ @Mock
+ private ClientChannelInfo clientChannelInfo;
+
@Before
public void init() throws Exception {
brokerController.setMessageStore(messageStore);
brokerController.setAuthenticationMetadataManager(authenticationMetadataManager);
brokerController.setAuthorizationMetadataManager(authorizationMetadataManager);
+ Field field = BrokerController.class.getDeclaredField("broker2Client");
+ field.setAccessible(true);
+ field.set(brokerController, broker2Client);
//doReturn(sendMessageProcessor).when(brokerController).getSendMessageProcessor();
@@ -280,6 +332,31 @@ public void testUpdateAndCreateTopic() throws Exception {
assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
}
+ @Test
+ public void testUpdateAndCreateTopicList() throws RemotingCommandException {
+ List systemTopicList = new ArrayList<>(systemTopicSet);
+ RemotingCommand request = buildCreateTopicListRequest(systemTopicList);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+ assertThat(response.getRemark()).isEqualTo("The topic[" + systemTopicList.get(0) + "] is conflict with system topic.");
+
+ List inValidTopicList = new ArrayList<>();
+ inValidTopicList.add("");
+ request = buildCreateTopicListRequest(inValidTopicList);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ List topicList = new ArrayList<>();
+ topicList.add("TEST_CREATE_TOPIC");
+ topicList.add("TEST_CREATE_TOPIC1");
+ request = buildCreateTopicListRequest(topicList);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ //test no changes
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
@Test
public void testDeleteTopicInRocksdb() throws Exception {
if (notToBeExecuted()) {
@@ -815,7 +892,6 @@ public void testCreateAcl() throws RemotingCommandException {
request.setBody(JSON.toJSONBytes(aclInfo));
RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
-
}
@Test
@@ -833,7 +909,6 @@ public void testUpdateAcl() throws RemotingCommandException {
request.setBody(JSON.toJSONBytes(aclInfo));
RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
-
}
@Test
@@ -893,6 +968,349 @@ public void testListAcl() throws RemotingCommandException {
assertThat(aclInfoData.get(0).getPolicies().get(0).getEntries().get(0).getDecision()).isEqualTo("Allow");
}
+ @Test
+ public void testGetTimeCheckPoint() throws RemotingCommandException {
+ when(this.brokerController.getTimerCheckpoint()).thenReturn(null);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_TIMER_CHECK_POINT, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+ assertThat(response.getRemark()).isEqualTo("The checkpoint is null");
+
+ when(this.brokerController.getTimerCheckpoint()).thenReturn(new TimerCheckpoint());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+ @Test
+ public void testGetTimeMetrics() throws RemotingCommandException, IOException {
+ when(this.brokerController.getMessageStore().getTimerMessageStore()).thenReturn(null);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_TIMER_METRICS, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ when(this.brokerController.getMessageStore().getTimerMessageStore()).thenReturn(timerMessageStore);
+ when(this.timerMessageStore.getTimerMetrics()).thenReturn(timerMetrics);
+ when(this.timerMetrics.encode()).thenReturn(new TimerMetrics.TimerMetricsSerializeWrapper().toJson(false));
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testUpdateColdDataFlowCtrGroupConfig() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_COLD_DATA_FLOW_CTR_CONFIG, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ request.setBody("consumerGroup1=1".getBytes());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ request.setBody("".getBytes());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testRemoveColdDataFlowCtrGroupConfig() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.REMOVE_COLD_DATA_FLOW_CTR_CONFIG, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ request.setBody("consumerGroup1".getBytes());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetColdDataFlowCtrInfo() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_COLD_DATA_FLOW_CTR_INFO, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testSetCommitLogReadAheadMode() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.SET_COMMITLOG_READ_MODE, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ HashMap extfields = new HashMap<>();
+ extfields.put(FIleReadaheadMode.READ_AHEAD_MODE, String.valueOf(LibC.MADV_DONTNEED));
+ request.setExtFields(extfields);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ extfields.clear();
+ extfields.put(FIleReadaheadMode.READ_AHEAD_MODE, String.valueOf(LibC.MADV_NORMAL));
+ request.setExtFields(extfields);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ this.brokerController.setMessageStore(defaultMessageStore);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ when(this.defaultMessageStore.getMessageStoreConfig()).thenReturn(messageStoreConfig);
+ when(this.defaultMessageStore.getCommitLog()).thenReturn(commitLog);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetUnknownCmdResponse() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(10000, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.REQUEST_CODE_NOT_SUPPORTED);
+ }
+
+ @Test
+ public void testGetAllMessageRequestMode() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_MESSAGE_REQUEST_MODE, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testResetOffset() throws RemotingCommandException {
+ ResetOffsetRequestHeader requestHeader =
+ createRequestHeader("topic","group",-1,false,-1,-1);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.INVOKE_BROKER_TO_RESET_OFFSET, requestHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.TOPIC_NOT_EXIST);
+
+ this.brokerController.getTopicConfigManager().getTopicConfigTable().put("topic", new TopicConfig("topic"));
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+
+ this.brokerController.getSubscriptionGroupManager().getSubscriptionGroupTable().put("group", new SubscriptionGroupConfig());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ requestHeader.setQueueId(0);
+ request = RemotingCommand.createRequestCommand(RequestCode.INVOKE_BROKER_TO_RESET_OFFSET, requestHeader);
+ request.makeCustomHeaderToNet();
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ requestHeader.setOffset(2L);
+ request = RemotingCommand.createRequestCommand(RequestCode.INVOKE_BROKER_TO_RESET_OFFSET, requestHeader);
+ request.makeCustomHeaderToNet();
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+ }
+
+ @Test
+ public void testGetConsumerStatus() throws RemotingCommandException {
+ GetConsumerStatusRequestHeader requestHeader = new GetConsumerStatusRequestHeader();
+ requestHeader.setGroup("group");
+ requestHeader.setTopic("topic");
+ requestHeader.setClientAddr("");
+ RemotingCommand request = RemotingCommand
+ .createRequestCommand(RequestCode.INVOKE_BROKER_TO_GET_CONSUMER_STATUS, requestHeader);
+ RemotingCommand responseCommand = RemotingCommand.createResponseCommand(null);
+ responseCommand.setCode(ResponseCode.SUCCESS);
+ when(broker2Client.getConsumeStatus(anyString(),anyString(),anyString())).thenReturn(responseCommand);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testQueryTopicConsumeByWho() throws RemotingCommandException {
+ QueryTopicConsumeByWhoRequestHeader requestHeader = new QueryTopicConsumeByWhoRequestHeader();
+ requestHeader.setTopic("topic");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_TOPIC_CONSUME_BY_WHO, requestHeader);
+ request.makeCustomHeaderToNet();
+ HashSet groups = new HashSet<>();
+ groups.add("group");
+ when(brokerController.getConsumerManager()).thenReturn(consumerManager);
+ when(consumerManager.queryTopicConsumeByWho(anyString())).thenReturn(groups);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ assertThat(RemotingSerializable.decode(response.getBody(), GroupList.class)
+ .getGroupList().contains("group"))
+ .isEqualTo(groups.contains("group"));
+ }
+
+ @Test
+ public void testQueryTopicByConsumer() throws RemotingCommandException {
+ QueryTopicsByConsumerRequestHeader requestHeader = new QueryTopicsByConsumerRequestHeader();
+ requestHeader.setGroup("group");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_TOPICS_BY_CONSUMER, requestHeader);
+ request.makeCustomHeaderToNet();
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testQuerySubscriptionByConsumer() throws RemotingCommandException {
+ QuerySubscriptionByConsumerRequestHeader requestHeader = new QuerySubscriptionByConsumerRequestHeader();
+ requestHeader.setGroup("group");
+ requestHeader.setTopic("topic");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_SUBSCRIPTION_BY_CONSUMER, requestHeader);
+ request.makeCustomHeaderToNet();
+ when(brokerController.getConsumerManager()).thenReturn(consumerManager);
+ when(consumerManager.findSubscriptionData(anyString(),anyString())).thenReturn(null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetSystemTopicListFromBroker() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_BROKER, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testCleanExpiredConsumeQueue() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.CLEAN_EXPIRED_CONSUMEQUEUE, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testDeleteExpiredCommitLog() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.DELETE_EXPIRED_COMMITLOG, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testCleanUnusedTopic() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.CLEAN_UNUSED_TOPIC, null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetConsumerRunningInfo() throws RemotingCommandException, RemotingSendRequestException, RemotingTimeoutException, InterruptedException {
+ when(brokerController.getConsumerManager()).thenReturn(consumerManager);
+ when(consumerManager.findChannel(anyString(),anyString())).thenReturn(null);
+ GetConsumerRunningInfoRequestHeader requestHeader = new GetConsumerRunningInfoRequestHeader();
+ requestHeader.setClientId("client");
+ requestHeader.setConsumerGroup("group");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_CONSUMER_RUNNING_INFO, requestHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ when(consumerManager.findChannel(anyString(),anyString())).thenReturn(clientChannelInfo);
+ when(clientChannelInfo.getVersion()).thenReturn(MQVersion.Version.V3_0_0_SNAPSHOT.ordinal());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ when(clientChannelInfo.getVersion()).thenReturn(MQVersion.Version.V5_2_3.ordinal());
+ when(brokerController.getBroker2Client()).thenReturn(broker2Client);
+ when(clientChannelInfo.getChannel()).thenReturn(channel);
+ RemotingCommand responseCommand = RemotingCommand.createResponseCommand(null);
+ responseCommand.setCode(ResponseCode.SUCCESS);
+ when(broker2Client.callClient(any(Channel.class),any(RemotingCommand.class))).thenReturn(responseCommand);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ when(broker2Client.callClient(any(Channel.class),any(RemotingCommand.class))).thenThrow(new RemotingTimeoutException("timeout"));
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.CONSUME_MSG_TIMEOUT);
+ }
+
+ @Test
+ public void testQueryCorrectionOffset() throws RemotingCommandException {
+ Map correctionOffsetMap = new HashMap<>();
+ correctionOffsetMap.put(0, 100L);
+ correctionOffsetMap.put(1, 200L);
+ Map compareOffsetMap = new HashMap<>();
+ compareOffsetMap.put(0, 80L);
+ compareOffsetMap.put(1, 300L);
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(consumerOffsetManager.queryMinOffsetInAllGroup(anyString(),anyString())).thenReturn(correctionOffsetMap);
+ when(consumerOffsetManager.queryOffset(anyString(),anyString())).thenReturn(compareOffsetMap);
+ QueryCorrectionOffsetHeader queryCorrectionOffsetHeader = new QueryCorrectionOffsetHeader();
+ queryCorrectionOffsetHeader.setTopic("topic");
+ queryCorrectionOffsetHeader.setCompareGroup("group");
+ queryCorrectionOffsetHeader.setFilterGroups("");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_CORRECTION_OFFSET, queryCorrectionOffsetHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ QueryCorrectionOffsetBody body = RemotingSerializable.decode(response.getBody(), QueryCorrectionOffsetBody.class);
+ Map correctionOffsets = body.getCorrectionOffsets();
+ assertThat(correctionOffsets.get(0)).isEqualTo(Long.MAX_VALUE);
+ assertThat(correctionOffsets.get(1)).isEqualTo(200L);
+ }
+
+ @Test
+ public void testNotifyMinBrokerIdChange() throws RemotingCommandException {
+ NotifyMinBrokerIdChangeRequestHeader requestHeader = new NotifyMinBrokerIdChangeRequestHeader();
+ requestHeader.setMinBrokerId(1L);
+ requestHeader.setMinBrokerAddr("127.0.0.1:10912");
+ requestHeader.setOfflineBrokerAddr("127.0.0.1:10911");
+ requestHeader.setHaBrokerAddr("");
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.NOTIFY_MIN_BROKER_ID_CHANGE, requestHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testUpdateBrokerHaInfo() throws RemotingCommandException {
+ ExchangeHAInfoResponseHeader requestHeader = new ExchangeHAInfoResponseHeader();
+ requestHeader.setMasterAddress("127.0.0.1:10911");
+ requestHeader.setMasterFlushOffset(0L);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.EXCHANGE_BROKER_HA_INFO, requestHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ when(brokerController.getMessageStore()).thenReturn(messageStore);
+ requestHeader.setMasterHaAddress("127.0.0.1:10912");
+ request = RemotingCommand.createRequestCommand(RequestCode.EXCHANGE_BROKER_HA_INFO, requestHeader);
+ request.makeCustomHeaderToNet();
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ when(messageStore.getMasterFlushedOffset()).thenReturn(0L);
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetBrokerHaStatus() throws RemotingCommandException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_BROKER_HA_STATUS,null);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ when(brokerController.getMessageStore()).thenReturn(messageStore);
+ when(messageStore.getHARuntimeInfo()).thenReturn(new HARuntimeInfo());
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testResetMasterFlushOffset() throws RemotingCommandException {
+ ResetMasterFlushOffsetHeader requestHeader = new ResetMasterFlushOffsetHeader();
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.RESET_MASTER_FLUSH_OFFSET,requestHeader);
+ RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ requestHeader.setMasterFlushOffset(0L);
+ request.makeCustomHeaderToNet();
+ response = adminBrokerProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ private ResetOffsetRequestHeader createRequestHeader(String topic,String group,long timestamp,boolean force,long offset,int queueId) {
+ ResetOffsetRequestHeader requestHeader = new ResetOffsetRequestHeader();
+ requestHeader.setTopic(topic);
+ requestHeader.setGroup(group);
+ requestHeader.setTimestamp(timestamp);
+ requestHeader.setForce(force);
+ requestHeader.setOffset(offset);
+ requestHeader.setQueueId(queueId);
+ return requestHeader;
+ }
+
private RemotingCommand buildCreateTopicRequest(String topic) {
CreateTopicRequestHeader requestHeader = new CreateTopicRequestHeader();
requestHeader.setTopic(topic);
@@ -900,12 +1318,29 @@ private RemotingCommand buildCreateTopicRequest(String topic) {
requestHeader.setReadQueueNums(8);
requestHeader.setWriteQueueNums(8);
requestHeader.setPerm(PermName.PERM_READ | PermName.PERM_WRITE);
-
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_AND_CREATE_TOPIC, requestHeader);
request.makeCustomHeaderToNet();
return request;
}
+ private RemotingCommand buildCreateTopicListRequest(List topicList) {
+ List topicConfigList = new ArrayList<>();
+ for (String topic:topicList) {
+ TopicConfig topicConfig = new TopicConfig(topic);
+ topicConfig.setReadQueueNums(8);
+ topicConfig.setWriteQueueNums(8);
+ topicConfig.setTopicFilterType(TopicFilterType.SINGLE_TAG);
+ topicConfig.setPerm(PermName.PERM_READ | PermName.PERM_WRITE);
+ topicConfig.setTopicSysFlag(0);
+ topicConfig.setOrder(false);
+ topicConfigList.add(topicConfig);
+ }
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_AND_CREATE_TOPIC_LIST, null);
+ CreateTopicListRequestBody createTopicListRequestBody = new CreateTopicListRequestBody(topicConfigList);
+ request.setBody(createTopicListRequestBody.encode());
+ return request;
+ }
+
private RemotingCommand buildDeleteTopicRequest(String topic) {
DeleteTopicRequestHeader requestHeader = new DeleteTopicRequestHeader();
requestHeader.setTopic(topic);
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessorTest.java
index c94591d381d..6b3c2578af3 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessorTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessorTest.java
@@ -16,19 +16,36 @@
*/
package org.apache.rocketmq.broker.processor;
+import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.client.ClientChannelInfo;
+import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
+import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
+import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
import org.apache.rocketmq.broker.topic.TopicConfigManager;
+import org.apache.rocketmq.broker.topic.TopicQueueMappingManager;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerListByGroupRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetResponseHeader;
+import org.apache.rocketmq.remoting.protocol.statictopic.LogicQueueMappingItem;
+import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingContext;
+import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingDetail;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.remoting.rpc.RpcClient;
+import org.apache.rocketmq.remoting.rpc.RpcException;
+import org.apache.rocketmq.remoting.rpc.RpcResponse;
import org.apache.rocketmq.store.MessageStore;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.junit.Before;
@@ -38,7 +55,17 @@
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -50,12 +77,24 @@ public class ConsumerManageProcessorTest {
private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig());
@Mock
private MessageStore messageStore;
+ @Mock
+ private Channel channel;
+ @Mock
+ private ConsumerOffsetManager consumerOffsetManager;
+ @Mock
+ private BrokerOuterAPI brokerOuterAPI;
+ @Mock
+ private RpcClient rpcClient;
+ @Mock
+ private Future responseFuture;
+ @Mock
+ private TopicQueueMappingContext mappingContext;
private String topic = "FooBar";
private String group = "FooBarGroup";
@Before
- public void init() {
+ public void init() throws RpcException {
brokerController.setMessageStore(messageStore);
TopicConfigManager topicConfigManager = new TopicConfigManager(brokerController);
topicConfigManager.getTopicConfigTable().put(topic, new TopicConfig(topic));
@@ -64,6 +103,12 @@ public void init() {
subscriptionGroupManager.getSubscriptionGroupTable().put(group, new SubscriptionGroupConfig());
when(brokerController.getSubscriptionGroupManager()).thenReturn(subscriptionGroupManager);
consumerManageProcessor = new ConsumerManageProcessor(brokerController);
+ when(brokerController.getBrokerOuterAPI()).thenReturn(brokerOuterAPI);
+ when(brokerOuterAPI.getRpcClient()).thenReturn(rpcClient);
+ when(rpcClient.invoke(any(),anyLong())).thenReturn(responseFuture);
+ TopicQueueMappingDetail topicQueueMappingDetail = new TopicQueueMappingDetail();
+ topicQueueMappingDetail.setBname("BrokerA");
+ when(mappingContext.getMappingDetail()).thenReturn(topicQueueMappingDetail);
}
@Test
@@ -82,6 +127,145 @@ public void testUpdateConsumerOffset_GroupNotExist() throws Exception {
assertThat(response.getCode()).isEqualTo(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
}
+ @Test
+ public void testUpdateConsumerOffset() throws RemotingCommandException {
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(consumerOffsetManager.hasOffsetReset(anyString(),anyString(),anyInt())).thenReturn(true);
+ RemotingCommand request = buildUpdateConsumerOffsetRequest(group, topic, 0, 0);
+ RemotingCommand response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ when(consumerOffsetManager.hasOffsetReset(anyString(),anyString(),anyInt())).thenReturn(false);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testGetConsumerListByGroup() throws RemotingCommandException {
+ GetConsumerListByGroupRequestHeader requestHeader = new GetConsumerListByGroupRequestHeader();
+ requestHeader.setConsumerGroup(group);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_CONSUMER_LIST_BY_GROUP, requestHeader);
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ brokerController.getConsumerManager().getConsumerTable().put(group,new ConsumerGroupInfo(group));
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+
+ ConsumerGroupInfo consumerGroupInfo =
+ this.brokerController.getConsumerManager().getConsumerGroupInfo(
+ requestHeader.getConsumerGroup());
+ consumerGroupInfo.getChannelInfoTable().put(channel,new ClientChannelInfo(channel));
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testQueryConsumerOffset() throws RemotingCommandException, ExecutionException, InterruptedException {
+ RemotingCommand request = buildQueryConsumerOffsetRequest(group, topic, 0, true);
+ RemotingCommand response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.QUERY_NOT_FOUND);
+
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(consumerOffsetManager.queryOffset(anyString(),anyString(),anyInt())).thenReturn(0L);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ when(consumerOffsetManager.queryOffset(anyString(),anyString(),anyInt())).thenReturn(-1L);
+ when(messageStore.getMinOffsetInQueue(anyString(),anyInt())).thenReturn(-1L);
+ when(messageStore.checkInMemByConsumeOffset(anyString(),anyInt(),anyLong(),anyInt())).thenReturn(true);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ TopicQueueMappingManager topicQueueMappingManager = mock(TopicQueueMappingManager.class);
+ when(brokerController.getTopicQueueMappingManager()).thenReturn(topicQueueMappingManager);
+ when(topicQueueMappingManager.buildTopicQueueMappingContext(any(QueryConsumerOffsetRequestHeader.class))).thenReturn(mappingContext);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.NOT_LEADER_FOR_QUEUE);
+
+ List items = new ArrayList<>();
+ LogicQueueMappingItem item1 = createLogicQueueMappingItem("BrokerC", 0, 0L, 0L);
+ items.add(item1);
+ when(mappingContext.getMappingItemList()).thenReturn(items);
+ when(mappingContext.getLeaderItem()).thenReturn(item1);
+ when(mappingContext.getCurrentItem()).thenReturn(item1);
+ when(mappingContext.isLeader()).thenReturn(true);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ LogicQueueMappingItem item2 = createLogicQueueMappingItem("BrokerA", 0, 0L, 0L);
+ items.add(item2);
+ QueryConsumerOffsetResponseHeader queryConsumerOffsetResponseHeader = new QueryConsumerOffsetResponseHeader();
+ queryConsumerOffsetResponseHeader.setOffset(0L);
+ RpcResponse rpcResponse = new RpcResponse(ResponseCode.SUCCESS,queryConsumerOffsetResponseHeader,null);
+ when(responseFuture.get()).thenReturn(rpcResponse);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+
+ queryConsumerOffsetResponseHeader.setOffset(-1L);
+ rpcResponse = new RpcResponse(ResponseCode.SUCCESS,queryConsumerOffsetResponseHeader,null);
+ when(responseFuture.get()).thenReturn(rpcResponse);
+ response = consumerManageProcessor.processRequest(handlerContext, request);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.QUERY_NOT_FOUND);
+ }
+
+ @Test
+ public void testRewriteRequestForStaticTopic() throws RpcException, ExecutionException, InterruptedException {
+ UpdateConsumerOffsetRequestHeader requestHeader = new UpdateConsumerOffsetRequestHeader();
+ requestHeader.setConsumerGroup(group);
+ requestHeader.setTopic(topic);
+ requestHeader.setQueueId(0);
+ requestHeader.setCommitOffset(0L);
+
+ RemotingCommand response = consumerManageProcessor.rewriteRequestForStaticTopic(requestHeader, mappingContext);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.NOT_LEADER_FOR_QUEUE);
+
+ List items = new ArrayList<>();
+ LogicQueueMappingItem item = createLogicQueueMappingItem("BrokerC", 0, 0L, 0L);
+ items.add(item);
+ when(mappingContext.getMappingItemList()).thenReturn(items);
+ when(mappingContext.isLeader()).thenReturn(true);
+ RpcResponse rpcResponse = new RpcResponse(ResponseCode.SUCCESS,new UpdateConsumerOffsetResponseHeader(),null);
+ when(responseFuture.get()).thenReturn(rpcResponse);
+ response = consumerManageProcessor.rewriteRequestForStaticTopic(requestHeader, mappingContext);
+ assertThat(response).isNotNull();
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ public RemotingCommand buildQueryConsumerOffsetRequest(String group, String topic, int queueId,boolean setZeroIfNotFound) {
+ QueryConsumerOffsetRequestHeader requestHeader = new QueryConsumerOffsetRequestHeader();
+ requestHeader.setConsumerGroup(group);
+ requestHeader.setTopic(topic);
+ requestHeader.setQueueId(queueId);
+ requestHeader.setSetZeroIfNotFound(setZeroIfNotFound);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_CONSUMER_OFFSET, requestHeader);
+ request.makeCustomHeaderToNet();
+ return request;
+ }
+
+ public LogicQueueMappingItem createLogicQueueMappingItem(String brokerName, int queueId, long startOffset, long logicOffset) {
+ LogicQueueMappingItem item = new LogicQueueMappingItem();
+ item.setBname(brokerName);
+ item.setQueueId(queueId);
+ item.setStartOffset(startOffset);
+ item.setLogicOffset(logicOffset);
+ return item;
+ }
+
private RemotingCommand buildUpdateConsumerOffsetRequest(String group, String topic, int queueId, long offset) {
UpdateConsumerOffsetRequestHeader requestHeader = new UpdateConsumerOffsetRequestHeader();
requestHeader.setConsumerGroup(group);
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PeekMessageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PeekMessageProcessorTest.java
new file mode 100644
index 00000000000..7f8504453ca
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PeekMessageProcessorTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.rocketmq.broker.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
+import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
+import org.apache.rocketmq.broker.topic.TopicConfigManager;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RequestCode;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.header.PeekMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.store.GetMessageStatus;
+import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.SelectMappedBufferResult;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PeekMessageProcessorTest {
+
+ private PeekMessageProcessor peekMessageProcessor;
+
+ @Spy
+ private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig());
+
+ @Mock
+ private ChannelHandlerContext handlerContext;
+
+ @Mock
+ private MessageStore messageStore;
+
+ @Mock
+ private SubscriptionGroupManager subscriptionGroupManager;
+
+ @Mock
+ private ConsumerOffsetManager consumerOffsetManager;
+
+ @Mock
+ private SubscriptionGroupConfig subscriptionGroupConfig;
+
+ @Mock
+ private Channel channel;
+
+ private TopicConfigManager topicConfigManager;
+
+ @Before
+ public void init() {
+ peekMessageProcessor = new PeekMessageProcessor(brokerController);
+ when(brokerController.getMessageStore()).thenReturn(messageStore);
+ topicConfigManager = new TopicConfigManager(brokerController);
+ when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager);
+ when(brokerController.getSubscriptionGroupManager()).thenReturn(subscriptionGroupManager);
+ when(subscriptionGroupManager.findSubscriptionGroupConfig(anyString())).thenReturn(subscriptionGroupConfig);
+ when(subscriptionGroupConfig.isConsumeEnable()).thenReturn(true);
+ topicConfigManager.getTopicConfigTable().put("topic", new TopicConfig("topic"));
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(consumerOffsetManager.queryOffset(anyString(), anyString(), anyInt())).thenReturn(-1L);
+ when(messageStore.getMinOffsetInQueue(anyString(),anyInt())).thenReturn(0L);
+ when(handlerContext.channel()).thenReturn(channel);
+ when(channel.remoteAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 12345));
+ }
+
+ @Test
+ public void testProcessRequest() throws RemotingCommandException {
+ RemotingCommand request = createPeekMessageRequest("group","topic",0);
+ GetMessageResult getMessageResult = new GetMessageResult();
+ getMessageResult.setStatus(GetMessageStatus.FOUND);
+ ByteBuffer bb = ByteBuffer.allocate(64);
+ bb.putLong(MessageDecoder.MESSAGE_STORE_TIMESTAMP_POSITION, System.currentTimeMillis());
+ SelectMappedBufferResult mappedBufferResult1 = new SelectMappedBufferResult(0, bb, 64, null);
+ for (int i = 0; i < 10;i++) {
+ getMessageResult.addMessage(mappedBufferResult1);
+ }
+ when(messageStore.getMessage(anyString(),anyString(),anyInt(),anyLong(),anyInt(),any())).thenReturn(getMessageResult);
+ RemotingCommand response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+ }
+
+ @Test
+ public void testProcessRequest_NoPermission() throws RemotingCommandException {
+ this.brokerController.getBrokerConfig().setBrokerPermission(PermName.PERM_WRITE);
+ RemotingCommand request = createPeekMessageRequest("group","topic",0);
+ RemotingCommand response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION);
+ this.brokerController.getBrokerConfig().setBrokerPermission(PermName.PERM_WRITE | PermName.PERM_READ);
+
+ topicConfigManager.getTopicConfigTable().get("topic").setPerm(PermName.PERM_WRITE);
+ response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION);
+ topicConfigManager.getTopicConfigTable().get("topic").setPerm(PermName.PERM_WRITE | PermName.PERM_READ);
+
+ when(subscriptionGroupConfig.isConsumeEnable()).thenReturn(false);
+ response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION);
+ }
+
+ @Test
+ public void testProcessRequest_TopicNotExist() throws RemotingCommandException {
+ RemotingCommand request = createPeekMessageRequest("group1","topic1",0);
+ RemotingCommand response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.TOPIC_NOT_EXIST);
+ }
+
+ @Test
+ public void testProcessRequest_SubscriptionGroupNotExist() throws RemotingCommandException {
+ when(subscriptionGroupManager.findSubscriptionGroupConfig(anyString())).thenReturn(null);
+ RemotingCommand request = createPeekMessageRequest("group","topic",0);
+ RemotingCommand response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+ }
+
+ @Test
+ public void testProcessRequest_QueueIdError() throws RemotingCommandException {
+ RemotingCommand request = createPeekMessageRequest("group","topic",17);
+ RemotingCommand response = peekMessageProcessor.processRequest(handlerContext, request);
+ assertThat(response.getCode()).isEqualTo(ResponseCode.SYSTEM_ERROR);
+ }
+
+ private RemotingCommand createPeekMessageRequest(String group,String topic,int queueId) {
+ PeekMessageRequestHeader peekMessageRequestHeader = new PeekMessageRequestHeader();
+ peekMessageRequestHeader.setConsumerGroup(group);
+ peekMessageRequestHeader.setTopic(topic);
+ peekMessageRequestHeader.setMaxMsgNums(10);
+ peekMessageRequestHeader.setQueueId(queueId);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PEEK_MESSAGE, peekMessageRequestHeader);
+ request.makeCustomHeaderToNet();
+ return request;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java
index d8c8fa1034e..8a2ce8a2ba4 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java
@@ -23,6 +23,7 @@
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ClientChannelInfo;
import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.KeyBuilder;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.constant.ConsumeInitMode;
import org.apache.rocketmq.common.message.MessageDecoder;
@@ -40,6 +41,7 @@
import org.apache.rocketmq.store.SelectMappedBufferResult;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.apache.rocketmq.store.logfile.DefaultMappedFile;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +55,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -151,14 +154,70 @@ public void testProcessRequest_whenTimerWheelIsFalse() throws RemotingCommandExc
assertThat(response.getRemark()).contains("pop message is forbidden because timerWheelEnable is false");
}
+ @Test
+ public void testGetInitOffset_retryTopic() throws RemotingCommandException {
+ when(messageStore.getMessageStoreConfig()).thenReturn(new MessageStoreConfig());
+ String newGroup = group + "-" + System.currentTimeMillis();
+ String retryTopic = KeyBuilder.buildPopRetryTopic(topic, newGroup);
+ long minOffset = 100L;
+ when(messageStore.getMinOffsetInQueue(retryTopic, 0)).thenReturn(minOffset);
+ brokerController.getTopicConfigManager().getTopicConfigTable().put(retryTopic, new TopicConfig(retryTopic, 1, 1));
+ GetMessageResult getMessageResult = createGetMessageResult(0);
+ when(messageStore.getMessageAsync(eq(newGroup), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(getMessageResult));
+
+ long offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0);
+ Assert.assertEquals(-1, offset);
+
+ RemotingCommand request = createPopMsgCommand(newGroup, topic, 0, ConsumeInitMode.MAX);
+ popMessageProcessor.processRequest(handlerContext, request);
+ offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0);
+ Assert.assertEquals(minOffset, offset);
+
+ when(messageStore.getMinOffsetInQueue(retryTopic, 0)).thenReturn(minOffset * 2);
+ popMessageProcessor.processRequest(handlerContext, request);
+ offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0);
+ Assert.assertEquals(minOffset, offset); // will not entry getInitOffset() again
+ messageStore.getMinOffsetInQueue(retryTopic, 0); // prevent UnnecessaryStubbingException
+ }
+
+ @Test
+ public void testGetInitOffset_normalTopic() throws RemotingCommandException {
+ long maxOffset = 999L;
+ when(messageStore.getMessageStoreConfig()).thenReturn(new MessageStoreConfig());
+ when(messageStore.getMaxOffsetInQueue(topic, 0)).thenReturn(maxOffset);
+ String newGroup = group + "-" + System.currentTimeMillis();
+ GetMessageResult getMessageResult = createGetMessageResult(0);
+ when(messageStore.getMessageAsync(eq(newGroup), anyString(), anyInt(), anyLong(), anyInt(), any()))
+ .thenReturn(CompletableFuture.completedFuture(getMessageResult));
+
+ long offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0);
+ Assert.assertEquals(-1, offset);
+
+ RemotingCommand request = createPopMsgCommand(newGroup, topic, 0, ConsumeInitMode.MAX);
+ popMessageProcessor.processRequest(handlerContext, request);
+ offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0);
+ Assert.assertEquals(maxOffset - 1, offset); // checkInMem return false
+
+ when(messageStore.getMaxOffsetInQueue(topic, 0)).thenReturn(maxOffset * 2);
+ popMessageProcessor.processRequest(handlerContext, request);
+ offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0);
+ Assert.assertEquals(maxOffset - 1, offset); // will not entry getInitOffset() again
+ messageStore.getMaxOffsetInQueue(topic, 0); // prevent UnnecessaryStubbingException
+ }
+
private RemotingCommand createPopMsgCommand() {
+ return createPopMsgCommand(group, topic, -1, ConsumeInitMode.MAX);
+ }
+
+ private RemotingCommand createPopMsgCommand(String group, String topic, int queueId, int initMode) {
PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
requestHeader.setConsumerGroup(group);
requestHeader.setMaxMsgNums(30);
- requestHeader.setQueueId(-1);
+ requestHeader.setQueueId(queueId);
requestHeader.setTopic(topic);
requestHeader.setInvisibleTime(10_000);
- requestHeader.setInitMode(ConsumeInitMode.MAX);
+ requestHeader.setInitMode(initMode);
requestHeader.setOrder(false);
requestHeader.setPollTime(15_000);
requestHeader.setBornTime(System.currentTimeMillis());
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java
index 78b76264fef..3010e836101 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java
@@ -20,12 +20,17 @@
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.failover.EscapeBridge;
import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
import org.apache.rocketmq.broker.topic.TopicConfigManager;
import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.KeyBuilder;
import org.apache.rocketmq.common.PopAckConstants;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.message.MessageConst;
@@ -36,9 +41,14 @@
import org.apache.rocketmq.common.utils.NetworkUtil;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.PutMessageResult;
+import org.apache.rocketmq.store.PutMessageStatus;
+import org.apache.rocketmq.store.AppendMessageResult;
+import org.apache.rocketmq.store.AppendMessageStatus;
import org.apache.rocketmq.store.pop.AckMsg;
import org.apache.rocketmq.store.pop.PopCheckPoint;
import org.apache.rocketmq.store.timer.TimerMessageStore;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,19 +60,25 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.Silent.class)
public class PopReviveServiceTest {
- private static final String REVIVE_TOPIC = PopAckConstants.REVIVE_TOPIC + "test";
+ private static final String CLUSTER_NAME = "test";
+ private static final String REVIVE_TOPIC = PopAckConstants.buildClusterReviveTopic(CLUSTER_NAME);
private static final int REVIVE_QUEUE_ID = 0;
private static final String GROUP = "group";
private static final String TOPIC = "topic";
private static final SocketAddress STORE_HOST = NetworkUtil.string2SocketAddress("127.0.0.1:8080");
+ private static final Long INVISIBLE_TIME = 1000L;
@Mock
private MessageStore messageStore;
@@ -76,6 +92,9 @@ public class PopReviveServiceTest {
private SubscriptionGroupManager subscriptionGroupManager;
@Mock
private BrokerController brokerController;
+ @Mock
+ private EscapeBridge escapeBridge;
+ private PopMessageProcessor popMessageProcessor;
private BrokerConfig brokerConfig;
private PopReviveService popReviveService;
@@ -83,12 +102,13 @@ public class PopReviveServiceTest {
@Before
public void before() {
brokerConfig = new BrokerConfig();
-
+ brokerConfig.setBrokerClusterName(CLUSTER_NAME);
when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
when(brokerController.getMessageStore()).thenReturn(messageStore);
when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager);
when(brokerController.getSubscriptionGroupManager()).thenReturn(subscriptionGroupManager);
+ when(brokerController.getEscapeBridge()).thenReturn(escapeBridge);
when(messageStore.getTimerMessageStore()).thenReturn(timerMessageStore);
when(timerMessageStore.getDequeueBehind()).thenReturn(0L);
when(timerMessageStore.getEnqueueBehind()).thenReturn(0L);
@@ -96,6 +116,9 @@ public void before() {
when(topicConfigManager.selectTopicConfig(anyString())).thenReturn(new TopicConfig());
when(subscriptionGroupManager.findSubscriptionGroupConfig(anyString())).thenReturn(new SubscriptionGroupConfig());
+ popMessageProcessor = new PopMessageProcessor(brokerController); // a real one, not mock
+ when(brokerController.getPopMessageProcessor()).thenReturn(popMessageProcessor);
+
popReviveService = spy(new PopReviveService(brokerController, REVIVE_TOPIC, REVIVE_QUEUE_ID));
popReviveService.setShouldRunPopRevive(true);
}
@@ -204,6 +227,184 @@ public void testSkipLongWaiteAckWithSameAck() throws Throwable {
assertEquals(maxReviveOffset, commitOffsetCaptor.getValue().longValue());
}
+ @Test
+ public void testReviveMsgFromCk_messageFound_writeRetryOK() throws Throwable {
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+ StringBuilder actualRetryTopic = new StringBuilder();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(new MessageExt(), "", false)));
+ when(escapeBridge.putMessageToSpecificQueue(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualRetryTopic.append(msg.getTopic());
+ return new PutMessageResult(PutMessageStatus.PUT_OK, new AppendMessageResult(AppendMessageStatus.PUT_OK));
+ });
+
+ popReviveService.mergeAndRevive(reviveObj);
+ Assert.assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, GROUP, false), actualRetryTopic.toString());
+ verify(escapeBridge, times(1)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(0)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageFound_writeRetryFailed_rewriteCK() throws Throwable {
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+ StringBuilder actualRetryTopic = new StringBuilder();
+ StringBuilder actualReviveTopic = new StringBuilder();
+ AtomicLong actualInvisibleTime = new AtomicLong(0L);
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(new MessageExt(), "", false)));
+ when(escapeBridge.putMessageToSpecificQueue(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualRetryTopic.append(msg.getTopic());
+ return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(AppendMessageStatus.MESSAGE_SIZE_EXCEEDED));
+ });
+ when(messageStore.putMessage(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualReviveTopic.append(msg.getTopic());
+ PopCheckPoint rewriteCK = JSON.parseObject(msg.getBody(), PopCheckPoint.class);
+ actualInvisibleTime.set(rewriteCK.getReviveTime());
+ return new PutMessageResult(PutMessageStatus.PUT_OK, new AppendMessageResult(AppendMessageStatus.PUT_OK));
+ });
+
+ popReviveService.mergeAndRevive(reviveObj);
+ Assert.assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, GROUP, false), actualRetryTopic.toString());
+ Assert.assertEquals(REVIVE_TOPIC, actualReviveTopic.toString());
+ Assert.assertEquals(INVISIBLE_TIME + 10 * 1000L, actualInvisibleTime.get()); // first interval is 10s
+ verify(escapeBridge, times(1)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(1)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageFound_writeRetryFailed_rewriteCK_end() throws Throwable {
+ brokerConfig.setSkipWhenCKRePutReachMaxTimes(true);
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ ck.setRePutTimes("17");
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+ StringBuilder actualRetryTopic = new StringBuilder();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(new MessageExt(), "", false)));
+ when(escapeBridge.putMessageToSpecificQueue(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualRetryTopic.append(msg.getTopic());
+ return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(AppendMessageStatus.MESSAGE_SIZE_EXCEEDED));
+ });
+
+ popReviveService.mergeAndRevive(reviveObj);
+ Assert.assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, GROUP, false), actualRetryTopic.toString());
+ verify(escapeBridge, times(1)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(0)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageFound_writeRetryFailed_rewriteCK_noEnd() throws Throwable {
+ brokerConfig.setSkipWhenCKRePutReachMaxTimes(false);
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ ck.setRePutTimes(Byte.MAX_VALUE + "");
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+ StringBuilder actualRetryTopic = new StringBuilder();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(new MessageExt(), "", false)));
+ when(escapeBridge.putMessageToSpecificQueue(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualRetryTopic.append(msg.getTopic());
+ return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(AppendMessageStatus.MESSAGE_SIZE_EXCEEDED));
+ });
+
+ popReviveService.mergeAndRevive(reviveObj);
+ Assert.assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, GROUP, false), actualRetryTopic.toString());
+ verify(escapeBridge, times(1)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(1)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageNotFound_noRetry() throws Throwable {
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(null, "", false)));
+
+ popReviveService.mergeAndRevive(reviveObj);
+ verify(escapeBridge, times(0)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(0)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageNotFound_needRetry() throws Throwable {
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+ StringBuilder actualReviveTopic = new StringBuilder();
+ AtomicLong actualInvisibleTime = new AtomicLong(0L);
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(null, "", true)));
+ when(messageStore.putMessage(any(MessageExtBrokerInner.class))).thenAnswer(invocation -> {
+ MessageExtBrokerInner msg = invocation.getArgument(0);
+ actualReviveTopic.append(msg.getTopic());
+ PopCheckPoint rewriteCK = JSON.parseObject(msg.getBody(), PopCheckPoint.class);
+ actualInvisibleTime.set(rewriteCK.getReviveTime());
+ return new PutMessageResult(PutMessageStatus.PUT_OK, new AppendMessageResult(AppendMessageStatus.PUT_OK));
+ });
+
+ popReviveService.mergeAndRevive(reviveObj);
+ Assert.assertEquals(REVIVE_TOPIC, actualReviveTopic.toString());
+ Assert.assertEquals(INVISIBLE_TIME + 10 * 1000L, actualInvisibleTime.get()); // first interval is 10s
+ verify(escapeBridge, times(0)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(1)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageNotFound_needRetry_end() throws Throwable {
+ brokerConfig.setSkipWhenCKRePutReachMaxTimes(true);
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ ck.setRePutTimes("17");
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(null, "", true)));
+
+ popReviveService.mergeAndRevive(reviveObj);
+ verify(escapeBridge, times(0)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(0)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
+ @Test
+ public void testReviveMsgFromCk_messageNotFound_needRetry_noEnd() throws Throwable {
+ brokerConfig.setSkipWhenCKRePutReachMaxTimes(false);
+ PopCheckPoint ck = buildPopCheckPoint(0, 0, 0);
+ ck.setRePutTimes(Byte.MAX_VALUE + "");
+ PopReviveService.ConsumeReviveObj reviveObj = new PopReviveService.ConsumeReviveObj();
+ reviveObj.map.put("", ck);
+ reviveObj.endTime = System.currentTimeMillis();
+
+ when(escapeBridge.getMessageAsync(anyString(), anyLong(), anyInt(), anyString(), anyBoolean()))
+ .thenReturn(CompletableFuture.completedFuture(Triple.of(null, "", true)));
+
+ popReviveService.mergeAndRevive(reviveObj);
+ verify(escapeBridge, times(0)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); // write retry
+ verify(messageStore, times(1)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK
+ }
+
public static PopCheckPoint buildPopCheckPoint(long startOffset, long popTime, long reviveOffset) {
PopCheckPoint ck = new PopCheckPoint();
ck.setStartOffset(startOffset);
@@ -214,7 +415,8 @@ public static PopCheckPoint buildPopCheckPoint(long startOffset, long popTime, l
ck.setNum((byte) 1);
ck.setBitMap(0);
ck.setReviveOffset(reviveOffset);
- ck.setInvisibleTime(1000);
+ ck.setInvisibleTime(INVISIBLE_TIME);
+ ck.setBrokerName("broker-a");
return ck;
}
@@ -226,6 +428,7 @@ public static AckMsg buildAckMsg(long offset, long popTime) {
ackMsg.setTopic(TOPIC);
ackMsg.setQueueId(0);
ackMsg.setPopTime(popTime);
+ ackMsg.setBrokerName("broker-a");
return ackMsg;
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessorTest.java
index e91c1a09617..67ff74897ef 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessorTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryAssignmentProcessorTest.java
@@ -19,8 +19,10 @@
import com.google.common.collect.ImmutableSet;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ClientChannelInfo;
import org.apache.rocketmq.broker.topic.TopicRouteInfoManager;
@@ -126,6 +128,24 @@ public void testSetMessageRequestMode_RetryTopic() throws Exception {
assertThat(responseToReturn.getCode()).isEqualTo(ResponseCode.NO_PERMISSION);
}
+ @Test
+ public void testDoLoadBalance() throws Exception {
+ Method method = queryAssignmentProcessor.getClass()
+ .getDeclaredMethod("doLoadBalance", String.class, String.class, String.class, MessageModel.class,
+ String.class, SetMessageRequestModeRequestBody.class, ChannelHandlerContext.class);
+ method.setAccessible(true);
+
+ Set mqs1 = (Set) method.invoke(
+ queryAssignmentProcessor, MixAll.LMQ_PREFIX + topic, group, "127.0.0.1", MessageModel.CLUSTERING,
+ new AllocateMessageQueueAveragely().getName(), new SetMessageRequestModeRequestBody(), handlerContext);
+ Set mqs2 = (Set) method.invoke(
+ queryAssignmentProcessor, MixAll.LMQ_PREFIX + topic, group, "127.0.0.2", MessageModel.CLUSTERING,
+ new AllocateMessageQueueAveragely().getName(), new SetMessageRequestModeRequestBody(), handlerContext);
+
+ assertThat(mqs1).hasSize(1);
+ assertThat(mqs2).isEmpty();
+ }
+
@Test
public void testAllocate4Pop() {
testAllocate4Pop(new AllocateMessageQueueAveragely());
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryMessageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryMessageProcessorTest.java
new file mode 100644
index 00000000000..0fd54df7d8a
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/QueryMessageProcessorTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.rocketmq.broker.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RequestCode;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.header.QueryMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ViewMessageRequestHeader;
+import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.QueryMessageResult;
+import org.apache.rocketmq.store.SelectMappedBufferResult;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.HashMap;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class QueryMessageProcessorTest {
+ private QueryMessageProcessor queryMessageProcessor;
+ @Spy
+ private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig());
+
+ @Mock
+ private MessageStore messageStore;
+
+ @Mock
+ private ChannelHandlerContext handlerContext;
+
+ @Mock
+ private Channel channel;
+
+ @Mock
+ private ChannelFuture channelFuture;
+
+ @Before
+ public void init() {
+ when(handlerContext.channel()).thenReturn(channel);
+ queryMessageProcessor = new QueryMessageProcessor(brokerController);
+ when(brokerController.getMessageStore()).thenReturn(messageStore);
+ when(channel.writeAndFlush(any())).thenReturn(channelFuture);
+ }
+
+ @Test
+ public void testQueryMessage() throws RemotingCommandException {
+ QueryMessageResult result = new QueryMessageResult();
+ result.setIndexLastUpdateTimestamp(100);
+ result.setIndexLastUpdatePhyoffset(0);
+ result.addMessage(new SelectMappedBufferResult(0, null, 0, null));
+
+ when(messageStore.queryMessage(anyString(),anyString(),anyInt(),anyLong(),anyLong())).thenReturn(result);
+ RemotingCommand request = createQueryMessageRequest("topic", "msgKey", 1, 100, 200,"false");
+ request.makeCustomHeaderToNet();
+ RemotingCommand response = queryMessageProcessor.processRequest(handlerContext, request);
+ Assert.assertEquals(response.getCode(), ResponseCode.QUERY_NOT_FOUND);
+
+ result.addMessage(new SelectMappedBufferResult(0, null, 1, null));
+ when(messageStore.queryMessage(anyString(),anyString(),anyInt(),anyLong(),anyLong())).thenReturn(result);
+ response = queryMessageProcessor.processRequest(handlerContext, request);
+ Assert.assertNull(response);
+ }
+
+ @Test
+ public void testViewMessageById() throws RemotingCommandException {
+ ViewMessageRequestHeader viewMessageRequestHeader = new ViewMessageRequestHeader();
+ viewMessageRequestHeader.setTopic("topic");
+ viewMessageRequestHeader.setOffset(0L);
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.VIEW_MESSAGE_BY_ID, viewMessageRequestHeader);
+ request.makeCustomHeaderToNet();
+ request.setCode(RequestCode.VIEW_MESSAGE_BY_ID);
+
+ when(messageStore.selectOneMessageByOffset(anyLong())).thenReturn(null);
+ RemotingCommand response = queryMessageProcessor.processRequest(handlerContext, request);
+ Assert.assertEquals(response.getCode(), ResponseCode.SYSTEM_ERROR);
+
+ when(messageStore.selectOneMessageByOffset(anyLong())).thenReturn(new SelectMappedBufferResult(0, null, 0, null));
+ response = queryMessageProcessor.processRequest(handlerContext, request);
+ Assert.assertNull(response);
+ }
+
+ private RemotingCommand createQueryMessageRequest(String topic, String key, int maxNum, long beginTimestamp, long endTimestamp,String flag) {
+ QueryMessageRequestHeader requestHeader = new QueryMessageRequestHeader();
+ requestHeader.setTopic(topic);
+ requestHeader.setKey(key);
+ requestHeader.setMaxNum(maxNum);
+ requestHeader.setBeginTimestamp(beginTimestamp);
+ requestHeader.setEndTimestamp(endTimestamp);
+
+ HashMap extFields = new HashMap<>();
+ extFields.put(MixAll.UNIQUE_MSG_QUERY_FLAG, flag);
+
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_MESSAGE, requestHeader);
+ request.setExtFields(extFields);
+ return request;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/slave/SlaveSynchronizeTest.java b/broker/src/test/java/org/apache/rocketmq/broker/slave/SlaveSynchronizeTest.java
new file mode 100644
index 00000000000..95db733d0d1
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/slave/SlaveSynchronizeTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.rocketmq.broker.slave;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.loadbalance.MessageRequestModeManager;
+import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
+import org.apache.rocketmq.broker.out.BrokerOuterAPI;
+import org.apache.rocketmq.broker.processor.QueryAssignmentProcessor;
+import org.apache.rocketmq.broker.schedule.ScheduleMessageService;
+import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
+import org.apache.rocketmq.broker.topic.TopicConfigManager;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.remoting.exception.RemotingConnectException;
+import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
+import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerOffsetSerializeWrapper;
+import org.apache.rocketmq.remoting.protocol.body.MessageRequestModeSerializeWrapper;
+import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
+import org.apache.rocketmq.remoting.protocol.body.TopicConfigAndMappingSerializeWrapper;
+import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.apache.rocketmq.store.timer.TimerCheckpoint;
+import org.apache.rocketmq.store.timer.TimerMessageStore;
+import org.apache.rocketmq.store.timer.TimerMetrics;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SlaveSynchronizeTest {
+ @Spy
+ private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig());
+
+ private SlaveSynchronize slaveSynchronize;
+
+ @Mock
+ private BrokerOuterAPI brokerOuterAPI;
+
+ @Mock
+ private TopicConfigManager topicConfigManager;
+
+ @Mock
+ private ConsumerOffsetManager consumerOffsetManager;
+
+ @Mock
+ private MessageStoreConfig messageStoreConfig;
+
+ @Mock
+ private MessageStore messageStore;
+
+ @Mock
+ private ScheduleMessageService scheduleMessageService;
+
+ @Mock
+ private SubscriptionGroupManager subscriptionGroupManager;
+
+ @Mock
+ private QueryAssignmentProcessor queryAssignmentProcessor;
+
+ @Mock
+ private MessageRequestModeManager messageRequestModeManager;
+
+ @Mock
+ private TimerMessageStore timerMessageStore;
+
+ @Mock
+ private TimerMetrics timerMetrics;
+
+ @Mock
+ private TimerCheckpoint timerCheckpoint;
+
+ private static final String BROKER_ADDR = "127.0.0.1:10911";
+
+ @Before
+ public void init() {
+ when(brokerController.getBrokerOuterAPI()).thenReturn(brokerOuterAPI);
+ when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager);
+ when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig);
+ when(brokerController.getScheduleMessageService()).thenReturn(scheduleMessageService);
+ when(brokerController.getSubscriptionGroupManager()).thenReturn(subscriptionGroupManager);
+ when(brokerController.getQueryAssignmentProcessor()).thenReturn(queryAssignmentProcessor);
+ when(brokerController.getMessageStore()).thenReturn(messageStore);
+ when(brokerController.getTimerMessageStore()).thenReturn(timerMessageStore);
+ when(brokerController.getTimerCheckpoint()).thenReturn(timerCheckpoint);
+ when(topicConfigManager.getDataVersion()).thenReturn(new DataVersion());
+ when(topicConfigManager.getTopicConfigTable()).thenReturn(new ConcurrentHashMap<>());
+ when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager);
+ when(consumerOffsetManager.getOffsetTable()).thenReturn(new ConcurrentHashMap<>());
+ when(consumerOffsetManager.getDataVersion()).thenReturn(new DataVersion());
+ when(subscriptionGroupManager.getDataVersion()).thenReturn(new DataVersion());
+ when(subscriptionGroupManager.getSubscriptionGroupTable()).thenReturn(new ConcurrentHashMap<>());
+ when(queryAssignmentProcessor.getMessageRequestModeManager()).thenReturn(messageRequestModeManager);
+ when(messageRequestModeManager.getMessageRequestModeMap()).thenReturn(new ConcurrentHashMap<>());
+ when(messageStoreConfig.isTimerWheelEnable()).thenReturn(true);
+ when(messageStore.getTimerMessageStore()).thenReturn(timerMessageStore);
+ when(timerMessageStore.isShouldRunningDequeue()).thenReturn(false);
+ when(timerMessageStore.getTimerMetrics()).thenReturn(timerMetrics);
+ when(timerMetrics.getDataVersion()).thenReturn(new DataVersion());
+ when(timerCheckpoint.getDataVersion()).thenReturn(new DataVersion());
+ slaveSynchronize = new SlaveSynchronize(brokerController);
+ slaveSynchronize.setMasterAddr(BROKER_ADDR);
+ }
+
+ @Test
+ public void testSyncAll() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException, UnsupportedEncodingException {
+ TopicConfig newTopicConfig = new TopicConfig("NewTopic");
+ when(brokerOuterAPI.getAllTopicConfig(anyString())).thenReturn(createTopicConfigWrapper(newTopicConfig));
+ when(brokerOuterAPI.getAllConsumerOffset(anyString())).thenReturn(createConsumerOffsetWrapper());
+ when(brokerOuterAPI.getAllDelayOffset(anyString())).thenReturn("");
+ when(brokerOuterAPI.getAllSubscriptionGroupConfig(anyString())).thenReturn(createSubscriptionGroupWrapper());
+ when(brokerOuterAPI.getAllMessageRequestMode(anyString())).thenReturn(createMessageRequestModeWrapper());
+ when(brokerOuterAPI.getTimerMetrics(anyString())).thenReturn(createTimerMetricsWrapper());
+ slaveSynchronize.syncAll();
+ Assert.assertEquals(1, this.brokerController.getTopicConfigManager().getDataVersion().getStateVersion());
+ Assert.assertEquals(1, this.brokerController.getTopicQueueMappingManager().getDataVersion().getStateVersion());
+ Assert.assertEquals(1, consumerOffsetManager.getDataVersion().getStateVersion());
+ Assert.assertEquals(1, subscriptionGroupManager.getDataVersion().getStateVersion());
+ Assert.assertEquals(1, timerMetrics.getDataVersion().getStateVersion());
+ }
+
+ @Test
+ public void testGetMasterAddr() {
+ Assert.assertEquals(BROKER_ADDR, slaveSynchronize.getMasterAddr());
+ }
+
+ @Test
+ public void testSyncTimerCheckPoint() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException {
+ when(brokerOuterAPI.getTimerCheckPoint(anyString())).thenReturn(timerCheckpoint);
+ slaveSynchronize.syncTimerCheckPoint();
+ Assert.assertEquals(0, timerCheckpoint.getDataVersion().getStateVersion());
+ }
+
+ private TopicConfigAndMappingSerializeWrapper createTopicConfigWrapper(TopicConfig topicConfig) {
+ TopicConfigAndMappingSerializeWrapper wrapper = new TopicConfigAndMappingSerializeWrapper();
+ wrapper.setTopicConfigTable(new ConcurrentHashMap<>());
+ wrapper.getTopicConfigTable().put(topicConfig.getTopicName(), topicConfig);
+ DataVersion dataVersion = new DataVersion();
+ dataVersion.setStateVersion(1L);
+ wrapper.setDataVersion(dataVersion);
+ wrapper.setMappingDataVersion(dataVersion);
+ return wrapper;
+ }
+
+ private ConsumerOffsetSerializeWrapper createConsumerOffsetWrapper() {
+ ConsumerOffsetSerializeWrapper wrapper = new ConsumerOffsetSerializeWrapper();
+ wrapper.setOffsetTable(new ConcurrentHashMap<>());
+ DataVersion dataVersion = new DataVersion();
+ dataVersion.setStateVersion(1L);
+ wrapper.setDataVersion(dataVersion);
+ return wrapper;
+ }
+
+ private SubscriptionGroupWrapper createSubscriptionGroupWrapper() {
+ SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
+ wrapper.setSubscriptionGroupTable(new ConcurrentHashMap<>());
+ DataVersion dataVersion = new DataVersion();
+ dataVersion.setStateVersion(1L);
+ wrapper.setDataVersion(dataVersion);
+ return wrapper;
+ }
+
+ private MessageRequestModeSerializeWrapper createMessageRequestModeWrapper() {
+ MessageRequestModeSerializeWrapper wrapper = new MessageRequestModeSerializeWrapper();
+ wrapper.setMessageRequestModeMap(new ConcurrentHashMap<>());
+ return wrapper;
+ }
+
+ private TimerMetrics.TimerMetricsSerializeWrapper createTimerMetricsWrapper() {
+ TimerMetrics.TimerMetricsSerializeWrapper wrapper = new TimerMetrics.TimerMetricsSerializeWrapper();
+ wrapper.setTimingCount(new ConcurrentHashMap<>());
+ DataVersion dataVersion = new DataVersion();
+ dataVersion.setStateVersion(1L);
+ wrapper.setDataVersion(dataVersion);
+ return wrapper;
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/subscription/RocksdbGroupConfigTransferTest.java b/broker/src/test/java/org/apache/rocketmq/broker/subscription/RocksdbGroupConfigTransferTest.java
new file mode 100644
index 00000000000..205e642843b
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/subscription/RocksdbGroupConfigTransferTest.java
@@ -0,0 +1,340 @@
+/*
+ * 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.rocketmq.broker.subscription;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
+import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.store.DefaultMessageStore;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RocksdbGroupConfigTransferTest {
+ private final String basePath = Paths.get(System.getProperty("user.home"),
+ "unit-test-store", UUID.randomUUID().toString().substring(0, 16).toUpperCase()).toString();
+
+ private RocksDBSubscriptionGroupManager rocksDBSubscriptionGroupManager;
+
+ private SubscriptionGroupManager jsonSubscriptionGroupManager;
+ @Mock
+ private BrokerController brokerController;
+
+ @Mock
+ private DefaultMessageStore defaultMessageStore;
+
+ @Before
+ public void init() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ BrokerConfig brokerConfig = new BrokerConfig();
+ Mockito.lenient().when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setStorePathRootDir(basePath);
+ messageStoreConfig.setTransferMetadataJsonToRocksdb(true);
+ Mockito.lenient().when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig);
+ Mockito.lenient().when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
+ when(defaultMessageStore.getStateMachineVersion()).thenReturn(0L);
+ }
+
+ @After
+ public void destroy() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ Path pathToBeDeleted = Paths.get(basePath);
+
+ try {
+ Files.walk(pathToBeDeleted)
+ .sorted(Comparator.reverseOrder())
+ .forEach(path -> {
+ try {
+ Files.delete(path);
+ } catch (IOException e) {
+ // ignore
+ }
+ });
+ } catch (IOException e) {
+ // ignore
+ }
+ if (rocksDBSubscriptionGroupManager != null) {
+ rocksDBSubscriptionGroupManager.stop();
+ }
+ }
+
+
+ public void initRocksDBSubscriptionGroupManager() {
+ if (rocksDBSubscriptionGroupManager == null) {
+ rocksDBSubscriptionGroupManager = new RocksDBSubscriptionGroupManager(brokerController);
+ rocksDBSubscriptionGroupManager.load();
+ }
+ }
+
+ public void initJsonSubscriptionGroupManager() {
+ if (jsonSubscriptionGroupManager == null) {
+ jsonSubscriptionGroupManager = new SubscriptionGroupManager(brokerController);
+ jsonSubscriptionGroupManager.load();
+ }
+ }
+
+ @Test
+ public void theFirstTimeLoadJsonSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initJsonSubscriptionGroupManager();
+ DataVersion dataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(0L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, jsonSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ }
+
+ @Test
+ public void theFirstTimeLoadRocksDBSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initRocksDBSubscriptionGroupManager();
+ DataVersion dataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(0L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ }
+
+
+ @Test
+ public void addGroupLoadJsonSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initJsonSubscriptionGroupManager();
+ int beforeSize = jsonSubscriptionGroupManager.getSubscriptionGroupTable().size();
+ String groupName = "testAddGroupConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ SubscriptionGroupConfig subscriptionGroupConfig = new SubscriptionGroupConfig();
+ subscriptionGroupConfig.setGroupName(groupName);
+ subscriptionGroupConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ jsonSubscriptionGroupManager.updateSubscriptionGroupConfig(subscriptionGroupConfig);
+
+ int afterSize = jsonSubscriptionGroupManager.getSubscriptionGroupTable().size();
+ DataVersion afterDataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+
+ Assert.assertEquals(0, beforeDataVersionCounter);
+ Assert.assertEquals(1, afterDataVersionCounter);
+ Assert.assertEquals(1, afterSize - beforeSize);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ }
+
+ @Test
+ public void addForbiddenGroupLoadJsonSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initJsonSubscriptionGroupManager();
+ int beforeSize = jsonSubscriptionGroupManager.getForbiddenTable().size();
+ String groupName = "testAddGroupConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ SubscriptionGroupConfig subscriptionGroupConfig = new SubscriptionGroupConfig();
+ subscriptionGroupConfig.setGroupName(groupName);
+ subscriptionGroupConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ jsonSubscriptionGroupManager.setForbidden(groupName, "topic", 0);
+ int afterSize = jsonSubscriptionGroupManager.getForbiddenTable().size();
+ DataVersion afterDataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+
+ Assert.assertEquals(1, afterDataVersionCounter - beforeDataVersionCounter);
+ Assert.assertEquals(1, afterSize - beforeSize);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ }
+
+ @Test
+ public void addGroupLoadRocksdbSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initRocksDBSubscriptionGroupManager();
+ int beforeSize = rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size();
+ String groupName = "testAddGroupConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ SubscriptionGroupConfig subscriptionGroupConfig = new SubscriptionGroupConfig();
+ subscriptionGroupConfig.setGroupName(groupName);
+ subscriptionGroupConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ rocksDBSubscriptionGroupManager.updateSubscriptionGroupConfig(subscriptionGroupConfig);
+ int afterSize = rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size();
+ DataVersion afterDataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+ Assert.assertEquals(1, afterDataVersionCounter);
+ Assert.assertEquals(0, beforeDataVersionCounter);
+ Assert.assertEquals(1, afterSize - beforeSize);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ }
+
+ @Test
+ public void addForbiddenLoadRocksdbSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initRocksDBSubscriptionGroupManager();
+ int beforeSize = rocksDBSubscriptionGroupManager.getForbiddenTable().size();
+ String groupName = "testAddGroupConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ SubscriptionGroupConfig subscriptionGroupConfig = new SubscriptionGroupConfig();
+ subscriptionGroupConfig.setGroupName(groupName);
+ subscriptionGroupConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ rocksDBSubscriptionGroupManager.updateForbidden(groupName, "topic", 0, true);
+
+ int afterSize = rocksDBSubscriptionGroupManager.getForbiddenTable().size();
+ DataVersion afterDataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+ Assert.assertEquals(1, afterDataVersionCounter - beforeDataVersionCounter);
+ Assert.assertEquals(1, afterSize - beforeSize);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ }
+
+ @Test
+ public void theSecondTimeLoadJsonSubscriptionGroupManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addGroupLoadJsonSubscriptionGroupManager();
+ jsonSubscriptionGroupManager.stop();
+ rocksDBSubscriptionGroupManager = null;
+ addForbiddenGroupLoadJsonSubscriptionGroupManager();
+ jsonSubscriptionGroupManager.stop();
+ rocksDBSubscriptionGroupManager = null;
+ jsonSubscriptionGroupManager = new SubscriptionGroupManager(brokerController);
+ jsonSubscriptionGroupManager.load();
+ DataVersion dataVersion = jsonSubscriptionGroupManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(2L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, jsonSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertNotEquals(0, jsonSubscriptionGroupManager.getForbiddenTable().size());
+ Assert.assertNotEquals(0, jsonSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ }
+
+ @Test
+ public void theSecondTimeLoadRocksdbTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addGroupLoadRocksdbSubscriptionGroupManager();
+ rocksDBSubscriptionGroupManager.stop();
+ rocksDBSubscriptionGroupManager = null;
+ addForbiddenLoadRocksdbSubscriptionGroupManager();
+ rocksDBSubscriptionGroupManager.stop();
+ rocksDBSubscriptionGroupManager = null;
+ rocksDBSubscriptionGroupManager = new RocksDBSubscriptionGroupManager(brokerController);
+ rocksDBSubscriptionGroupManager.load();
+ DataVersion dataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(2L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getForbiddenTable().size());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ }
+
+
+ @Test
+ public void jsonUpgradeToRocksdb() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addGroupLoadJsonSubscriptionGroupManager();
+ addForbiddenGroupLoadJsonSubscriptionGroupManager();
+ initRocksDBSubscriptionGroupManager();
+ DataVersion dataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(3L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getForbiddenTable().size());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertEquals(rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size(), jsonSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertEquals(rocksDBSubscriptionGroupManager.getForbiddenTable().size(), jsonSubscriptionGroupManager.getForbiddenTable().size());
+
+ rocksDBSubscriptionGroupManager.stop();
+ rocksDBSubscriptionGroupManager = new RocksDBSubscriptionGroupManager(brokerController);
+ rocksDBSubscriptionGroupManager.load();
+ dataVersion = rocksDBSubscriptionGroupManager.getDataVersion();
+ Assert.assertEquals(3L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getForbiddenTable().size());
+ Assert.assertNotEquals(0, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertEquals(rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().size(), jsonSubscriptionGroupManager.getSubscriptionGroupTable().size());
+ Assert.assertEquals(rocksDBSubscriptionGroupManager.getForbiddenTable().size(), jsonSubscriptionGroupManager.getForbiddenTable().size());
+ }
+
+ private boolean notToBeExecuted() {
+ return MixAll.isMac();
+ }
+
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java
index 3c829437cf1..3384d479c6e 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java
@@ -18,7 +18,12 @@
package org.apache.rocketmq.broker.subscription;
import com.google.common.collect.ImmutableMap;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.UUID;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.SubscriptionGroupAttributes;
@@ -30,36 +35,46 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
@RunWith(MockitoJUnitRunner.class)
public class SubscriptionGroupManagerTest {
private String group = "group";
+
+ private final String basePath = Paths.get(System.getProperty("user.home"),
+ "unit-test-store", UUID.randomUUID().toString().substring(0, 16).toUpperCase()).toString();
@Mock
private BrokerController brokerControllerMock;
private SubscriptionGroupManager subscriptionGroupManager;
@Before
public void before() {
+ if (notToBeExecuted()) {
+ return;
+ }
SubscriptionGroupAttributes.ALL.put("test", new BooleanAttribute(
"test",
false,
false
));
subscriptionGroupManager = spy(new SubscriptionGroupManager(brokerControllerMock));
- when(brokerControllerMock.getMessageStore()).thenReturn(null);
- doNothing().when(subscriptionGroupManager).persist();
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setStorePathRootDir(basePath);
+ Mockito.lenient().when(brokerControllerMock.getMessageStoreConfig()).thenReturn(messageStoreConfig);
}
@After
public void destroy() {
- if (MixAll.isMac()) {
+ if (notToBeExecuted()) {
return;
}
if (subscriptionGroupManager != null) {
@@ -69,18 +84,18 @@ public void destroy() {
@Test
public void testUpdateAndCreateSubscriptionGroupInRocksdb() {
- if (MixAll.isMac()) {
+ if (notToBeExecuted()) {
return;
}
- when(brokerControllerMock.getMessageStoreConfig()).thenReturn(new MessageStoreConfig());
- subscriptionGroupManager = spy(new RocksDBSubscriptionGroupManager(brokerControllerMock));
- subscriptionGroupManager.load();
group += System.currentTimeMillis();
updateSubscriptionGroupConfig();
}
@Test
public void updateSubscriptionGroupConfig() {
+ if (notToBeExecuted()) {
+ return;
+ }
SubscriptionGroupConfig subscriptionGroupConfig = new SubscriptionGroupConfig();
subscriptionGroupConfig.setGroupName(group);
Map attr = ImmutableMap.of("+test", "true");
@@ -99,4 +114,56 @@ public void updateSubscriptionGroupConfig() {
assertThatThrownBy(() -> subscriptionGroupManager.updateSubscriptionGroupConfig(subscriptionGroupConfig1))
.isInstanceOf(RuntimeException.class).hasMessage("attempt to update an unchangeable attribute. key: test");
}
+
+ private boolean notToBeExecuted() {
+ return MixAll.isMac();
+ }
+ @Test
+ public void testUpdateSubscriptionGroupConfigList_NullConfigList() {
+ if (notToBeExecuted()) {
+ return;
+ }
+
+ subscriptionGroupManager.updateSubscriptionGroupConfigList(null);
+ // Verifying that persist() is not called
+ verify(subscriptionGroupManager, never()).persist();
+ }
+
+ @Test
+ public void testUpdateSubscriptionGroupConfigList_EmptyConfigList() {
+ if (notToBeExecuted()) {
+ return;
+ }
+
+ subscriptionGroupManager.updateSubscriptionGroupConfigList(Collections.emptyList());
+ // Verifying that persist() is not called
+ verify(subscriptionGroupManager, never()).persist();
+ }
+
+ @Test
+ public void testUpdateSubscriptionGroupConfigList_ValidConfigList() {
+ if (notToBeExecuted()) {
+ return;
+ }
+
+ final List configList = new LinkedList<>();
+ final List groupNames = new LinkedList<>();
+ for (int i = 0; i < 10; i++) {
+ SubscriptionGroupConfig config = new SubscriptionGroupConfig();
+ String groupName = String.format("group-%d", i);
+ config.setGroupName(groupName);
+ configList.add(config);
+ groupNames.add(groupName);
+ }
+
+ subscriptionGroupManager.updateSubscriptionGroupConfigList(configList);
+
+ // Verifying that persist() is called once
+ verify(subscriptionGroupManager, times(1)).persist();
+
+ groupNames.forEach(groupName ->
+ assertThat(subscriptionGroupManager.getSubscriptionGroupTable().get(groupName)).isNotNull());
+
+ }
+
}
\ No newline at end of file
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigManagerTest.java
index ed71a3313a8..b0e0d057363 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigManagerTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigManagerTest.java
@@ -16,10 +16,13 @@
*/
package org.apache.rocketmq.broker.topic;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
+
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MixAll;
@@ -39,6 +42,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static com.google.common.collect.Sets.newHashSet;
@@ -47,6 +51,10 @@
@RunWith(MockitoJUnitRunner.class)
public class RocksdbTopicConfigManagerTest {
+
+ private final String basePath = Paths.get(System.getProperty("user.home"),
+ "unit-test-store", UUID.randomUUID().toString().substring(0, 16).toUpperCase()).toString();
+
private RocksDBTopicConfigManager topicConfigManager;
@Mock
private BrokerController brokerController;
@@ -62,9 +70,11 @@ public void init() {
BrokerConfig brokerConfig = new BrokerConfig();
when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setStorePathRootDir(basePath);
+ messageStoreConfig.setTransferMetadataJsonToRocksdb(true);
when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig);
- when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
- when(defaultMessageStore.getStateMachineVersion()).thenReturn(0L);
+ Mockito.lenient().when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
+ Mockito.lenient().when(defaultMessageStore.getStateMachineVersion()).thenReturn(0L);
topicConfigManager = new RocksDBTopicConfigManager(brokerController);
topicConfigManager.load();
}
@@ -197,7 +207,6 @@ public void testNormalAddKeyOnCreating() {
TopicConfig existingTopicConfig = topicConfigManager.getTopicConfigTable().get(topic);
Assert.assertEquals("enum-2", existingTopicConfig.getAttributes().get("enum.key"));
Assert.assertEquals("16", existingTopicConfig.getAttributes().get("long.range.key"));
- // assert file
}
@Test
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigTransferTest.java b/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigTransferTest.java
new file mode 100644
index 00000000000..2a727090987
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/topic/RocksdbTopicConfigTransferTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.rocketmq.broker.topic;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.remoting.protocol.DataVersion;
+import org.apache.rocketmq.store.DefaultMessageStore;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RocksdbTopicConfigTransferTest {
+
+ private final String basePath = Paths.get(System.getProperty("user.home"),
+ "unit-test-store", UUID.randomUUID().toString().substring(0, 16).toUpperCase()).toString();
+
+ private RocksDBTopicConfigManager rocksdbTopicConfigManager;
+
+ private TopicConfigManager jsonTopicConfigManager;
+ @Mock
+ private BrokerController brokerController;
+
+ @Mock
+ private DefaultMessageStore defaultMessageStore;
+
+ @Before
+ public void init() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ BrokerConfig brokerConfig = new BrokerConfig();
+ when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setStorePathRootDir(basePath);
+ messageStoreConfig.setTransferMetadataJsonToRocksdb(true);
+ Mockito.lenient().when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig);
+ when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
+ when(defaultMessageStore.getStateMachineVersion()).thenReturn(0L);
+ }
+
+ @After
+ public void destroy() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ Path pathToBeDeleted = Paths.get(basePath);
+ try {
+ Files.walk(pathToBeDeleted)
+ .sorted(Comparator.reverseOrder())
+ .forEach(path -> {
+ try {
+ Files.delete(path);
+ } catch (IOException e) {
+ // ignore
+ }
+ });
+ } catch (IOException e) {
+ // ignore
+ }
+ if (rocksdbTopicConfigManager != null) {
+ rocksdbTopicConfigManager.stop();
+ }
+ }
+
+ public void initRocksdbTopicConfigManager() {
+ if (rocksdbTopicConfigManager == null) {
+ rocksdbTopicConfigManager = new RocksDBTopicConfigManager(brokerController);
+ rocksdbTopicConfigManager.load();
+ }
+ }
+
+ public void initJsonTopicConfigManager() {
+ if (jsonTopicConfigManager == null) {
+ jsonTopicConfigManager = new TopicConfigManager(brokerController);
+ jsonTopicConfigManager.load();
+ }
+ }
+
+ @Test
+ public void theFirstTimeLoadJsonTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initJsonTopicConfigManager();
+ DataVersion dataVersion = jsonTopicConfigManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(0L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, jsonTopicConfigManager.getTopicConfigTable().size());
+ }
+
+ @Test
+ public void theFirstTimeLoadRocksdbTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initRocksdbTopicConfigManager();
+ DataVersion dataVersion = rocksdbTopicConfigManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(0L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksdbTopicConfigManager.getTopicConfigTable().size());
+ }
+
+
+ @Test
+ public void addTopicLoadJsonTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initJsonTopicConfigManager();
+ String topicName = "testAddTopicConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ TopicConfig topicConfig = new TopicConfig();
+ topicConfig.setTopicName(topicName);
+ topicConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = jsonTopicConfigManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ jsonTopicConfigManager.updateTopicConfig(topicConfig);
+
+ DataVersion afterDataVersion = jsonTopicConfigManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+
+ Assert.assertEquals(0, beforeDataVersionCounter);
+ Assert.assertEquals(1, afterDataVersionCounter);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ }
+
+ @Test
+ public void addTopicLoadRocksdbTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ initRocksdbTopicConfigManager();
+ String topicName = "testAddTopicConfig-" + System.currentTimeMillis();
+
+ Map attributes = new HashMap<>();
+
+ TopicConfig topicConfig = new TopicConfig();
+ topicConfig.setTopicName(topicName);
+ topicConfig.setAttributes(attributes);
+ DataVersion beforeDataVersion = rocksdbTopicConfigManager.getDataVersion();
+ long beforeDataVersionCounter = beforeDataVersion.getCounter().get();
+ long beforeTimestamp = beforeDataVersion.getTimestamp();
+
+ rocksdbTopicConfigManager.updateTopicConfig(topicConfig);
+
+ DataVersion afterDataVersion = rocksdbTopicConfigManager.getDataVersion();
+ long afterDataVersionCounter = afterDataVersion.getCounter().get();
+ long afterTimestamp = afterDataVersion.getTimestamp();
+ Assert.assertEquals(0, beforeDataVersionCounter);
+ Assert.assertEquals(1, afterDataVersionCounter);
+ Assert.assertTrue(afterTimestamp >= beforeTimestamp);
+ }
+
+ @Test
+ public void theSecondTimeLoadJsonTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addTopicLoadJsonTopicConfigManager();
+ jsonTopicConfigManager.stop();
+ jsonTopicConfigManager = new TopicConfigManager(brokerController);
+ jsonTopicConfigManager.load();
+ DataVersion dataVersion = jsonTopicConfigManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(1L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, jsonTopicConfigManager.getTopicConfigTable().size());
+ }
+
+ @Test
+ public void theSecondTimeLoadRocksdbTopicConfigManager() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addTopicLoadRocksdbTopicConfigManager();
+ rocksdbTopicConfigManager.stop();
+ rocksdbTopicConfigManager = null;
+ rocksdbTopicConfigManager = new RocksDBTopicConfigManager(brokerController);
+ rocksdbTopicConfigManager.load();
+ DataVersion dataVersion = rocksdbTopicConfigManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(1L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksdbTopicConfigManager.getTopicConfigTable().size());
+ }
+
+ @Test
+ public void jsonUpgradeToRocksdb() {
+ if (notToBeExecuted()) {
+ return;
+ }
+ addTopicLoadJsonTopicConfigManager();
+ initRocksdbTopicConfigManager();
+ DataVersion dataVersion = rocksdbTopicConfigManager.getDataVersion();
+ Assert.assertNotNull(dataVersion);
+ Assert.assertEquals(2L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksdbTopicConfigManager.getTopicConfigTable().size());
+ Assert.assertEquals(rocksdbTopicConfigManager.getTopicConfigTable().size(), jsonTopicConfigManager.getTopicConfigTable().size());
+
+ rocksdbTopicConfigManager.stop();
+ rocksdbTopicConfigManager = new RocksDBTopicConfigManager(brokerController);
+ rocksdbTopicConfigManager.load();
+ dataVersion = rocksdbTopicConfigManager.getDataVersion();
+ Assert.assertEquals(2L, dataVersion.getCounter().get());
+ Assert.assertEquals(0L, dataVersion.getStateVersion());
+ Assert.assertNotEquals(0, rocksdbTopicConfigManager.getTopicConfigTable().size());
+ Assert.assertEquals(rocksdbTopicConfigManager.getTopicConfigTable().size(), rocksdbTopicConfigManager.getTopicConfigTable().size());
+ }
+
+
+ private boolean notToBeExecuted() {
+ return MixAll.isMac();
+ }
+
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicConfigManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicConfigManagerTest.java
index 6052a79d413..3fd1d14c3a2 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicConfigManagerTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicConfigManagerTest.java
@@ -16,10 +16,13 @@
*/
package org.apache.rocketmq.broker.topic;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
+
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.TopicAttributes;
@@ -37,6 +40,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static com.google.common.collect.Sets.newHashSet;
@@ -45,6 +49,9 @@
@RunWith(MockitoJUnitRunner.class)
public class TopicConfigManagerTest {
+
+ private final String basePath = Paths.get(System.getProperty("user.home"),
+ "unit-test-store", UUID.randomUUID().toString().substring(0, 16).toUpperCase()).toString();
private TopicConfigManager topicConfigManager;
@Mock
private BrokerController brokerController;
@@ -57,8 +64,9 @@ public void init() {
BrokerConfig brokerConfig = new BrokerConfig();
when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setStorePathRootDir(basePath);
when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig);
- when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
+ Mockito.lenient().when(brokerController.getMessageStore()).thenReturn(defaultMessageStore);
when(defaultMessageStore.getStateMachineVersion()).thenReturn(0L);
topicConfigManager = new TopicConfigManager(brokerController);
}
diff --git a/client/pom.xml b/client/pom.xml
index 13a92815586..5a6c92f97dd 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -19,7 +19,7 @@
org.apache.rocketmq
rocketmq-all
- 5.2.1-SNAPSHOT
+ 5.3.1-SNAPSHOT
4.0.0
diff --git a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
index 0fc04fcccb2..696b073b373 100644
--- a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
+++ b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
@@ -20,6 +20,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.message.MessageQueue;
@@ -64,6 +65,8 @@ public class ClientConfig {
*/
private int persistConsumerOffsetInterval = 1000 * 5;
private long pullTimeDelayMillsWhenException = 1000;
+
+ private int traceMsgBatchNum = 10;
private boolean unitMode = false;
private String unitName;
private boolean decodeReadBody = Boolean.parseBoolean(System.getProperty(DECODE_READ_BODY, "true"));
@@ -127,6 +130,14 @@ public String buildMQClientId() {
return sb.toString();
}
+ public int getTraceMsgBatchNum() {
+ return traceMsgBatchNum;
+ }
+
+ public void setTraceMsgBatchNum(int traceMsgBatchNum) {
+ this.traceMsgBatchNum = traceMsgBatchNum;
+ }
+
public String getClientIP() {
return clientIP;
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumer.java
index 3364df48f89..20857f14e08 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumer.java
@@ -200,7 +200,7 @@ public DefaultLitePullConsumer(RPCHook rpcHook) {
* Constructor specifying consumer group, RPC hook
*
* @param consumerGroup Consumer group.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param rpcHook RPC hook to execute before each remoting command.
*/
public DefaultLitePullConsumer(final String consumerGroup, RPCHook rpcHook) {
this.consumerGroup = consumerGroup;
@@ -213,7 +213,7 @@ public DefaultLitePullConsumer(final String consumerGroup, RPCHook rpcHook) {
* Constructor specifying namespace, consumer group and RPC hook.
*
* @param consumerGroup Consumer group.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param rpcHook RPC hook to execute before each remoting command.
*/
@Deprecated
public DefaultLitePullConsumer(final String namespace, final String consumerGroup, RPCHook rpcHook) {
@@ -270,6 +270,7 @@ public void subscribe(String topic, MessageSelector messageSelector) throws MQCl
public void unsubscribe(String topic) {
this.defaultLitePullConsumerImpl.unsubscribe(withNamespace(topic));
}
+
@Override
public void assign(Collection messageQueues) {
defaultLitePullConsumerImpl.assign(queuesWithNamespace(messageQueues));
@@ -338,7 +339,8 @@ public void commit() {
this.defaultLitePullConsumerImpl.commitAll();
}
- @Override public void commit(Map offsetMap, boolean persist) {
+ @Override
+ public void commit(Map offsetMap, boolean persist) {
this.defaultLitePullConsumerImpl.commit(offsetMap, persist);
}
@@ -361,11 +363,11 @@ public Set assignment() throws MQClientException {
* @param messageQueueListener
*/
@Override
- public void subscribe(String topic, String subExpression, MessageQueueListener messageQueueListener) throws MQClientException {
+ public void subscribe(String topic, String subExpression,
+ MessageQueueListener messageQueueListener) throws MQClientException {
this.defaultLitePullConsumerImpl.subscribe(withNamespace(topic), subExpression, messageQueueListener);
}
-
@Override
public void commit(final Set messageQueues, boolean persist) {
this.defaultLitePullConsumerImpl.commit(messageQueues, persist);
@@ -589,7 +591,7 @@ public TraceDispatcher getTraceDispatcher() {
private void setTraceDispatcher() {
if (enableTrace) {
try {
- AsyncTraceDispatcher traceDispatcher = new AsyncTraceDispatcher(consumerGroup, TraceDispatcher.Type.CONSUME, traceTopic, rpcHook);
+ AsyncTraceDispatcher traceDispatcher = new AsyncTraceDispatcher(consumerGroup, TraceDispatcher.Type.CONSUME, getTraceMsgBatchNum(), traceTopic, rpcHook);
traceDispatcher.getTraceProducer().setUseTLS(this.isUseTLS());
traceDispatcher.setNamespaceV2(namespaceV2);
this.traceDispatcher = traceDispatcher;
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumer.java
index 089fd39b3e9..7c9a65ecdbf 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumer.java
@@ -262,7 +262,7 @@ public void setRegisterTopics(Set registerTopics) {
public void sendMessageBack(MessageExt msg, int delayLevel)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
msg.setTopic(withNamespace(msg.getTopic()));
- this.defaultMQPullConsumerImpl.sendMessageBack(msg, delayLevel, null);
+ this.defaultMQPullConsumerImpl.sendMessageBack(msg, delayLevel, msg.getBrokerName());
}
/**
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
index 38a412c237b..5df5cc8fa1a 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
@@ -51,11 +51,9 @@
/**
* In most scenarios, this is the mostly recommended class to consume messages.
*
- *
* Technically speaking, this push client is virtually a wrapper of the underlying pull service. Specifically, on
* arrival of messages pulled from brokers, it roughly invokes the registered callback handler to feed the messages.
*
- *
* See quickstart/Consumer in the example module for a typical usage.
*
*
@@ -76,7 +74,6 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
* Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve
* load balance. It's required and needs to be globally unique.
*
- *
* See here for further discussion.
*/
private String consumerGroup;
@@ -84,13 +81,11 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
/**
* Message model defines the way how messages are delivered to each consumer clients.
*
- *
* RocketMQ supports two message models: clustering and broadcasting. If clustering is set, consumer clients with
* the same {@link #consumerGroup} would only consume shards of the messages subscribed, which achieves load
* balances; Conversely, if the broadcasting is set, each consumer client will consume all subscribed messages
* separately.
*
- *
* This field defaults to clustering.
*/
private MessageModel messageModel = MessageModel.CLUSTERING;
@@ -98,7 +93,6 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
/**
* Consuming point on consumer booting.
*
- *
* There are three consuming points:
*
* -
@@ -239,7 +233,6 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
*/
private int pullBatchSize = 32;
-
private int pullBatchSizeInBytes = 256 * 1024;
/**
@@ -256,7 +249,6 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
* Max re-consume times.
* In concurrently mode, -1 means 16;
* In orderly mode, -1 means Integer.MAX_VALUE.
- *
* If messages are re-consumed more than {@link #maxReconsumeTimes} before success.
*/
private int maxReconsumeTimes = -1;
@@ -312,7 +304,6 @@ public DefaultMQPushConsumer(final String consumerGroup) {
this(consumerGroup, null, new AllocateMessageQueueAveragely());
}
-
/**
* Constructor specifying RPC hook.
*
@@ -326,29 +317,29 @@ public DefaultMQPushConsumer(RPCHook rpcHook) {
* Constructor specifying consumer group, RPC hook.
*
* @param consumerGroup Consumer group.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param rpcHook RPC hook to execute before each remoting command.
*/
public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook) {
this(consumerGroup, rpcHook, new AllocateMessageQueueAveragely());
}
-
/**
* Constructor specifying consumer group, enabled msg trace flag and customized trace topic name.
*
- * @param consumerGroup Consumer group.
- * @param enableMsgTrace Switch flag instance for message trace.
+ * @param consumerGroup Consumer group.
+ * @param enableMsgTrace Switch flag instance for message trace.
* @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default trace topic name.
*/
- public DefaultMQPushConsumer(final String consumerGroup, boolean enableMsgTrace, final String customizedTraceTopic) {
+ public DefaultMQPushConsumer(final String consumerGroup, boolean enableMsgTrace,
+ final String customizedTraceTopic) {
this(consumerGroup, null, new AllocateMessageQueueAveragely(), enableMsgTrace, customizedTraceTopic);
}
/**
* Constructor specifying consumer group, RPC hook and message queue allocating algorithm.
*
- * @param consumerGroup Consume queue.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param consumerGroup Consumer group.
+ * @param rpcHook RPC hook to execute before each remoting command.
* @param allocateMessageQueueStrategy Message queue allocating algorithm.
*/
public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook,
@@ -359,14 +350,15 @@ public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook,
/**
* Constructor specifying consumer group, RPC hook, message queue allocating algorithm, enabled msg trace flag and customized trace topic name.
*
- * @param consumerGroup Consume queue.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param consumerGroup Consumer group.
+ * @param rpcHook RPC hook to execute before each remoting command.
* @param allocateMessageQueueStrategy message queue allocating algorithm.
- * @param enableMsgTrace Switch flag instance for message trace.
- * @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default trace topic name.
+ * @param enableMsgTrace Switch flag instance for message trace.
+ * @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default trace topic name.
*/
public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook,
- AllocateMessageQueueStrategy allocateMessageQueueStrategy, boolean enableMsgTrace, final String customizedTraceTopic) {
+ AllocateMessageQueueStrategy allocateMessageQueueStrategy, boolean enableMsgTrace,
+ final String customizedTraceTopic) {
this.consumerGroup = consumerGroup;
this.rpcHook = rpcHook;
this.allocateMessageQueueStrategy = allocateMessageQueueStrategy;
@@ -378,7 +370,7 @@ public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook,
/**
* Constructor specifying namespace and consumer group.
*
- * @param namespace Namespace for this MQ Producer instance.
+ * @param namespace Namespace for this MQ Producer instance.
* @param consumerGroup Consumer group.
*/
@Deprecated
@@ -389,9 +381,9 @@ public DefaultMQPushConsumer(final String namespace, final String consumerGroup)
/**
* Constructor specifying namespace, consumer group and RPC hook .
*
- * @param namespace Namespace for this MQ Producer instance.
+ * @param namespace Namespace for this MQ Producer instance.
* @param consumerGroup Consumer group.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param rpcHook RPC hook to execute before each remoting command.
*/
@Deprecated
public DefaultMQPushConsumer(final String namespace, final String consumerGroup, RPCHook rpcHook) {
@@ -401,9 +393,9 @@ public DefaultMQPushConsumer(final String namespace, final String consumerGroup,
/**
* Constructor specifying namespace, consumer group, RPC hook and message queue allocating algorithm.
*
- * @param namespace Namespace for this MQ Producer instance.
- * @param consumerGroup Consume queue.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param namespace Namespace for this MQ Producer instance.
+ * @param consumerGroup Consumer group.
+ * @param rpcHook RPC hook to execute before each remoting command.
* @param allocateMessageQueueStrategy Message queue allocating algorithm.
*/
@Deprecated
@@ -419,16 +411,17 @@ public DefaultMQPushConsumer(final String namespace, final String consumerGroup,
/**
* Constructor specifying namespace, consumer group, RPC hook, message queue allocating algorithm, enabled msg trace flag and customized trace topic name.
*
- * @param namespace Namespace for this MQ Producer instance.
- * @param consumerGroup Consume queue.
- * @param rpcHook RPC hook to execute before each remoting command.
+ * @param namespace Namespace for this MQ Producer instance.
+ * @param consumerGroup Consumer group.
+ * @param rpcHook RPC hook to execute before each remoting command.
* @param allocateMessageQueueStrategy message queue allocating algorithm.
- * @param enableMsgTrace Switch flag instance for message trace.
- * @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default trace topic name.
+ * @param enableMsgTrace Switch flag instance for message trace.
+ * @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default trace topic name.
*/
@Deprecated
public DefaultMQPushConsumer(final String namespace, final String consumerGroup, RPCHook rpcHook,
- AllocateMessageQueueStrategy allocateMessageQueueStrategy, boolean enableMsgTrace, final String customizedTraceTopic) {
+ AllocateMessageQueueStrategy allocateMessageQueueStrategy, boolean enableMsgTrace,
+ final String customizedTraceTopic) {
this.consumerGroup = consumerGroup;
this.namespace = namespace;
this.rpcHook = rpcHook;
@@ -443,7 +436,8 @@ public DefaultMQPushConsumer(final String namespace, final String consumerGroup,
*/
@Deprecated
@Override
- public void createTopic(String key, String newTopic, int queueNum, Map attributes) throws MQClientException {
+ public void createTopic(String key, String newTopic, int queueNum,
+ Map attributes) throws MQClientException {
createTopic(key, withNamespace(newTopic), queueNum, 0, null);
}
@@ -457,7 +451,8 @@ public void setUseTLS(boolean useTLS) {
*/
@Deprecated
@Override
- public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag, Map attributes) throws MQClientException {
+ public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag,
+ Map attributes) throws MQClientException {
this.defaultMQPushConsumerImpl.createTopic(key, withNamespace(newTopic), queueNum, topicSysFlag);
}
@@ -677,39 +672,39 @@ public void setSubscription(Map subscription) {
/**
* Send message back to broker which will be re-delivered in future.
- *
+ *
* This method will be removed or it's visibility will be changed in a certain version after April 5, 2020, so
* please do not use this method.
*
- * @param msg Message to send back.
+ * @param msg Message to send back.
* @param delayLevel delay level.
- * @throws RemotingException if there is any network-tier error.
- * @throws MQBrokerException if there is any broker error.
+ * @throws RemotingException if there is any network-tier error.
+ * @throws MQBrokerException if there is any broker error.
* @throws InterruptedException if the thread is interrupted.
- * @throws MQClientException if there is any client error.
+ * @throws MQClientException if there is any client error.
*/
@Deprecated
@Override
public void sendMessageBack(MessageExt msg, int delayLevel)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
msg.setTopic(withNamespace(msg.getTopic()));
- this.defaultMQPushConsumerImpl.sendMessageBack(msg, delayLevel, (String) null);
+ this.defaultMQPushConsumerImpl.sendMessageBack(msg, delayLevel, msg.getBrokerName());
}
/**
* Send message back to the broker whose name is brokerName
and the message will be re-delivered in
* future.
- *
+ *
* This method will be removed or it's visibility will be changed in a certain version after April 5, 2020, so
* please do not use this method.
*
- * @param msg Message to send back.
+ * @param msg Message to send back.
* @param delayLevel delay level.
* @param brokerName broker name.
- * @throws RemotingException if there is any network-tier error.
- * @throws MQBrokerException if there is any broker error.
+ * @throws RemotingException if there is any network-tier error.
+ * @throws MQBrokerException if there is any broker error.
* @throws InterruptedException if the thread is interrupted.
- * @throws MQClientException if there is any client error.
+ * @throws MQClientException if there is any client error.
*/
@Deprecated
@Override
@@ -735,7 +730,7 @@ public void start() throws MQClientException {
this.defaultMQPushConsumerImpl.start();
if (enableTrace) {
try {
- AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(consumerGroup, TraceDispatcher.Type.CONSUME, traceTopic, rpcHook);
+ AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(consumerGroup, TraceDispatcher.Type.CONSUME, getTraceMsgBatchNum(), traceTopic, rpcHook);
dispatcher.setHostConsumer(this.defaultMQPushConsumerImpl);
dispatcher.setNamespaceV2(namespaceV2);
traceDispatcher = dispatcher;
@@ -799,9 +794,9 @@ public void registerMessageListener(MessageListenerOrderly messageListener) {
/**
* Subscribe a topic to consuming subscription.
*
- * @param topic topic to subscribe.
+ * @param topic topic to subscribe.
* @param subExpression subscription expression.it only support or operation such as "tag1 || tag2 || tag3"
- * if null or * expression,meaning subscribe all
+ * if null or * expression,meaning subscribe all
* @throws MQClientException if there is any client error.
*/
@Override
@@ -812,8 +807,8 @@ public void subscribe(String topic, String subExpression) throws MQClientExcepti
/**
* Subscribe a topic to consuming subscription.
*
- * @param topic topic to consume.
- * @param fullClassName full class name,must extend org.apache.rocketmq.common.filter. MessageFilter
+ * @param topic topic to consume.
+ * @param fullClassName full class name,must extend org.apache.rocketmq.common.filter. MessageFilter
* @param filterClassSource class source code,used UTF-8 file encoding,must be responsible for your code safety
*/
@Override
@@ -824,7 +819,7 @@ public void subscribe(String topic, String fullClassName, String filterClassSour
/**
* Subscribe a topic by message selector.
*
- * @param topic topic to consume.
+ * @param topic topic to consume.
* @param messageSelector {@link org.apache.rocketmq.client.consumer.MessageSelector}
* @see org.apache.rocketmq.client.consumer.MessageSelector#bySql
* @see org.apache.rocketmq.client.consumer.MessageSelector#byTag
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/MQAdminImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/MQAdminImpl.java
index bcfe29bd4f6..c1e3ee33dc1 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/MQAdminImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/MQAdminImpl.java
@@ -28,6 +28,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.Validators;
import org.apache.rocketmq.client.exception.MQBrokerException;
@@ -43,6 +44,7 @@
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageId;
import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.topic.TopicValidator;
import org.apache.rocketmq.common.utils.NetworkUtil;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
@@ -199,7 +201,7 @@ public long searchOffset(MessageQueue mq, long timestamp, BoundaryType boundaryT
if (brokerAddr != null) {
try {
return this.mQClientFactory.getMQClientAPIImpl().searchOffset(brokerAddr, mq, timestamp,
- boundaryType, timeoutMillis);
+ boundaryType, timeoutMillis);
} catch (Exception e) {
throw new MQClientException("Invoke Broker[" + brokerAddr + "] exception", e);
}
@@ -277,13 +279,20 @@ public MessageExt viewMessage(String topic, String msgId)
public QueryResult queryMessage(String topic, String key, int maxNum, long begin,
long end) throws MQClientException,
InterruptedException {
- return queryMessage(topic, key, maxNum, begin, end, false);
+ return queryMessage(null, topic, key, maxNum, begin, end, false);
}
public QueryResult queryMessageByUniqKey(String topic, String uniqKey, int maxNum, long begin, long end)
throws MQClientException, InterruptedException {
- return queryMessage(topic, uniqKey, maxNum, begin, end, true);
+ return queryMessage(null, topic, uniqKey, maxNum, begin, end, true);
+ }
+
+ public QueryResult queryMessageByUniqKey(String clusterName, String topic, String uniqKey, int maxNum, long begin,
+ long end)
+ throws MQClientException, InterruptedException {
+
+ return queryMessage(clusterName, topic, uniqKey, maxNum, begin, end, true);
}
public MessageExt queryMessageByUniqKey(String topic,
@@ -311,25 +320,29 @@ public MessageExt queryMessageByUniqKey(String clusterName, String topic,
}
}
- protected QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end,
+ public QueryResult queryMessage(String clusterName, String topic, String key, int maxNum, long begin, long end,
boolean isUniqKey) throws MQClientException,
InterruptedException {
- return queryMessage(null, topic, key, maxNum, begin, end, isUniqKey);
- }
+ boolean isLmq = MixAll.isLmq(topic);
+
+ String routeTopic = topic;
+ // if topic is lmq ,then use clusterName as lmq parent topic
+ // Use clusterName or lmq parent topic to get topic route for lmq or rmq_sys_wheel_timer
+ if (!StringUtils.isEmpty(topic) && (isLmq || topic.equals(TopicValidator.SYSTEM_TOPIC_PREFIX + "wheel_timer"))
+ && !StringUtils.isEmpty(clusterName)) {
+ routeTopic = clusterName;
+ }
- protected QueryResult queryMessage(String clusterName, String topic, String key, int maxNum, long begin, long end,
- boolean isUniqKey) throws MQClientException,
- InterruptedException {
- TopicRouteData topicRouteData = this.mQClientFactory.getAnExistTopicRouteData(topic);
+ TopicRouteData topicRouteData = this.mQClientFactory.getAnExistTopicRouteData(routeTopic);
if (null == topicRouteData) {
- this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
- topicRouteData = this.mQClientFactory.getAnExistTopicRouteData(topic);
+ this.mQClientFactory.updateTopicRouteInfoFromNameServer(routeTopic);
+ topicRouteData = this.mQClientFactory.getAnExistTopicRouteData(routeTopic);
}
if (topicRouteData != null) {
List brokerAddrs = new LinkedList<>();
for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
- if (clusterName != null && !clusterName.isEmpty()
+ if (!isLmq && clusterName != null && !clusterName.isEmpty()
&& !clusterName.equals(brokerData.getCluster())) {
continue;
}
@@ -347,7 +360,11 @@ protected QueryResult queryMessage(String clusterName, String topic, String key,
for (String addr : brokerAddrs) {
try {
QueryMessageRequestHeader requestHeader = new QueryMessageRequestHeader();
- requestHeader.setTopic(topic);
+ if (isLmq) {
+ requestHeader.setTopic(clusterName);
+ } else {
+ requestHeader.setTopic(topic);
+ }
requestHeader.setKey(key);
requestHeader.setMaxNum(maxNum);
requestHeader.setBeginTimestamp(begin);
@@ -436,7 +453,7 @@ public void operationFail(Throwable throwable) {
String[] keyArray = keys.split(MessageConst.KEY_SEPARATOR);
for (String k : keyArray) {
// both topic and key must be equal at the same time
- if (Objects.equals(key, k) && Objects.equals(topic, msgTopic)) {
+ if (Objects.equals(key, k) && (isLmq || Objects.equals(topic, msgTopic))) {
matched = true;
break;
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java
index f3d7e7c70f9..b539b8f098a 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java
@@ -78,6 +78,7 @@
import org.apache.rocketmq.common.namesrv.TopAddressing;
import org.apache.rocketmq.common.sysflag.PullSysFlag;
import org.apache.rocketmq.common.topic.TopicValidator;
+import org.apache.rocketmq.common.utils.StartAndShutdown;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
import org.apache.rocketmq.remoting.ChannelEventListener;
@@ -137,6 +138,7 @@
import org.apache.rocketmq.remoting.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.remoting.protocol.body.ResetOffsetBody;
import org.apache.rocketmq.remoting.protocol.body.SetMessageRequestModeRequestBody;
+import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupList;
import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.remoting.protocol.body.TopicList;
@@ -183,9 +185,9 @@
import org.apache.rocketmq.remoting.protocol.header.GetTopicStatsInfoRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetTopicsByClusterRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetUserRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.HeartbeatRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ListAclsRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ListUsersRequestHeader;
-import org.apache.rocketmq.remoting.protocol.header.HeartbeatRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.LockBatchMqRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.PopMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.PopMessageResponseHeader;
@@ -246,7 +248,7 @@
import static org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode.SUCCESS;
-public class MQClientAPIImpl implements NameServerUpdateCallback {
+public class MQClientAPIImpl implements NameServerUpdateCallback, StartAndShutdown {
private final static Logger log = LoggerFactory.getLogger(MQClientAPIImpl.class);
private static boolean sendSmartMsg =
Boolean.parseBoolean(System.getProperty("org.apache.rocketmq.client.sendSmartMsg", "true"));
@@ -400,6 +402,22 @@ public void createSubscriptionGroup(final String addr, final SubscriptionGroupCo
}
+ public void createSubscriptionGroupList(final String address, final List configs,
+ final long timeoutMillis) throws RemotingException, InterruptedException, MQClientException {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_AND_CREATE_SUBSCRIPTIONGROUP_LIST, null);
+ SubscriptionGroupList requestBody = new SubscriptionGroupList(configs);
+ request.setBody(requestBody.encode());
+
+ RemotingCommand response = this.remotingClient.invokeSync(
+ MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), address), request, timeoutMillis);
+ assert response != null;
+ if (response.getCode() == ResponseCode.SUCCESS) {
+ return;
+ }
+
+ throw new MQClientException(response.getCode(), response.getRemark());
+ }
+
public void createTopic(final String addr, final String defaultTopic, final TopicConfig topicConfig,
final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessagePopConcurrentlyService.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessagePopConcurrentlyService.java
index 3713d1aba4d..d5191871106 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessagePopConcurrentlyService.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessagePopConcurrentlyService.java
@@ -471,7 +471,7 @@ public void run() {
processQueue.decFoundMsg(-msgs.size());
}
- log.warn("processQueue invalid. isDropped={}, isPopTimeout={}, messageQueue={}, msgs={}",
+ log.warn("processQueue invalid or popTimeout. isDropped={}, isPopTimeout={}, messageQueue={}, msgs={}",
processQueue.isDropped(), isPopTimeout(), messageQueue, msgs);
}
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java
index a3276cd7823..3f90b67ec99 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java
@@ -164,10 +164,6 @@ private enum SubscriptionType {
public DefaultLitePullConsumerImpl(final DefaultLitePullConsumer defaultLitePullConsumer, final RPCHook rpcHook) {
this.defaultLitePullConsumer = defaultLitePullConsumer;
this.rpcHook = rpcHook;
- this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(
- this.defaultLitePullConsumer.getPullThreadNums(),
- new ThreadFactoryImpl("PullMsgThread-" + this.defaultLitePullConsumer.getConsumerGroup())
- );
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("MonitorMessageQueueChangeThread"));
this.pullTimeDelayMillsWhenException = defaultLitePullConsumer.getPullTimeDelayMillsWhenException();
}
@@ -293,6 +289,8 @@ public synchronized void start() throws MQClientException {
this.defaultLitePullConsumer.changeInstanceNameToPID();
}
+ initScheduledThreadPoolExecutor();
+
initMQClientFactory();
initRebalanceImpl();
@@ -324,6 +322,13 @@ public synchronized void start() throws MQClientException {
}
}
+ private void initScheduledThreadPoolExecutor() {
+ this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(
+ this.defaultLitePullConsumer.getPullThreadNums(),
+ new ThreadFactoryImpl("PullMsgThread-" + this.defaultLitePullConsumer.getConsumerGroup())
+ );
+ }
+
private void initMQClientFactory() throws MQClientException {
this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultLitePullConsumer, this.rpcHook);
boolean registerOK = mQClientFactory.registerConsumer(this.defaultLitePullConsumer.getConsumerGroup(), this);
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
index 3e832e5a9a3..c92cadf5057 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
@@ -621,10 +621,9 @@ public void onException(Throwable e) {
private PopResult processPopResult(final PopResult popResult, final SubscriptionData subscriptionData) {
if (PopStatus.FOUND == popResult.getPopStatus()) {
List msgFoundList = popResult.getMsgFoundList();
- List msgListFilterAgain = msgFoundList;
+ List msgListFilterAgain = new ArrayList<>(popResult.getMsgFoundList().size());
if (!subscriptionData.getTagsSet().isEmpty() && !subscriptionData.isClassFilterMode()
&& popResult.getMsgFoundList().size() > 0) {
- msgListFilterAgain = new ArrayList<>(popResult.getMsgFoundList().size());
for (MessageExt msg : popResult.getMsgFoundList()) {
if (msg.getTags() != null) {
if (subscriptionData.getTagsSet().contains(msg.getTags())) {
@@ -632,6 +631,8 @@ private PopResult processPopResult(final PopResult popResult, final Subscription
}
}
}
+ } else {
+ msgListFilterAgain.addAll(msgFoundList);
}
if (!this.filterMessageHookList.isEmpty()) {
@@ -649,6 +650,15 @@ private PopResult processPopResult(final PopResult popResult, final Subscription
}
}
+ Iterator iterator = msgListFilterAgain.iterator();
+ while (iterator.hasNext()) {
+ MessageExt msg = iterator.next();
+ if (msg.getReconsumeTimes() > getMaxReconsumeTimes()) {
+ iterator.remove();
+ log.info("Reconsume times has reached {}, so ack msg={}", msg.getReconsumeTimes(), msg);
+ }
+ }
+
if (msgFoundList.size() != msgListFilterAgain.size()) {
for (MessageExt msg : msgFoundList) {
if (!msgListFilterAgain.contains(msg)) {
@@ -742,7 +752,7 @@ public void sendMessageBack(MessageExt msg, int delayLevel, final String brokerN
public void sendMessageBack(MessageExt msg, int delayLevel, final MessageQueue mq)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
- sendMessageBack(msg, delayLevel, null, mq);
+ sendMessageBack(msg, delayLevel, msg.getBrokerName(), mq);
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java
index b4ebf692736..c9fd3c83e04 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java
@@ -152,6 +152,7 @@ public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String cli
this.nettyClientConfig.setClientCallbackExecutorThreads(clientConfig.getClientCallbackExecutorThreads());
this.nettyClientConfig.setUseTLS(clientConfig.isUseTLS());
this.nettyClientConfig.setSocksProxyConfig(clientConfig.getSocksProxyConfig());
+ this.nettyClientConfig.setScanAvailableNameSrv(false);
ClientRemotingProcessor clientRemotingProcessor = new ClientRemotingProcessor(this);
ChannelEventListener channelEventListener;
if (clientConfig.isEnableHeartbeatChannelEventListener()) {
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIExt.java b/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIExt.java
index b97e00c577f..0e2092b8a0f 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIExt.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIExt.java
@@ -400,6 +400,33 @@ public CompletableFuture updateConsumerOffsetOneWay(
return future;
}
+ public CompletableFuture updateConsumerOffsetAsync(
+ String brokerAddr,
+ UpdateConsumerOffsetRequestHeader header,
+ long timeoutMillis
+ ) {
+ RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_CONSUMER_OFFSET, header);
+ CompletableFuture future = new CompletableFuture<>();
+ invoke(brokerAddr, request, timeoutMillis).whenComplete((response, t) -> {
+ if (t != null) {
+ log.error("updateConsumerOffsetAsync failed, brokerAddr={}, requestHeader={}", brokerAddr, header, t);
+ future.completeExceptionally(t);
+ return;
+ }
+ switch (response.getCode()) {
+ case ResponseCode.SUCCESS: {
+ future.complete(null);
+ }
+ case ResponseCode.SYSTEM_ERROR:
+ case ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST:
+ case ResponseCode.TOPIC_NOT_EXIST: {
+ future.completeExceptionally(new MQBrokerException(response.getCode(), response.getRemark()));
+ }
+ }
+ });
+ return future;
+ }
+
public CompletableFuture> getConsumerListByGroupAsync(
String brokerAddr,
GetConsumerListByGroupRequestHeader requestHeader,
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIFactory.java b/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIFactory.java
index c68859b2889..0fa31b66406 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIFactory.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/mqclient/MQClientAPIFactory.java
@@ -26,6 +26,7 @@
import org.apache.rocketmq.client.common.NameserverAccessConfig;
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.utils.AsyncShutdownHelper;
import org.apache.rocketmq.common.utils.StartAndShutdown;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
@@ -85,9 +86,11 @@ public void start() throws Exception {
@Override
public void shutdown() throws Exception {
+ AsyncShutdownHelper helper = new AsyncShutdownHelper();
for (int i = 0; i < this.clientNum; i++) {
- clients[i].shutdown();
+ helper.addTarget(clients[i]);
}
+ helper.shutdown().await(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
protected MQClientAPIExt createAndStart(String instanceName) {
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java
index 7ef34025137..74a2516174a 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java
@@ -194,6 +194,14 @@ public void setSemaphoreAsyncSendSize(int size) {
semaphoreAsyncSendSize = new Semaphore(size, true);
}
+ public int getSemaphoreAsyncSendNumAvailablePermits() {
+ return semaphoreAsyncSendNum == null ? 0 : semaphoreAsyncSendNum.availablePermits();
+ }
+
+ public int getSemaphoreAsyncSendSizeAvailablePermits() {
+ return semaphoreAsyncSendSize == null ? 0 : semaphoreAsyncSendSize.availablePermits();
+ }
+
public void initTransactionEnv() {
TransactionMQProducer producer = (TransactionMQProducer) this.defaultMQProducer;
if (producer.getExecutorService() != null) {
@@ -563,7 +571,7 @@ public void run() {
class BackpressureSendCallBack implements SendCallback {
public boolean isSemaphoreAsyncSizeAcquired = false;
- public boolean isSemaphoreAsyncNumbAcquired = false;
+ public boolean isSemaphoreAsyncNumAcquired = false;
public int msgLen;
private final SendCallback sendCallback;
@@ -573,24 +581,49 @@ public BackpressureSendCallBack(final SendCallback sendCallback) {
@Override
public void onSuccess(SendResult sendResult) {
- if (isSemaphoreAsyncSizeAcquired) {
- semaphoreAsyncSendSize.release(msgLen);
- }
- if (isSemaphoreAsyncNumbAcquired) {
- semaphoreAsyncSendNum.release();
- }
+ semaphoreProcessor();
sendCallback.onSuccess(sendResult);
}
@Override
public void onException(Throwable e) {
+ semaphoreProcessor();
+ sendCallback.onException(e);
+ }
+
+ public void semaphoreProcessor() {
if (isSemaphoreAsyncSizeAcquired) {
+ defaultMQProducer.acquireBackPressureForAsyncSendSizeLock();
semaphoreAsyncSendSize.release(msgLen);
+ defaultMQProducer.releaseBackPressureForAsyncSendSizeLock();
}
- if (isSemaphoreAsyncNumbAcquired) {
+ if (isSemaphoreAsyncNumAcquired) {
+ defaultMQProducer.acquireBackPressureForAsyncSendNumLock();
semaphoreAsyncSendNum.release();
+ defaultMQProducer.releaseBackPressureForAsyncSendNumLock();
}
- sendCallback.onException(e);
+ }
+
+ public void semaphoreAsyncAdjust(int semaphoreAsyncNum, int semaphoreAsyncSize) throws InterruptedException {
+ defaultMQProducer.acquireBackPressureForAsyncSendNumLock();
+ if (semaphoreAsyncNum > 0) {
+ semaphoreAsyncSendNum.release(semaphoreAsyncNum);
+ } else {
+ semaphoreAsyncSendNum.acquire(- semaphoreAsyncNum);
+ }
+ defaultMQProducer.setBackPressureForAsyncSendNumInsideAdjust(defaultMQProducer.getBackPressureForAsyncSendNum()
+ + semaphoreAsyncNum);
+ defaultMQProducer.releaseBackPressureForAsyncSendNumLock();
+
+ defaultMQProducer.acquireBackPressureForAsyncSendSizeLock();
+ if (semaphoreAsyncSize > 0) {
+ semaphoreAsyncSendSize.release(semaphoreAsyncSize);
+ } else {
+ semaphoreAsyncSendSize.acquire(- semaphoreAsyncSize);
+ }
+ defaultMQProducer.setBackPressureForAsyncSendSizeInsideAdjust(defaultMQProducer.getBackPressureForAsyncSendSize()
+ + semaphoreAsyncSize);
+ defaultMQProducer.releaseBackPressureForAsyncSendSizeLock();
}
}
@@ -599,32 +632,40 @@ public void executeAsyncMessageSend(Runnable runnable, final Message msg, final
throws MQClientException, InterruptedException {
ExecutorService executor = this.getAsyncSenderExecutor();
boolean isEnableBackpressureForAsyncMode = this.getDefaultMQProducer().isEnableBackpressureForAsyncMode();
- boolean isSemaphoreAsyncNumbAcquired = false;
+ boolean isSemaphoreAsyncNumAcquired = false;
boolean isSemaphoreAsyncSizeAcquired = false;
int msgLen = msg.getBody() == null ? 1 : msg.getBody().length;
+ sendCallback.msgLen = msgLen;
try {
if (isEnableBackpressureForAsyncMode) {
+ defaultMQProducer.acquireBackPressureForAsyncSendNumLock();
long costTime = System.currentTimeMillis() - beginStartTime;
- isSemaphoreAsyncNumbAcquired = timeout - costTime > 0
+
+ isSemaphoreAsyncNumAcquired = timeout - costTime > 0
&& semaphoreAsyncSendNum.tryAcquire(timeout - costTime, TimeUnit.MILLISECONDS);
- if (!isSemaphoreAsyncNumbAcquired) {
+ sendCallback.isSemaphoreAsyncNumAcquired = isSemaphoreAsyncNumAcquired;
+ defaultMQProducer.releaseBackPressureForAsyncSendNumLock();
+ if (!isSemaphoreAsyncNumAcquired) {
sendCallback.onException(
new RemotingTooMuchRequestException("send message tryAcquire semaphoreAsyncNum timeout"));
return;
}
+
+ defaultMQProducer.acquireBackPressureForAsyncSendSizeLock();
costTime = System.currentTimeMillis() - beginStartTime;
+
isSemaphoreAsyncSizeAcquired = timeout - costTime > 0
&& semaphoreAsyncSendSize.tryAcquire(msgLen, timeout - costTime, TimeUnit.MILLISECONDS);
+ sendCallback.isSemaphoreAsyncSizeAcquired = isSemaphoreAsyncSizeAcquired;
+ defaultMQProducer.releaseBackPressureForAsyncSendSizeLock();
if (!isSemaphoreAsyncSizeAcquired) {
sendCallback.onException(
new RemotingTooMuchRequestException("send message tryAcquire semaphoreAsyncSize timeout"));
return;
}
}
- sendCallback.isSemaphoreAsyncSizeAcquired = isSemaphoreAsyncSizeAcquired;
- sendCallback.isSemaphoreAsyncNumbAcquired = isSemaphoreAsyncNumbAcquired;
- sendCallback.msgLen = msgLen;
+
executor.submit(runnable);
} catch (RejectedExecutionException e) {
if (isEnableBackpressureForAsyncMode) {
@@ -969,7 +1010,7 @@ private SendResult sendKernelImpl(final Message msg,
boolean messageCloned = false;
if (msgBodyCompressed) {
//If msg body was compressed, msgbody should be reset using prevBody.
- //Clone new message using commpressed message body and recover origin massage.
+ //Clone new message using compressed message body and recover origin massage.
//Fix bug:https://github.com/apache/rocketmq-externals/issues/66
tmpMessage = MessageAccessor.cloneMessage(msg);
messageCloned = true;
@@ -1538,6 +1579,7 @@ public Message request(final Message msg,
@Override
public void onSuccess(SendResult sendResult) {
requestResponseFuture.setSendRequestOk(true);
+ requestResponseFuture.acquireCountDownLatch();
}
@Override
@@ -1595,6 +1637,7 @@ public Message request(final Message msg, final MessageQueueSelector selector, f
@Override
public void onSuccess(SendResult sendResult) {
requestResponseFuture.setSendRequestOk(true);
+ requestResponseFuture.acquireCountDownLatch();
}
@Override
@@ -1652,6 +1695,7 @@ public Message request(final Message msg, final MessageQueue mq, final long time
@Override
public void onSuccess(SendResult sendResult) {
requestResponseFuture.setSendRequestOk(true);
+ requestResponseFuture.acquireCountDownLatch();
}
@Override
diff --git a/client/src/main/java/org/apache/rocketmq/client/lock/ReadWriteCASLock.java b/client/src/main/java/org/apache/rocketmq/client/lock/ReadWriteCASLock.java
new file mode 100644
index 00000000000..3d157313715
--- /dev/null
+++ b/client/src/main/java/org/apache/rocketmq/client/lock/ReadWriteCASLock.java
@@ -0,0 +1,63 @@
+/*
+ * 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.rocketmq.client.lock;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ReadWriteCASLock {
+ //true : can lock ; false : not lock
+ private final AtomicBoolean writeLock = new AtomicBoolean(true);
+
+ private final AtomicInteger readLock = new AtomicInteger(0);
+
+ public void acquireWriteLock() {
+ boolean isLock = false;
+ do {
+ isLock = writeLock.compareAndSet(true, false);
+ } while (!isLock);
+
+ do {
+ isLock = readLock.get() == 0;
+ } while (!isLock);
+ }
+
+ public void releaseWriteLock() {
+ this.writeLock.compareAndSet(false, true);
+ }
+
+ public void acquireReadLock() {
+ boolean isLock = false;
+ do {
+ isLock = writeLock.get();
+ } while (!isLock);
+ readLock.getAndIncrement();
+ }
+
+ public void releaseReadLock() {
+ this.readLock.getAndDecrement();
+ }
+
+ public boolean getWriteLock() {
+ return this.writeLock.get() && this.readLock.get() == 0;
+ }
+
+ public boolean getReadLock() {
+ return this.writeLock.get();
+ }
+
+}
diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java b/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java
index 4fd038663b5..f0842de8ba7 100644
--- a/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java
@@ -24,6 +24,7 @@
import org.apache.rocketmq.client.exception.RequestTimeoutException;
import org.apache.rocketmq.client.impl.MQClientManager;
import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl;
+import org.apache.rocketmq.client.lock.ReadWriteCASLock;
import org.apache.rocketmq.client.trace.AsyncTraceDispatcher;
import org.apache.rocketmq.client.trace.TraceDispatcher;
import org.apache.rocketmq.client.trace.hook.EndTransactionTraceHookImpl;
@@ -79,7 +80,8 @@ public class DefaultMQProducer extends ClientConfig implements MQProducer {
ResponseCode.SYSTEM_BUSY,
ResponseCode.NO_PERMISSION,
ResponseCode.NO_BUYER_ID,
- ResponseCode.NOT_IN_CURRENT_UNIT
+ ResponseCode.NOT_IN_CURRENT_UNIT,
+ ResponseCode.GO_AWAY
));
/**
@@ -174,6 +176,16 @@ public class DefaultMQProducer extends ClientConfig implements MQProducer {
private RPCHook rpcHook = null;
+ /**
+ * backPressureForAsyncSendNum is guaranteed to be modified at runtime and no new requests are allowed
+ */
+ private final ReadWriteCASLock backPressureForAsyncSendNumLock = new ReadWriteCASLock();
+
+ /**
+ * backPressureForAsyncSendSize is guaranteed to be modified at runtime and no new requests are allowed
+ */
+ private final ReadWriteCASLock backPressureForAsyncSendSizeLock = new ReadWriteCASLock();
+
/**
* Compress level of compress algorithm.
*/
@@ -348,7 +360,7 @@ public void start() throws MQClientException {
}
if (enableTrace) {
try {
- AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(producerGroup, TraceDispatcher.Type.PRODUCE, traceTopic, rpcHook);
+ AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(producerGroup, TraceDispatcher.Type.PRODUCE, getTraceMsgBatchNum(), traceTopic, rpcHook);
dispatcher.setHostProducer(this.defaultMQProducerImpl);
dispatcher.setNamespaceV2(this.namespaceV2);
traceDispatcher = dispatcher;
@@ -1333,18 +1345,64 @@ public int getBackPressureForAsyncSendNum() {
return backPressureForAsyncSendNum;
}
+ /**
+ * For user modify backPressureForAsyncSendNum at runtime
+ */
public void setBackPressureForAsyncSendNum(int backPressureForAsyncSendNum) {
+ this.backPressureForAsyncSendNumLock.acquireWriteLock();
+ backPressureForAsyncSendNum = Math.max(backPressureForAsyncSendNum, 10);
+ int acquiredBackPressureForAsyncSendNum = this.backPressureForAsyncSendNum
+ - defaultMQProducerImpl.getSemaphoreAsyncSendNumAvailablePermits();
this.backPressureForAsyncSendNum = backPressureForAsyncSendNum;
- defaultMQProducerImpl.setSemaphoreAsyncSendNum(backPressureForAsyncSendNum);
+ defaultMQProducerImpl.setSemaphoreAsyncSendNum(backPressureForAsyncSendNum - acquiredBackPressureForAsyncSendNum);
+ this.backPressureForAsyncSendNumLock.releaseWriteLock();
}
public int getBackPressureForAsyncSendSize() {
return backPressureForAsyncSendSize;
}
+ /**
+ * For user modify backPressureForAsyncSendSize at runtime
+ */
public void setBackPressureForAsyncSendSize(int backPressureForAsyncSendSize) {
+ this.backPressureForAsyncSendSizeLock.acquireWriteLock();
+ backPressureForAsyncSendSize = Math.max(backPressureForAsyncSendSize, 1024 * 1024);
+ int acquiredBackPressureForAsyncSendSize = this.backPressureForAsyncSendSize
+ - defaultMQProducerImpl.getSemaphoreAsyncSendSizeAvailablePermits();
+ this.backPressureForAsyncSendSize = backPressureForAsyncSendSize;
+ defaultMQProducerImpl.setSemaphoreAsyncSendSize(backPressureForAsyncSendSize - acquiredBackPressureForAsyncSendSize);
+ this.backPressureForAsyncSendSizeLock.releaseWriteLock();
+ }
+
+ /**
+ * Used for system internal adjust backPressureForAsyncSendSize
+ */
+ public void setBackPressureForAsyncSendSizeInsideAdjust(int backPressureForAsyncSendSize) {
this.backPressureForAsyncSendSize = backPressureForAsyncSendSize;
- defaultMQProducerImpl.setSemaphoreAsyncSendSize(backPressureForAsyncSendSize);
+ }
+
+ /**
+ * Used for system internal adjust backPressureForAsyncSendNum
+ */
+ public void setBackPressureForAsyncSendNumInsideAdjust(int backPressureForAsyncSendNum) {
+ this.backPressureForAsyncSendNum = backPressureForAsyncSendNum;
+ }
+
+ public void acquireBackPressureForAsyncSendSizeLock() {
+ this.backPressureForAsyncSendSizeLock.acquireReadLock();
+ }
+
+ public void releaseBackPressureForAsyncSendSizeLock() {
+ this.backPressureForAsyncSendSizeLock.releaseReadLock();
+ }
+
+ public void acquireBackPressureForAsyncSendNumLock() {
+ this.backPressureForAsyncSendNumLock.acquireReadLock();
+ }
+
+ public void releaseBackPressureForAsyncSendNumLock() {
+ this.backPressureForAsyncSendNumLock.releaseReadLock();
}
public List getTopics() {
diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/RequestResponseFuture.java b/client/src/main/java/org/apache/rocketmq/client/producer/RequestResponseFuture.java
index e66c08fdc53..203f92907a4 100644
--- a/client/src/main/java/org/apache/rocketmq/client/producer/RequestResponseFuture.java
+++ b/client/src/main/java/org/apache/rocketmq/client/producer/RequestResponseFuture.java
@@ -107,6 +107,10 @@ public void setSendRequestOk(boolean sendRequestOk) {
this.sendRequestOk = sendRequestOk;
}
+ public void acquireCountDownLatch() {
+ this.countDownLatch.countDown();
+ }
+
public Message getRequestMsg() {
return requestMsg;
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java b/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java
index 1fe19773a5a..e321e1583d2 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java
@@ -16,19 +16,6 @@
*/
package org.apache.rocketmq.client.trace;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import org.apache.rocketmq.client.AccessChannel;
import org.apache.rocketmq.client.common.ThreadLocalIndex;
import org.apache.rocketmq.client.exception.MQClientException;
@@ -44,68 +31,75 @@
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.topic.TopicValidator;
-import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
+import org.apache.rocketmq.remoting.RPCHook;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import static org.apache.rocketmq.client.trace.TraceConstants.TRACE_INSTANCE_NAME;
public class AsyncTraceDispatcher implements TraceDispatcher {
- private final static Logger log = LoggerFactory.getLogger(AsyncTraceDispatcher.class);
- private final static AtomicInteger COUNTER = new AtomicInteger();
- private final static short MAX_MSG_KEY_SIZE = Short.MAX_VALUE - 10000;
- private final int queueSize;
- private final int batchSize;
+ private static final Logger log = LoggerFactory.getLogger(AsyncTraceDispatcher.class);
+ private static final AtomicInteger COUNTER = new AtomicInteger();
+ private static final AtomicInteger INSTANCE_NUM = new AtomicInteger(0);
+ private volatile boolean stopped = false;
+ private final int traceInstanceId = INSTANCE_NUM.getAndIncrement();
+ private final int batchNum;
private final int maxMsgSize;
- private final long pollingTimeMil;
- private final long waitTimeThresholdMil;
private final DefaultMQProducer traceProducer;
- private final ThreadPoolExecutor traceExecutor;
- // The last discard number of log
private AtomicLong discardCount;
private Thread worker;
+ private final ThreadPoolExecutor traceExecutor;
private final ArrayBlockingQueue traceContextQueue;
- private final HashMap taskQueueByTopic;
- private ArrayBlockingQueue appenderQueue;
+ private final ArrayBlockingQueue appenderQueue;
private volatile Thread shutDownHook;
- private volatile boolean stopped = false;
+
private DefaultMQProducerImpl hostProducer;
private DefaultMQPushConsumerImpl hostConsumer;
private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();
- private String dispatcherId = UUID.randomUUID().toString();
private volatile String traceTopicName;
private AtomicBoolean isStarted = new AtomicBoolean(false);
private volatile AccessChannel accessChannel = AccessChannel.LOCAL;
private String group;
private Type type;
private String namespaceV2;
+ private final int flushTraceInterval = 5000;
+
+ private long lastFlushTime = System.currentTimeMillis();
- public AsyncTraceDispatcher(String group, Type type, String traceTopicName, RPCHook rpcHook) {
- // queueSize is greater than or equal to the n power of 2 of value
- this.queueSize = 2048;
- this.batchSize = 100;
+ public AsyncTraceDispatcher(String group, Type type, int batchNum, String traceTopicName, RPCHook rpcHook) {
+ this.batchNum = Math.min(batchNum, 20);/* max value 20*/
this.maxMsgSize = 128000;
- this.pollingTimeMil = 100;
- this.waitTimeThresholdMil = 500;
this.discardCount = new AtomicLong(0L);
- this.traceContextQueue = new ArrayBlockingQueue<>(1024);
- this.taskQueueByTopic = new HashMap();
+ this.traceContextQueue = new ArrayBlockingQueue<>(2048);
this.group = group;
this.type = type;
-
- this.appenderQueue = new ArrayBlockingQueue<>(queueSize);
+ this.appenderQueue = new ArrayBlockingQueue<>(2048);
if (!UtilAll.isBlank(traceTopicName)) {
this.traceTopicName = traceTopicName;
} else {
this.traceTopicName = TopicValidator.RMQ_SYS_TRACE_TOPIC;
}
this.traceExecutor = new ThreadPoolExecutor(//
- 10, //
- 20, //
+ 2, //
+ 4, //
1000 * 60, //
TimeUnit.MILLISECONDS, //
this.appenderQueue, //
- new ThreadFactoryImpl("MQTraceSendThread_"));
+ new ThreadFactoryImpl("MQTraceSendThread_" + traceInstanceId + "_"));
traceProducer = getAndCreateTraceProducer(rpcHook);
}
@@ -153,7 +147,6 @@ public void setNamespaceV2(String namespaceV2) {
this.namespaceV2 = namespaceV2;
}
- @Override
public void start(String nameSrvAddr, AccessChannel accessChannel) throws MQClientException {
if (isStarted.compareAndSet(false, true)) {
traceProducer.setNamesrvAddr(nameSrvAddr);
@@ -163,7 +156,8 @@ public void start(String nameSrvAddr, AccessChannel accessChannel) throws MQClie
traceProducer.start();
}
this.accessChannel = accessChannel;
- this.worker = new Thread(new AsyncRunnable(), "MQ-AsyncTraceDispatcher-Thread-" + dispatcherId);
+ this.worker = new ThreadFactoryImpl("MQ-AsyncArrayDispatcher-Thread" + traceInstanceId, true)
+ .newThread(new AsyncRunnable());
this.worker.setDaemon(true);
this.worker.start();
this.registerShutDownHook();
@@ -197,37 +191,28 @@ public boolean append(final Object ctx) {
@Override
public void flush() {
- // The maximum waiting time for refresh,avoid being written all the time, resulting in failure to return.
long end = System.currentTimeMillis() + 500;
- while (System.currentTimeMillis() <= end) {
- synchronized (taskQueueByTopic) {
- for (TraceDataSegment taskInfo : taskQueueByTopic.values()) {
- taskInfo.sendAllData();
- }
- }
- synchronized (traceContextQueue) {
- if (traceContextQueue.size() == 0 && appenderQueue.size() == 0) {
- break;
- }
- }
+ while (traceContextQueue.size() > 0 || appenderQueue.size() > 0 && System.currentTimeMillis() <= end) {
try {
- Thread.sleep(1);
- } catch (InterruptedException e) {
- break;
+ flushTraceContext(true);
+ } catch (Throwable throwable) {
+ log.error("flushTraceContext error", throwable);
}
}
- log.info("------end trace send " + traceContextQueue.size() + " " + appenderQueue.size());
+ if (appenderQueue.size() > 0) {
+ log.error("There are still some traces that haven't been sent " + traceContextQueue.size() + " " + appenderQueue.size());
+ }
}
@Override
public void shutdown() {
- this.stopped = true;
flush();
this.traceExecutor.shutdown();
if (isStarted.get()) {
traceProducer.shutdown();
}
this.removeShutdownHook();
+ stopped = true;
}
public void registerShutDownHook() {
@@ -259,152 +244,121 @@ public void removeShutdownHook() {
}
class AsyncRunnable implements Runnable {
- private boolean stopped;
+ private volatile boolean stopped = false;
@Override
public void run() {
while (!stopped) {
- synchronized (traceContextQueue) {
- long endTime = System.currentTimeMillis() + pollingTimeMil;
- while (System.currentTimeMillis() < endTime) {
- try {
- TraceContext traceContext = traceContextQueue.poll(
- endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS
- );
-
- if (traceContext != null && !traceContext.getTraceBeans().isEmpty()) {
- // get the topic which the trace message will send to
- String traceTopicName = this.getTraceTopicName(traceContext.getRegionId());
-
- // get the traceDataSegment which will save this trace message, create if null
- TraceDataSegment traceDataSegment = taskQueueByTopic.get(traceTopicName);
- if (traceDataSegment == null) {
- traceDataSegment = new TraceDataSegment(traceTopicName, traceContext.getRegionId());
- taskQueueByTopic.put(traceTopicName, traceDataSegment);
- }
-
- // encode traceContext and save it into traceDataSegment
- // NOTE if data size in traceDataSegment more than maxMsgSize,
- // a AsyncDataSendTask will be created and submitted
- TraceTransferBean traceTransferBean = TraceDataEncoder.encoderFromContextBean(traceContext);
- traceDataSegment.addTraceTransferBean(traceTransferBean);
- }
- } catch (InterruptedException ignore) {
- log.debug("traceContextQueue#poll exception");
- }
- }
-
- // NOTE send the data in traceDataSegment which the first TraceTransferBean
- // is longer than waitTimeThreshold
- sendDataByTimeThreshold();
-
- if (AsyncTraceDispatcher.this.stopped) {
- this.stopped = true;
- }
+ try {
+ flushTraceContext(false);
+ } catch (Throwable e) {
+ log.error("flushTraceContext error", e);
}
}
-
+ if (AsyncTraceDispatcher.this.stopped) {
+ this.stopped = true;
+ }
}
+ }
- private void sendDataByTimeThreshold() {
- long now = System.currentTimeMillis();
- for (TraceDataSegment taskInfo : taskQueueByTopic.values()) {
- if (now - taskInfo.firstBeanAddTime >= waitTimeThresholdMil) {
- taskInfo.sendAllData();
+ private void flushTraceContext(boolean forceFlush) throws InterruptedException {
+ List contextList = new ArrayList<>(batchNum);
+ int size = traceContextQueue.size();
+ if (size != 0) {
+ if (forceFlush || size >= batchNum || System.currentTimeMillis() - lastFlushTime > flushTraceInterval) {
+ for (int i = 0; i < batchNum; i++) {
+ TraceContext context = traceContextQueue.poll();
+ if (context != null) {
+ contextList.add(context);
+ } else {
+ break;
+ }
}
+ asyncSendTraceMessage(contextList);
+ return;
}
}
+ // To prevent an infinite loop, add a wait time between each two task executions
+ Thread.sleep(5);
+ }
- private String getTraceTopicName(String regionId) {
- AccessChannel accessChannel = AsyncTraceDispatcher.this.getAccessChannel();
- if (AccessChannel.CLOUD == accessChannel) {
- return TraceConstants.TRACE_TOPIC_PREFIX + regionId;
- }
-
- return AsyncTraceDispatcher.this.getTraceTopicName();
- }
+ private void asyncSendTraceMessage(List contextList) {
+ AsyncDataSendTask request = new AsyncDataSendTask(contextList);
+ traceExecutor.submit(request);
+ lastFlushTime = System.currentTimeMillis();
}
- class TraceDataSegment {
- private long firstBeanAddTime;
- private int currentMsgSize;
- private int currentMsgKeySize;
- private final String traceTopicName;
- private final String regionId;
- private final List traceTransferBeanList = new ArrayList<>();
+ class AsyncDataSendTask implements Runnable {
+ private final List contextList;
- TraceDataSegment(String traceTopicName, String regionId) {
- this.traceTopicName = traceTopicName;
- this.regionId = regionId;
+ public AsyncDataSendTask(List contextList) {
+ this.contextList = contextList;
}
- public void addTraceTransferBean(TraceTransferBean traceTransferBean) {
- initFirstBeanAddTime();
- this.traceTransferBeanList.add(traceTransferBean);
- this.currentMsgSize += traceTransferBean.getTransData().length();
-
- this.currentMsgKeySize = traceTransferBean.getTransKey().stream()
- .reduce(currentMsgKeySize, (acc, x) -> acc + x.length(), Integer::sum);
- if (currentMsgSize >= traceProducer.getMaxMessageSize() - 10 * 1000 || currentMsgKeySize >= MAX_MSG_KEY_SIZE) {
- List dataToSend = new ArrayList<>(traceTransferBeanList);
- AsyncDataSendTask asyncDataSendTask = new AsyncDataSendTask(traceTopicName, regionId, dataToSend);
- traceExecutor.submit(asyncDataSendTask);
- this.clear();
- }
+ @Override
+ public void run() {
+ sendTraceData(contextList);
}
- public void sendAllData() {
- if (this.traceTransferBeanList.isEmpty()) {
- return;
- }
- List dataToSend = new ArrayList<>(traceTransferBeanList);
- AsyncDataSendTask asyncDataSendTask = new AsyncDataSendTask(traceTopicName, regionId, dataToSend);
- traceExecutor.submit(asyncDataSendTask);
-
- this.clear();
- }
+ public void sendTraceData(List contextList) {
+ Map> transBeanMap = new HashMap<>(16);
+ String traceTopic;
+ for (TraceContext context : contextList) {
+ AccessChannel accessChannel = context.getAccessChannel();
+ if (accessChannel == null) {
+ accessChannel = AsyncTraceDispatcher.this.accessChannel;
+ }
+ String currentRegionId = context.getRegionId();
+ if (currentRegionId == null || context.getTraceBeans().isEmpty()) {
+ continue;
+ }
+ if (AccessChannel.CLOUD == accessChannel) {
+ traceTopic = TraceConstants.TRACE_TOPIC_PREFIX + currentRegionId;
+ } else {
+ traceTopic = traceTopicName;
+ }
- private void initFirstBeanAddTime() {
- if (firstBeanAddTime == 0) {
- firstBeanAddTime = System.currentTimeMillis();
+ String topic = context.getTraceBeans().get(0).getTopic();
+ String key = topic + TraceConstants.CONTENT_SPLITOR + traceTopic;
+ List transBeanList = transBeanMap.computeIfAbsent(key, k -> new ArrayList<>());
+ TraceTransferBean traceData = TraceDataEncoder.encoderFromContextBean(context);
+ transBeanList.add(traceData);
+ }
+ for (Map.Entry> entry : transBeanMap.entrySet()) {
+ String[] key = entry.getKey().split(String.valueOf(TraceConstants.CONTENT_SPLITOR));
+ flushData(entry.getValue(), key[0], key[1]);
}
}
- private void clear() {
- this.firstBeanAddTime = 0;
- this.currentMsgSize = 0;
- this.currentMsgKeySize = 0;
- this.traceTransferBeanList.clear();
- }
- }
-
- class AsyncDataSendTask implements Runnable {
- private final String traceTopicName;
- private final String regionId;
- private final List traceTransferBeanList;
-
- public AsyncDataSendTask(String traceTopicName, String regionId, List traceTransferBeanList) {
- this.traceTopicName = traceTopicName;
- this.regionId = regionId;
- this.traceTransferBeanList = traceTransferBeanList;
- }
-
- @Override
- public void run() {
+ private void flushData(List transBeanList, String topic, String traceTopic) {
+ if (transBeanList.size() == 0) {
+ return;
+ }
StringBuilder buffer = new StringBuilder(1024);
- Set keySet = new HashSet<>();
- for (TraceTransferBean bean : traceTransferBeanList) {
+ int count = 0;
+ Set keySet = new HashSet();
+ for (TraceTransferBean bean : transBeanList) {
keySet.addAll(bean.getTransKey());
buffer.append(bean.getTransData());
+ count++;
+ if (buffer.length() >= traceProducer.getMaxMessageSize()) {
+ sendTraceDataByMQ(keySet, buffer.toString(), traceTopic);
+ buffer.delete(0, buffer.length());
+ keySet.clear();
+ count = 0;
+ }
+ }
+ if (count > 0) {
+ sendTraceDataByMQ(keySet, buffer.toString(), traceTopic);
}
- sendTraceDataByMQ(keySet, buffer.toString(), traceTopicName);
+ transBeanList.clear();
}
/**
* Send message trace data
*
- * @param keySet the keyset in this batch(including msgId in original message not offsetMsgId)
- * @param data the message trace data in this batch
+ * @param keySet the keyset in this batch(including msgId in original message not offsetMsgId)
+ * @param data the message trace data in this batch
* @param traceTopic the topic which message trace data will send to
*/
private void sendTraceDataByMQ(Set keySet, final String data, String traceTopic) {
@@ -467,4 +421,4 @@ private Set tryGetMessageQueueBrokerSet(DefaultMQProducerImpl producer,
}
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java
index 70c147e1eb3..17db1fbfa15 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java
@@ -21,7 +21,7 @@
import org.apache.rocketmq.common.message.MessageType;
public class TraceBean {
- private static final String LOCAL_ADDRESS = UtilAll.ipToIPv4Str(UtilAll.getIP());
+ private static final String LOCAL_ADDRESS;
private String topic = "";
private String msgId = "";
private String offsetMsgId = "";
@@ -37,6 +37,15 @@ public class TraceBean {
private String transactionId;
private boolean fromTransactionCheck;
+ static {
+ byte[] ip = UtilAll.getIP();
+ if (ip.length == 4) {
+ LOCAL_ADDRESS = UtilAll.ipToIPv4Str(ip);
+ } else {
+ LOCAL_ADDRESS = UtilAll.ipToIPv6Str(ip);
+ }
+ }
+
public MessageType getMsgType() {
return msgType;
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java
index 1ad4b610515..67f7ab3f8fb 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java
@@ -32,7 +32,7 @@ public class TraceConstants {
public static final String ROCKETMQ_SUCCESS = "rocketmq.success";
public static final String ROCKETMQ_TAGS = "rocketmq.tags";
public static final String ROCKETMQ_KEYS = "rocketmq.keys";
- public static final String ROCKETMQ_SOTRE_HOST = "rocketmq.store_host";
+ public static final String ROCKETMQ_STORE_HOST = "rocketmq.store_host";
public static final String ROCKETMQ_BODY_LENGTH = "rocketmq.body_length";
public static final String ROCKETMQ_MSG_ID = "rocketmq.mgs_id";
public static final String ROCKETMQ_MSG_TYPE = "rocketmq.mgs_type";
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java
index 0fdd95243a5..57e9b6410db 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java
@@ -193,9 +193,10 @@ public static TraceTransferBean encoderFromContextBean(TraceContext ctx) {
.append(bean.getKeys()).append(TraceConstants.CONTENT_SPLITOR)//
.append(ctx.getContextCode()).append(TraceConstants.CONTENT_SPLITOR);
if (!ctx.getAccessChannel().equals(AccessChannel.CLOUD)) {
- sb.append(ctx.getTimeStamp()).append(TraceConstants.CONTENT_SPLITOR)
- .append(ctx.getGroupName()).append(TraceConstants.FIELD_SPLITOR);
+ sb.append(ctx.getTimeStamp()).append(TraceConstants.CONTENT_SPLITOR);
+ sb.append(ctx.getGroupName());
}
+ sb.append(TraceConstants.FIELD_SPLITOR);
}
}
break;
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java
index 62d310f1961..44e4b69776e 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java
@@ -60,7 +60,7 @@ public void endTransaction(EndTransactionContext context) {
span.setTag(Tags.MESSAGE_BUS_DESTINATION, msg.getTopic());
span.setTag(TraceConstants.ROCKETMQ_TAGS, msg.getTags());
span.setTag(TraceConstants.ROCKETMQ_KEYS, msg.getKeys());
- span.setTag(TraceConstants.ROCKETMQ_SOTRE_HOST, context.getBrokerAddr());
+ span.setTag(TraceConstants.ROCKETMQ_STORE_HOST, context.getBrokerAddr());
span.setTag(TraceConstants.ROCKETMQ_MSG_ID, context.getMsgId());
span.setTag(TraceConstants.ROCKETMQ_MSG_TYPE, MessageType.Trans_msg_Commit.name());
span.setTag(TraceConstants.ROCKETMQ_TRANSACTION_ID, context.getTransactionId());
diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java
index 60c18a22a77..3cb64933849 100644
--- a/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java
@@ -60,7 +60,7 @@ public void sendMessageBefore(SendMessageContext context) {
span.setTag(Tags.MESSAGE_BUS_DESTINATION, msg.getTopic());
span.setTag(TraceConstants.ROCKETMQ_TAGS, msg.getTags());
span.setTag(TraceConstants.ROCKETMQ_KEYS, msg.getKeys());
- span.setTag(TraceConstants.ROCKETMQ_SOTRE_HOST, context.getBrokerAddr());
+ span.setTag(TraceConstants.ROCKETMQ_STORE_HOST, context.getBrokerAddr());
span.setTag(TraceConstants.ROCKETMQ_MSG_TYPE, context.getMsgType().name());
span.setTag(TraceConstants.ROCKETMQ_BODY_LENGTH, msg.getBody().length);
context.setMqTraceContext(span);
diff --git a/client/src/test/java/org/apache/rocketmq/client/ClientConfigTest.java b/client/src/test/java/org/apache/rocketmq/client/ClientConfigTest.java
new file mode 100644
index 00000000000..5afe9cc011d
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/ClientConfigTest.java
@@ -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.
+ */
+package org.apache.rocketmq.client;
+
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.remoting.protocol.LanguageCode;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ClientConfigTest {
+
+ private ClientConfig clientConfig;
+
+ private final String resource = "resource";
+
+ @Before
+ public void init() {
+ clientConfig = createClientConfig();
+ }
+
+ @Test
+ public void testWithNamespace() {
+ Set resources = clientConfig.withNamespace(Collections.singleton(resource));
+ assertTrue(resources.contains("lmq%resource"));
+ }
+
+ @Test
+ public void testWithoutNamespace() {
+ String actual = clientConfig.withoutNamespace(resource);
+ assertEquals(resource, actual);
+ Set resources = clientConfig.withoutNamespace(Collections.singleton(resource));
+ assertTrue(resources.contains(resource));
+ }
+
+ @Test
+ public void testQueuesWithNamespace() {
+ MessageQueue messageQueue = new MessageQueue();
+ messageQueue.setTopic("defaultTopic");
+ Collection messageQueues = clientConfig.queuesWithNamespace(Collections.singleton(messageQueue));
+ assertTrue(messageQueues.contains(messageQueue));
+ assertEquals("lmq%defaultTopic", messageQueues.iterator().next().getTopic());
+ }
+
+ private ClientConfig createClientConfig() {
+ ClientConfig result = new ClientConfig();
+ result.setUnitName("unitName");
+ result.setClientIP("127.0.0.1");
+ result.setClientCallbackExecutorThreads(1);
+ result.setPollNameServerInterval(1000 * 30);
+ result.setHeartbeatBrokerInterval(1000 * 30);
+ result.setPersistConsumerOffsetInterval(1000 * 5);
+ result.setPullTimeDelayMillsWhenException(1000);
+ result.setUnitMode(true);
+ result.setSocksProxyConfig("{}");
+ result.setLanguage(LanguageCode.JAVA);
+ result.setDecodeReadBody(true);
+ result.setDecodeDecompressBody(true);
+ result.setAccessChannel(AccessChannel.LOCAL);
+ result.setMqClientApiTimeout(1000 * 3);
+ result.setEnableStreamRequestType(true);
+ result.setSendLatencyEnable(true);
+ result.setEnableHeartbeatChannelEventListener(true);
+ result.setDetectTimeout(200);
+ result.setDetectInterval(1000 * 2);
+ result.setUseHeartbeatV2(false);
+ result.buildMQClientId();
+ result.setNamespace("lmq");
+ return result;
+ }
+}
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java
index 65237bc8f76..592c247057b 100644
--- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java
@@ -63,8 +63,6 @@
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.quality.Strictness;
-import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.stubbing.Answer;
import static org.assertj.core.api.Assertions.assertThat;
@@ -81,8 +79,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-@RunWith(MockitoJUnitRunner.class)
-@MockitoSettings(strictness = Strictness.LENIENT)
+@RunWith(MockitoJUnitRunner.Silent.class)
public class DefaultLitePullConsumerTest {
@Spy
private MQClientInstance mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig());
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java
index a10fd74b34f..834be5cf16f 100644
--- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java
@@ -209,7 +209,9 @@ public PullResult answer(InvocationOnMock mock) throws Throwable {
@AfterClass
public static void terminate() {
- pushConsumer.shutdown();
+ if (pushConsumer != null) {
+ pushConsumer.shutdown();
+ }
}
@Test
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/store/ControllableOffsetTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/store/ControllableOffsetTest.java
new file mode 100644
index 00000000000..23a56ca51ca
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/store/ControllableOffsetTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.rocketmq.client.consumer.store;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class ControllableOffsetTest {
+
+ private ControllableOffset controllableOffset;
+
+ @Before
+ public void setUp() {
+ controllableOffset = new ControllableOffset(0);
+ }
+
+ @Test
+ public void testUpdateAndFreeze_ShouldFreezeOffsetAtTargetValue() {
+ controllableOffset.updateAndFreeze(100);
+ assertEquals(100, controllableOffset.getOffset());
+ controllableOffset.update(200);
+ assertEquals(100, controllableOffset.getOffset());
+ }
+
+ @Test
+ public void testUpdate_ShouldUpdateOffsetWhenNotFrozen() {
+ controllableOffset.update(200);
+ assertEquals(200, controllableOffset.getOffset());
+ }
+
+ @Test
+ public void testUpdate_ShouldNotUpdateOffsetWhenFrozen() {
+ controllableOffset.updateAndFreeze(100);
+ controllableOffset.update(200);
+ assertEquals(100, controllableOffset.getOffset());
+ }
+
+ @Test
+ public void testUpdate_ShouldNotDecreaseOffsetWhenIncreaseOnly() {
+ controllableOffset.update(200);
+ controllableOffset.update(100, true);
+ assertEquals(200, controllableOffset.getOffset());
+ }
+
+ @Test
+ public void testUpdate_ShouldUpdateOffsetToGreaterValueWhenIncreaseOnly() {
+ controllableOffset.update(100);
+ controllableOffset.update(200, true);
+ assertEquals(200, controllableOffset.getOffset());
+ }
+
+}
diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/ClientRemotingProcessorTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/ClientRemotingProcessorTest.java
new file mode 100644
index 00000000000..ed31aa10430
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/impl/ClientRemotingProcessorTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.rocketmq.client.impl;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.rocketmq.client.ClientConfig;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.impl.factory.MQClientInstance;
+import org.apache.rocketmq.client.impl.producer.MQProducerInner;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+import org.apache.rocketmq.remoting.protocol.RequestCode;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
+import org.apache.rocketmq.remoting.protocol.body.ResetOffsetBody;
+import org.apache.rocketmq.remoting.protocol.header.CheckTransactionStateRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerRunningInfoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerStatusRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.NotifyConsumerIdsChangedRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ReplyMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ResetOffsetRequestHeader;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import static org.apache.rocketmq.common.message.MessageDecoder.NAME_VALUE_SEPARATOR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ClientRemotingProcessorTest {
+
+ @Mock
+ private MQClientInstance mQClientFactory;
+
+ private ClientRemotingProcessor processor;
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBroker = "defaultBroker";
+
+ private final String defaultGroup = "defaultGroup";
+
+ @Before
+ public void init() throws RemotingException, InterruptedException, MQClientException {
+ processor = new ClientRemotingProcessor(mQClientFactory);
+ ClientConfig clientConfig = mock(ClientConfig.class);
+ when(clientConfig.getNamespace()).thenReturn("namespace");
+ when(mQClientFactory.getClientConfig()).thenReturn(clientConfig);
+ MQProducerInner producerInner = mock(MQProducerInner.class);
+ when(mQClientFactory.selectProducer(defaultGroup)).thenReturn(producerInner);
+ }
+
+ @Test
+ public void testCheckTransactionState() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.CHECK_TRANSACTION_STATE);
+ when(request.getBody()).thenReturn(getMessageResult());
+ CheckTransactionStateRequestHeader requestHeader = new CheckTransactionStateRequestHeader();
+ when(request.decodeCommandCustomHeader(CheckTransactionStateRequestHeader.class)).thenReturn(requestHeader);
+ assertNull(processor.processRequest(ctx, request));
+ }
+
+ @Test
+ public void testNotifyConsumerIdsChanged() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.NOTIFY_CONSUMER_IDS_CHANGED);
+ NotifyConsumerIdsChangedRequestHeader requestHeader = new NotifyConsumerIdsChangedRequestHeader();
+ when(request.decodeCommandCustomHeader(NotifyConsumerIdsChangedRequestHeader.class)).thenReturn(requestHeader);
+ assertNull(processor.processRequest(ctx, request));
+ }
+
+ @Test
+ public void testResetOffset() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.RESET_CONSUMER_CLIENT_OFFSET);
+ ResetOffsetBody offsetBody = new ResetOffsetBody();
+ when(request.getBody()).thenReturn(RemotingSerializable.encode(offsetBody));
+ ResetOffsetRequestHeader requestHeader = new ResetOffsetRequestHeader();
+ when(request.decodeCommandCustomHeader(ResetOffsetRequestHeader.class)).thenReturn(requestHeader);
+ assertNull(processor.processRequest(ctx, request));
+ }
+
+ @Test
+ public void testGetConsumeStatus() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.GET_CONSUMER_STATUS_FROM_CLIENT);
+ GetConsumerStatusRequestHeader requestHeader = new GetConsumerStatusRequestHeader();
+ when(request.decodeCommandCustomHeader(GetConsumerStatusRequestHeader.class)).thenReturn(requestHeader);
+ assertNotNull(processor.processRequest(ctx, request));
+ }
+
+ @Test
+ public void testGetConsumerRunningInfo() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.GET_CONSUMER_RUNNING_INFO);
+ ConsumerRunningInfo consumerRunningInfo = new ConsumerRunningInfo();
+ consumerRunningInfo.setJstack("jstack");
+ when(mQClientFactory.consumerRunningInfo(anyString())).thenReturn(consumerRunningInfo);
+ GetConsumerRunningInfoRequestHeader requestHeader = new GetConsumerRunningInfoRequestHeader();
+ requestHeader.setJstackEnable(true);
+ requestHeader.setConsumerGroup(defaultGroup);
+ when(request.decodeCommandCustomHeader(GetConsumerRunningInfoRequestHeader.class)).thenReturn(requestHeader);
+ RemotingCommand command = processor.processRequest(ctx, request);
+ assertNotNull(command);
+ assertEquals(ResponseCode.SUCCESS, command.getCode());
+ }
+
+ @Test
+ public void testConsumeMessageDirectly() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.CONSUME_MESSAGE_DIRECTLY);
+ when(request.getBody()).thenReturn(getMessageResult());
+ ConsumeMessageDirectlyResult directlyResult = mock(ConsumeMessageDirectlyResult.class);
+ when(mQClientFactory.consumeMessageDirectly(any(MessageExt.class), anyString(), anyString())).thenReturn(directlyResult);
+ ConsumeMessageDirectlyResultRequestHeader requestHeader = new ConsumeMessageDirectlyResultRequestHeader();
+ requestHeader.setConsumerGroup(defaultGroup);
+ requestHeader.setBrokerName(defaultBroker);
+ when(request.decodeCommandCustomHeader(ConsumeMessageDirectlyResultRequestHeader.class)).thenReturn(requestHeader);
+ RemotingCommand command = processor.processRequest(ctx, request);
+ assertNotNull(command);
+ assertEquals(ResponseCode.SUCCESS, command.getCode());
+ }
+
+ @Test
+ public void testReceiveReplyMessage() throws Exception {
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ RemotingCommand request = mock(RemotingCommand.class);
+ when(request.getCode()).thenReturn(RequestCode.PUSH_REPLY_MESSAGE_TO_CLIENT);
+ when(request.getBody()).thenReturn(getMessageResult());
+ when(request.decodeCommandCustomHeader(ReplyMessageRequestHeader.class)).thenReturn(createReplyMessageRequestHeader());
+ when(request.getBody()).thenReturn(new byte[1]);
+ RemotingCommand command = processor.processRequest(ctx, request);
+ assertNotNull(command);
+ assertEquals(ResponseCode.SUCCESS, command.getCode());
+ }
+
+ private ReplyMessageRequestHeader createReplyMessageRequestHeader() {
+ ReplyMessageRequestHeader result = new ReplyMessageRequestHeader();
+ result.setTopic(defaultTopic);
+ result.setQueueId(0);
+ result.setStoreTimestamp(System.currentTimeMillis());
+ result.setBornTimestamp(System.currentTimeMillis());
+ result.setReconsumeTimes(1);
+ result.setBornHost("127.0.0.1:12911");
+ result.setStoreHost("127.0.0.1:10911");
+ result.setSysFlag(1);
+ result.setFlag(1);
+ result.setProperties("CORRELATION_ID" + NAME_VALUE_SEPARATOR + "1");
+ return result;
+ }
+
+ private byte[] getMessageResult() throws Exception {
+ byte[] bytes = MessageDecoder.encode(createMessageExt(), false);
+ ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
+ byteBuffer.put(bytes);
+ return byteBuffer.array();
+ }
+
+ private MessageExt createMessageExt() {
+ MessageExt result = new MessageExt();
+ result.setBody("body".getBytes(StandardCharsets.UTF_8));
+ result.setTopic(defaultTopic);
+ result.setBrokerName(defaultBroker);
+ result.putUserProperty("key", "value");
+ result.getProperties().put(MessageConst.PROPERTY_PRODUCER_GROUP, defaultGroup);
+ result.getProperties().put(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, "TX1");
+ result.setKeys("keys");
+ SocketAddress bornHost = new InetSocketAddress("127.0.0.1", 12911);
+ SocketAddress storeHost = new InetSocketAddress("127.0.0.1", 10911);
+ result.setBornHost(bornHost);
+ result.setStoreHost(storeHost);
+ return result;
+ }
+}
diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/MQAdminImplTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/MQAdminImplTest.java
new file mode 100644
index 00000000000..f52aba2dc00
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/impl/MQAdminImplTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.rocketmq.client.impl;
+
+import org.apache.rocketmq.client.ClientConfig;
+import org.apache.rocketmq.client.QueryResult;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.impl.factory.MQClientInstance;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.sysflag.MessageSysFlag;
+import org.apache.rocketmq.remoting.InvokeCallback;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.header.QueryMessageResponseHeader;
+import org.apache.rocketmq.remoting.protocol.route.BrokerData;
+import org.apache.rocketmq.remoting.protocol.route.QueueData;
+import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MQAdminImplTest {
+
+ @Mock
+ private MQClientInstance mQClientFactory;
+
+ @Mock
+ private MQClientAPIImpl mQClientAPIImpl;
+
+ private MQAdminImpl mqAdminImpl;
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBroker = "defaultBroker";
+
+ private final String defaultCluster = "defaultCluster";
+
+ private final String defaultBrokerAddr = "127.0.0.1:10911";
+
+ private final long defaultTimeout = 3000L;
+
+ @Before
+ public void init() throws RemotingException, InterruptedException, MQClientException {
+ when(mQClientFactory.getMQClientAPIImpl()).thenReturn(mQClientAPIImpl);
+ when(mQClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(createRouteData());
+ ClientConfig clientConfig = mock(ClientConfig.class);
+ when(clientConfig.getNamespace()).thenReturn("namespace");
+ when(mQClientFactory.getClientConfig()).thenReturn(clientConfig);
+ when(mQClientFactory.findBrokerAddressInPublish(any())).thenReturn(defaultBrokerAddr);
+ when(mQClientFactory.getAnExistTopicRouteData(any())).thenReturn(createRouteData());
+ mqAdminImpl = new MQAdminImpl(mQClientFactory);
+ }
+
+ @Test
+ public void assertTimeoutMillis() {
+ assertEquals(6000L, mqAdminImpl.getTimeoutMillis());
+ mqAdminImpl.setTimeoutMillis(defaultTimeout);
+ assertEquals(defaultTimeout, mqAdminImpl.getTimeoutMillis());
+ }
+
+ @Test
+ public void testCreateTopic() throws MQClientException {
+ mqAdminImpl.createTopic("", defaultTopic, 6);
+ }
+
+ @Test
+ public void assertFetchPublishMessageQueues() throws MQClientException {
+ List queueList = mqAdminImpl.fetchPublishMessageQueues(defaultTopic);
+ assertNotNull(queueList);
+ assertEquals(6, queueList.size());
+ for (MessageQueue each : queueList) {
+ assertEquals(defaultTopic, each.getTopic());
+ assertEquals(defaultBroker, each.getBrokerName());
+ }
+ }
+
+ @Test
+ public void assertFetchSubscribeMessageQueues() throws MQClientException {
+ Set queueList = mqAdminImpl.fetchSubscribeMessageQueues(defaultTopic);
+ assertNotNull(queueList);
+ assertEquals(6, queueList.size());
+ for (MessageQueue each : queueList) {
+ assertEquals(defaultTopic, each.getTopic());
+ assertEquals(defaultBroker, each.getBrokerName());
+ }
+ }
+
+ @Test
+ public void assertSearchOffset() throws MQClientException {
+ assertEquals(0, mqAdminImpl.searchOffset(new MessageQueue(), defaultTimeout));
+ }
+
+ @Test
+ public void assertMaxOffset() throws MQClientException {
+ assertEquals(0, mqAdminImpl.maxOffset(new MessageQueue()));
+ }
+
+ @Test
+ public void assertMinOffset() throws MQClientException {
+ assertEquals(0, mqAdminImpl.minOffset(new MessageQueue()));
+ }
+
+ @Test
+ public void assertEarliestMsgStoreTime() throws MQClientException {
+ assertEquals(0, mqAdminImpl.earliestMsgStoreTime(new MessageQueue()));
+ }
+
+ @Test(expected = MQClientException.class)
+ public void assertViewMessage() throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
+ MessageExt actual = mqAdminImpl.viewMessage(defaultTopic, "1");
+ assertNotNull(actual);
+ }
+
+ @Test
+ public void assertQueryMessage() throws InterruptedException, MQClientException, MQBrokerException, RemotingException {
+ doAnswer(invocation -> {
+ InvokeCallback callback = invocation.getArgument(3);
+ QueryMessageResponseHeader responseHeader = new QueryMessageResponseHeader();
+ responseHeader.setIndexLastUpdatePhyoffset(1L);
+ responseHeader.setIndexLastUpdateTimestamp(System.currentTimeMillis());
+ RemotingCommand response = mock(RemotingCommand.class);
+ when(response.decodeCommandCustomHeader(QueryMessageResponseHeader.class)).thenReturn(responseHeader);
+ when(response.getBody()).thenReturn(getMessageResult());
+ when(response.getCode()).thenReturn(ResponseCode.SUCCESS);
+ callback.operationSucceed(response);
+ return null;
+ }).when(mQClientAPIImpl).queryMessage(anyString(), any(), anyLong(), any(InvokeCallback.class), any());
+ QueryResult actual = mqAdminImpl.queryMessage(defaultTopic, "keys", 100, 1L, 50L);
+ assertNotNull(actual);
+ assertEquals(1, actual.getMessageList().size());
+ assertEquals(defaultTopic, actual.getMessageList().get(0).getTopic());
+ }
+
+ @Test
+ public void assertQueryMessageByUniqKey() throws InterruptedException, MQClientException, MQBrokerException, RemotingException {
+ doAnswer(invocation -> {
+ InvokeCallback callback = invocation.getArgument(3);
+ QueryMessageResponseHeader responseHeader = new QueryMessageResponseHeader();
+ responseHeader.setIndexLastUpdatePhyoffset(1L);
+ responseHeader.setIndexLastUpdateTimestamp(System.currentTimeMillis());
+ RemotingCommand response = mock(RemotingCommand.class);
+ when(response.decodeCommandCustomHeader(QueryMessageResponseHeader.class)).thenReturn(responseHeader);
+ when(response.getBody()).thenReturn(getMessageResult());
+ when(response.getCode()).thenReturn(ResponseCode.SUCCESS);
+ callback.operationSucceed(response);
+ return null;
+ }).when(mQClientAPIImpl).queryMessage(anyString(), any(), anyLong(), any(InvokeCallback.class), any());
+ String msgId = buildMsgId();
+ MessageExt actual = mqAdminImpl.queryMessageByUniqKey(defaultTopic, msgId);
+ assertNotNull(actual);
+ assertEquals(msgId, actual.getMsgId());
+ assertEquals(defaultTopic, actual.getTopic());
+ actual = mqAdminImpl.queryMessageByUniqKey(defaultCluster, defaultTopic, msgId);
+ assertNotNull(actual);
+ assertEquals(msgId, actual.getMsgId());
+ assertEquals(defaultTopic, actual.getTopic());
+ QueryResult queryResult = mqAdminImpl.queryMessageByUniqKey(defaultTopic, msgId, 1, 0L, 1L);
+ assertNotNull(queryResult);
+ assertEquals(1, queryResult.getMessageList().size());
+ assertEquals(defaultTopic, queryResult.getMessageList().get(0).getTopic());
+ }
+
+ private String buildMsgId() {
+ MessageExt msgExt = createMessageExt();
+ int storeHostIPLength = (msgExt.getFlag() & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 : 16;
+ int msgIDLength = storeHostIPLength + 4 + 8;
+ ByteBuffer byteBufferMsgId = ByteBuffer.allocate(msgIDLength);
+ return MessageDecoder.createMessageId(byteBufferMsgId, msgExt.getStoreHostBytes(), msgExt.getCommitLogOffset());
+ }
+
+ private TopicRouteData createRouteData() {
+ TopicRouteData result = new TopicRouteData();
+ result.setBrokerDatas(createBrokerData());
+ result.setQueueDatas(createQueueData());
+ return result;
+ }
+
+ private List createBrokerData() {
+ HashMap brokerAddrs = new HashMap<>();
+ brokerAddrs.put(MixAll.MASTER_ID, defaultBrokerAddr);
+ return Collections.singletonList(new BrokerData(defaultCluster, defaultBroker, brokerAddrs));
+ }
+
+ private List createQueueData() {
+ QueueData queueData = new QueueData();
+ queueData.setPerm(6);
+ queueData.setBrokerName(defaultBroker);
+ queueData.setReadQueueNums(6);
+ queueData.setWriteQueueNums(6);
+ return Collections.singletonList(queueData);
+ }
+
+ private byte[] getMessageResult() throws Exception {
+ byte[] bytes = MessageDecoder.encode(createMessageExt(), false);
+ ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
+ byteBuffer.put(bytes);
+ return byteBuffer.array();
+ }
+
+ private MessageExt createMessageExt() {
+ MessageExt result = new MessageExt();
+ result.setBody("body".getBytes(StandardCharsets.UTF_8));
+ result.setTopic(defaultTopic);
+ result.setBrokerName(defaultBroker);
+ result.putUserProperty("key", "value");
+ result.setKeys("keys");
+ SocketAddress bornHost = new InetSocketAddress("127.0.0.1", 12911);
+ SocketAddress storeHost = new InetSocketAddress("127.0.0.1", 10911);
+ result.setBornHost(bornHost);
+ result.setStoreHost(storeHost);
+ return result;
+ }
+}
diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java
index b0876c7c0d9..e311e0c9b85 100644
--- a/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java
@@ -16,13 +16,6 @@
*/
package org.apache.rocketmq.client.impl;
-import java.lang.reflect.Field;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import org.apache.rocketmq.client.ClientConfig;
import org.apache.rocketmq.client.consumer.AckCallback;
import org.apache.rocketmq.client.consumer.AckResult;
@@ -30,6 +23,9 @@
import org.apache.rocketmq.client.consumer.PopCallback;
import org.apache.rocketmq.client.consumer.PopResult;
import org.apache.rocketmq.client.consumer.PopStatus;
+import org.apache.rocketmq.client.consumer.PullCallback;
+import org.apache.rocketmq.client.consumer.PullResult;
+import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.hook.SendMessageContext;
@@ -39,8 +35,10 @@
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageAccessor;
import org.apache.rocketmq.common.message.MessageConst;
@@ -49,20 +47,65 @@
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.message.MessageQueueAssignment;
import org.apache.rocketmq.common.message.MessageRequestMode;
+import org.apache.rocketmq.common.namesrv.TopAddressing;
+import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.InvokeCallback;
import org.apache.rocketmq.remoting.RemotingClient;
+import org.apache.rocketmq.remoting.common.HeartbeatV2Result;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.exception.RemotingConnectException;
import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.ResponseFuture;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
+import org.apache.rocketmq.remoting.protocol.admin.TopicOffset;
+import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
+import org.apache.rocketmq.remoting.protocol.body.AclInfo;
+import org.apache.rocketmq.remoting.protocol.body.BrokerMemberGroup;
+import org.apache.rocketmq.remoting.protocol.body.BrokerReplicasInfo;
+import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
+import org.apache.rocketmq.remoting.protocol.body.BrokerStatsItem;
+import org.apache.rocketmq.remoting.protocol.body.ClusterAclVersionInfo;
+import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
+import org.apache.rocketmq.remoting.protocol.body.Connection;
+import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
+import org.apache.rocketmq.remoting.protocol.body.ConsumeQueueData;
+import org.apache.rocketmq.remoting.protocol.body.ConsumeStatsList;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
+import org.apache.rocketmq.remoting.protocol.body.EpochEntryCache;
+import org.apache.rocketmq.remoting.protocol.body.GetConsumerStatusBody;
+import org.apache.rocketmq.remoting.protocol.body.GroupList;
+import org.apache.rocketmq.remoting.protocol.body.HARuntimeInfo;
+import org.apache.rocketmq.remoting.protocol.body.KVTable;
+import org.apache.rocketmq.remoting.protocol.body.LockBatchRequestBody;
+import org.apache.rocketmq.remoting.protocol.body.ProducerConnection;
+import org.apache.rocketmq.remoting.protocol.body.ProducerInfo;
+import org.apache.rocketmq.remoting.protocol.body.ProducerTableInfo;
import org.apache.rocketmq.remoting.protocol.body.QueryAssignmentResponseBody;
+import org.apache.rocketmq.remoting.protocol.body.QueryConsumeQueueResponseBody;
+import org.apache.rocketmq.remoting.protocol.body.QueryConsumeTimeSpanBody;
+import org.apache.rocketmq.remoting.protocol.body.QueryCorrectionOffsetBody;
+import org.apache.rocketmq.remoting.protocol.body.QuerySubscriptionResponseBody;
+import org.apache.rocketmq.remoting.protocol.body.QueueTimeSpan;
+import org.apache.rocketmq.remoting.protocol.body.ResetOffsetBody;
+import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
+import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
+import org.apache.rocketmq.remoting.protocol.body.TopicList;
+import org.apache.rocketmq.remoting.protocol.body.UnlockBatchRequestBody;
+import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.apache.rocketmq.remoting.protocol.header.AckMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ChangeInvisibleTimeRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ChangeInvisibleTimeResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.EndTransactionRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.ExtraInfoUtil;
+import org.apache.rocketmq.remoting.protocol.header.GetBrokerAclConfigResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.GetConsumerListByGroupResponseBody;
import org.apache.rocketmq.remoting.protocol.header.GetConsumerListByGroupResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.GetEarliestMsgStoretimeResponseHeader;
@@ -70,15 +113,33 @@
import org.apache.rocketmq.remoting.protocol.header.GetMinOffsetResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.PopMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.PopMessageResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.PullMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.PullMessageResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.SearchOffsetResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.SendMessageResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.UpdateGroupForbiddenRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.controller.ElectMasterResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.controller.GetMetaDataResponseHeader;
import org.apache.rocketmq.remoting.protocol.header.namesrv.AddWritePermOfBrokerResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.namesrv.GetKVConfigResponseHeader;
+import org.apache.rocketmq.remoting.protocol.header.namesrv.WipeWritePermOfBrokerResponseHeader;
+import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
+import org.apache.rocketmq.remoting.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
+import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.remoting.protocol.route.BrokerData;
+import org.apache.rocketmq.remoting.protocol.route.QueueData;
+import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
+import org.apache.rocketmq.remoting.protocol.statictopic.TopicConfigAndQueueMapping;
+import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingDetail;
+import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingInfo;
+import org.apache.rocketmq.remoting.protocol.subscription.GroupForbidden;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.assertj.core.api.Assertions;
import org.junit.Before;
@@ -86,34 +147,74 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class MQClientAPIImplTest {
+
private MQClientAPIImpl mqClientAPI = new MQClientAPIImpl(new NettyClientConfig(), null, null, new ClientConfig());
+
@Mock
private RemotingClient remotingClient;
+
@Mock
private DefaultMQProducerImpl defaultMQProducerImpl;
- private String brokerAddr = "127.0.0.1";
- private String brokerName = "DefaultBroker";
- private String clusterName = "DefaultCluster";
- private static String group = "FooBarGroup";
- private static String topic = "FooBar";
- private Message msg = new Message("FooBar", new byte[] {});
- private static String clientId = "127.0.0.2@UnitTest";
+ @Mock
+ private RemotingCommand response;
+
+ private final String brokerAddr = "127.0.0.1";
+
+ private final String brokerName = "DefaultBroker";
+
+ private final String clusterName = "DefaultCluster";
+
+ private final String group = "FooBarGroup";
+
+ private final String topic = "FooBar";
+
+ private final Message msg = new Message("FooBar", new byte[]{});
+
+ private final String clientId = "127.0.0.2@UnitTest";
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBrokerAddr = "127.0.0.1:10911";
+
+ private final String defaultNsAddr = "127.0.0.1:9876";
+
+ private final long defaultTimeout = 3000L;
@Before
public void init() throws Exception {
@@ -153,12 +254,9 @@ public void testSendMessageOneWay_WithException() throws RemotingException, Inte
@Test
public void testSendMessageSync_Success() throws InterruptedException, RemotingException, MQBrokerException {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createSendMessageSuccessResponse(request);
- }
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createSendMessageSuccessResponse(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
SendMessageRequestHeader requestHeader = createSendMessageRequestHeader();
@@ -173,17 +271,14 @@ public Object answer(InvocationOnMock mock) throws Throwable {
}
@Test
- public void testSendMessageSync_WithException() throws InterruptedException, RemotingException, MQBrokerException {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
- response.setCode(ResponseCode.SYSTEM_ERROR);
- response.setOpaque(request.getOpaque());
- response.setRemark("Broker is broken.");
- return response;
- }
+ public void testSendMessageSync_WithException() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
+ response.setCode(ResponseCode.SYSTEM_ERROR);
+ response.setOpaque(request.getOpaque());
+ response.setRemark("Broker is broken.");
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
SendMessageRequestHeader requestHeader = createSendMessageRequestHeader();
@@ -204,16 +299,13 @@ public void testSendMessageAsync_Success() throws RemotingException, Interrupted
3 * 1000, CommunicationMode.ASYNC, new SendMessageContext(), defaultMQProducerImpl);
assertThat(sendResult).isNull();
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- responseFuture.setResponseCommand(createSendMessageSuccessResponse(request));
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer(mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ responseFuture.setResponseCommand(createSendMessageSuccessResponse(request));
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
SendMessageContext sendMessageContext = new SendMessageContext();
sendMessageContext.setProducer(new DefaultMQProducerImpl(new DefaultMQProducer()));
@@ -266,34 +358,26 @@ public void onException(Throwable e) {
}
@Test
- public void testCreatePlainAccessConfig_Success() throws InterruptedException, RemotingException, MQBrokerException {
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createSuccessResponse4UpdateAclConfig(request);
- }
+ public void testCreatePlainAccessConfig_Success() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createSuccessResponse4UpdateAclConfig(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
PlainAccessConfig config = createUpdateAclConfig();
try {
mqClientAPI.createPlainAccessConfig(brokerAddr, config, 3 * 1000);
- } catch (MQClientException ex) {
+ } catch (MQClientException ignored) {
}
}
@Test
- public void testCreatePlainAccessConfig_Exception() throws InterruptedException, RemotingException, MQBrokerException {
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createErrorResponse4UpdateAclConfig(request);
- }
+ public void testCreatePlainAccessConfig_Exception() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createErrorResponse4UpdateAclConfig(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
PlainAccessConfig config = createUpdateAclConfig();
@@ -306,33 +390,25 @@ public Object answer(InvocationOnMock mock) throws Throwable {
}
@Test
- public void testDeleteAccessConfig_Success() throws InterruptedException, RemotingException, MQBrokerException {
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createSuccessResponse4DeleteAclConfig(request);
- }
+ public void testDeleteAccessConfig_Success() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createSuccessResponse4DeleteAclConfig(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
String accessKey = "1234567";
try {
mqClientAPI.deleteAccessConfig(brokerAddr, accessKey, 3 * 1000);
- } catch (MQClientException ex) {
+ } catch (MQClientException ignored) {
}
}
@Test
- public void testDeleteAccessConfig_Exception() throws InterruptedException, RemotingException, MQBrokerException {
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createErrorResponse4DeleteAclConfig(request);
- }
+ public void testDeleteAccessConfig_Exception() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createErrorResponse4DeleteAclConfig(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
try {
@@ -344,17 +420,14 @@ public Object answer(InvocationOnMock mock) throws Throwable {
}
@Test
- public void testResumeCheckHalfMessage_WithException() throws RemotingException, InterruptedException, MQBrokerException, MQClientException {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
- response.setCode(ResponseCode.SYSTEM_ERROR);
- response.setOpaque(request.getOpaque());
- response.setRemark("Put message back to RMQ_SYS_TRANS_HALF_TOPIC failed.");
- return response;
- }
+ public void testResumeCheckHalfMessage_WithException() throws RemotingException, InterruptedException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
+ response.setCode(ResponseCode.SYSTEM_ERROR);
+ response.setOpaque(request.getOpaque());
+ response.setRemark("Put message back to RMQ_SYS_TRANS_HALF_TOPIC failed.");
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
boolean result = mqClientAPI.resumeCheckHalfMessage(brokerAddr, "topic,", "test", 3000);
@@ -362,13 +435,10 @@ public Object answer(InvocationOnMock mock) throws Throwable {
}
@Test
- public void testResumeCheckHalfMessage_Success() throws InterruptedException, RemotingException, MQBrokerException, MQClientException {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- RemotingCommand request = mock.getArgument(1);
- return createResumeSuccessResponse(request);
- }
+ public void testResumeCheckHalfMessage_Success() throws InterruptedException, RemotingException {
+ doAnswer(mock -> {
+ RemotingCommand request = mock.getArgument(1);
+ return createResumeSuccessResponse(request);
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
boolean result = mqClientAPI.resumeCheckHalfMessage(brokerAddr, "topic", "test", 3000);
@@ -378,16 +448,13 @@ public Object answer(InvocationOnMock mock) throws Throwable {
@Test
public void testSendMessageTypeofReply() throws Exception {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- responseFuture.setResponseCommand(createSendMessageSuccessResponse(request));
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer(mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ responseFuture.setResponseCommand(createSendMessageSuccessResponse(request));
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(ArgumentMatchers.anyString(), ArgumentMatchers.any(RemotingCommand.class), ArgumentMatchers.anyLong(), ArgumentMatchers.any(InvokeCallback.class));
SendMessageContext sendMessageContext = new SendMessageContext();
sendMessageContext.setProducer(new DefaultMQProducerImpl(new DefaultMQProducer()));
@@ -410,19 +477,16 @@ public void onException(Throwable e) {
@Test
public void testQueryAssignment_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
-
- RemotingCommand response = RemotingCommand.createResponseCommand(null);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- QueryAssignmentResponseBody b = new QueryAssignmentResponseBody();
- b.setMessageQueueAssignments(Collections.singleton(new MessageQueueAssignment()));
- response.setBody(b.encode());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ QueryAssignmentResponseBody b = new QueryAssignmentResponseBody();
+ b.setMessageQueueAssignments(Collections.singleton(new MessageQueueAssignment()));
+ response.setBody(b.encode());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
Set assignments = mqClientAPI.queryAssignment(brokerAddr, topic, group, clientId, null, MessageModel.CLUSTERING, 10 * 1000);
assertThat(assignments).size().isEqualTo(1);
@@ -432,48 +496,45 @@ public RemotingCommand answer(InvocationOnMock mock) {
public void testPopMessageAsync_Success() throws Exception {
final long popTime = System.currentTimeMillis();
final int invisibleTime = 10 * 1000;
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
-
- PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
- responseHeader.setInvisibleTime(invisibleTime);
- responseHeader.setPopTime(popTime);
- responseHeader.setReviveQid(0);
- responseHeader.setRestNum(1);
- StringBuilder startOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
- responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
- StringBuilder msgOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
- responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
- response.setRemark("FOUND");
- response.makeCustomHeaderToNet();
-
- MessageExt message = new MessageExt();
- message.setQueueId(0);
- message.setFlag(12);
- message.setQueueOffset(0L);
- message.setCommitLogOffset(100L);
- message.setSysFlag(0);
- message.setBornTimestamp(System.currentTimeMillis());
- message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
- message.setStoreTimestamp(System.currentTimeMillis());
- message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
- message.setBody("body".getBytes());
- message.setTopic(topic);
- message.putUserProperty("key", "value");
- response.setBody(MessageDecoder.encode(message, false));
- responseFuture.setResponseCommand(response);
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer((Answer) mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+
+ PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
+ responseHeader.setInvisibleTime(invisibleTime);
+ responseHeader.setPopTime(popTime);
+ responseHeader.setReviveQid(0);
+ responseHeader.setRestNum(1);
+ StringBuilder startOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
+ responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
+ StringBuilder msgOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
+ responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
+ response.setRemark("FOUND");
+ response.makeCustomHeaderToNet();
+
+ MessageExt message = new MessageExt();
+ message.setQueueId(0);
+ message.setFlag(12);
+ message.setQueueOffset(0L);
+ message.setCommitLogOffset(100L);
+ message.setSysFlag(0);
+ message.setBornTimestamp(System.currentTimeMillis());
+ message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
+ message.setStoreTimestamp(System.currentTimeMillis());
+ message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
+ message.setBody("body".getBytes());
+ message.setTopic(topic);
+ message.putUserProperty("key", "value");
+ response.setBody(MessageDecoder.encode(message, false));
+ responseFuture.setResponseCommand(response);
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
final CountDownLatch done = new CountDownLatch(1);
mqClientAPI.popMessageAsync(brokerName, brokerAddr, new PopMessageRequestHeader(), 10 * 1000, new PopCallback() {
@@ -501,50 +562,47 @@ public void testPopLmqMessage_async() throws Exception {
final long popTime = System.currentTimeMillis();
final int invisibleTime = 10 * 1000;
final String lmqTopic = MixAll.LMQ_PREFIX + "lmq1";
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
-
- PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
- responseHeader.setInvisibleTime(invisibleTime);
- responseHeader.setPopTime(popTime);
- responseHeader.setReviveQid(0);
- responseHeader.setRestNum(1);
- StringBuilder startOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
- responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
- StringBuilder msgOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
- responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
- response.setRemark("FOUND");
- response.makeCustomHeaderToNet();
-
- MessageExt message = new MessageExt();
- message.setQueueId(3);
- message.setFlag(0);
- message.setQueueOffset(5L);
- message.setCommitLogOffset(11111L);
- message.setSysFlag(0);
- message.setBornTimestamp(System.currentTimeMillis());
- message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
- message.setStoreTimestamp(System.currentTimeMillis());
- message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
- message.setBody("body".getBytes());
- message.setTopic(topic);
- message.putUserProperty("key", "value");
- message.putUserProperty(MessageConst.PROPERTY_INNER_MULTI_DISPATCH, lmqTopic);
- message.getProperties().put(MessageConst.PROPERTY_INNER_MULTI_QUEUE_OFFSET, String.valueOf(0));
- response.setBody(MessageDecoder.encode(message, false));
- responseFuture.setResponseCommand(response);
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer((Answer) mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+
+ PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
+ responseHeader.setInvisibleTime(invisibleTime);
+ responseHeader.setPopTime(popTime);
+ responseHeader.setReviveQid(0);
+ responseHeader.setRestNum(1);
+ StringBuilder startOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
+ responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
+ StringBuilder msgOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
+ responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
+ response.setRemark("FOUND");
+ response.makeCustomHeaderToNet();
+
+ MessageExt message = new MessageExt();
+ message.setQueueId(3);
+ message.setFlag(0);
+ message.setQueueOffset(5L);
+ message.setCommitLogOffset(11111L);
+ message.setSysFlag(0);
+ message.setBornTimestamp(System.currentTimeMillis());
+ message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
+ message.setStoreTimestamp(System.currentTimeMillis());
+ message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
+ message.setBody("body".getBytes());
+ message.setTopic(topic);
+ message.putUserProperty("key", "value");
+ message.putUserProperty(MessageConst.PROPERTY_INNER_MULTI_DISPATCH, lmqTopic);
+ message.getProperties().put(MessageConst.PROPERTY_INNER_MULTI_QUEUE_OFFSET, String.valueOf(0));
+ response.setBody(MessageDecoder.encode(message, false));
+ responseFuture.setResponseCommand(response);
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
final CountDownLatch done = new CountDownLatch(1);
final PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
@@ -580,49 +638,46 @@ public void testPopMultiLmqMessage_async() throws Exception {
final String lmqTopic2 = MixAll.LMQ_PREFIX + "lmq2";
final String multiDispatch = String.join(MixAll.MULTI_DISPATCH_QUEUE_SPLITTER, lmqTopic, lmqTopic2);
final String multiOffset = String.join(MixAll.MULTI_DISPATCH_QUEUE_SPLITTER, "0", "0");
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
-
- PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
- responseHeader.setInvisibleTime(invisibleTime);
- responseHeader.setPopTime(popTime);
- responseHeader.setReviveQid(0);
- responseHeader.setRestNum(1);
- StringBuilder startOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
- responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
- StringBuilder msgOffsetInfo = new StringBuilder(64);
- ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
- responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
- response.setRemark("FOUND");
- response.makeCustomHeaderToNet();
-
- MessageExt message = new MessageExt();
- message.setQueueId(0);
- message.setFlag(0);
- message.setQueueOffset(10L);
- message.setCommitLogOffset(10000L);
- message.setSysFlag(0);
- message.setBornTimestamp(System.currentTimeMillis());
- message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
- message.setStoreTimestamp(System.currentTimeMillis());
- message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
- message.setBody("body".getBytes());
- message.setTopic(topic);
- MessageAccessor.putProperty(message, MessageConst.PROPERTY_INNER_MULTI_DISPATCH, multiDispatch);
- MessageAccessor.putProperty(message, MessageConst.PROPERTY_INNER_MULTI_QUEUE_OFFSET, multiOffset);
- response.setBody(MessageDecoder.encode(message, false));
- responseFuture.setResponseCommand(response);
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer((Answer) mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+
+ PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
+ responseHeader.setInvisibleTime(invisibleTime);
+ responseHeader.setPopTime(popTime);
+ responseHeader.setReviveQid(0);
+ responseHeader.setRestNum(1);
+ StringBuilder startOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, topic, 0, 0L);
+ responseHeader.setStartOffsetInfo(startOffsetInfo.toString());
+ StringBuilder msgOffsetInfo = new StringBuilder(64);
+ ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, topic, 0, Collections.singletonList(0L));
+ responseHeader.setMsgOffsetInfo(msgOffsetInfo.toString());
+ response.setRemark("FOUND");
+ response.makeCustomHeaderToNet();
+
+ MessageExt message = new MessageExt();
+ message.setQueueId(0);
+ message.setFlag(0);
+ message.setQueueOffset(10L);
+ message.setCommitLogOffset(10000L);
+ message.setSysFlag(0);
+ message.setBornTimestamp(System.currentTimeMillis());
+ message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
+ message.setStoreTimestamp(System.currentTimeMillis());
+ message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
+ message.setBody("body".getBytes());
+ message.setTopic(topic);
+ MessageAccessor.putProperty(message, MessageConst.PROPERTY_INNER_MULTI_DISPATCH, multiDispatch);
+ MessageAccessor.putProperty(message, MessageConst.PROPERTY_INNER_MULTI_QUEUE_OFFSET, multiOffset);
+ response.setBody(MessageDecoder.encode(message, false));
+ responseFuture.setResponseCommand(response);
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
final CountDownLatch done = new CountDownLatch(1);
final PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
@@ -654,19 +709,16 @@ public void onException(Throwable e) {
@Test
public void testAckMessageAsync_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- RemotingCommand response = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, null);
- response.setOpaque(request.getOpaque());
- response.setCode(ResponseCode.SUCCESS);
- responseFuture.setResponseCommand(response);
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer((Answer) mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ RemotingCommand response = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, null);
+ response.setOpaque(request.getOpaque());
+ response.setCode(ResponseCode.SUCCESS);
+ responseFuture.setResponseCommand(response);
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
final CountDownLatch done = new CountDownLatch(1);
@@ -688,22 +740,19 @@ public void onException(Throwable e) {
@Test
public void testChangeInvisibleTimeAsync_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock mock) throws Throwable {
- InvokeCallback callback = mock.getArgument(3);
- RemotingCommand request = mock.getArgument(1);
- ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
- RemotingCommand response = RemotingCommand.createResponseCommand(ChangeInvisibleTimeResponseHeader.class);
- response.setOpaque(request.getOpaque());
- response.setCode(ResponseCode.SUCCESS);
- ChangeInvisibleTimeResponseHeader responseHeader = (ChangeInvisibleTimeResponseHeader) response.readCustomHeader();
- responseHeader.setPopTime(System.currentTimeMillis());
- responseHeader.setInvisibleTime(10 * 1000L);
- responseFuture.setResponseCommand(response);
- callback.operationSucceed(responseFuture.getResponseCommand());
- return null;
- }
+ doAnswer((Answer) mock -> {
+ InvokeCallback callback = mock.getArgument(3);
+ RemotingCommand request = mock.getArgument(1);
+ ResponseFuture responseFuture = new ResponseFuture(null, request.getOpaque(), 3 * 1000, null, null);
+ RemotingCommand response = RemotingCommand.createResponseCommand(ChangeInvisibleTimeResponseHeader.class);
+ response.setOpaque(request.getOpaque());
+ response.setCode(ResponseCode.SUCCESS);
+ ChangeInvisibleTimeResponseHeader responseHeader = (ChangeInvisibleTimeResponseHeader) response.readCustomHeader();
+ responseHeader.setPopTime(System.currentTimeMillis());
+ responseHeader.setInvisibleTime(10 * 1000L);
+ responseFuture.setResponseCommand(response);
+ callback.operationSucceed(responseFuture.getResponseCommand());
+ return null;
}).when(remotingClient).invokeAsync(anyString(), any(RemotingCommand.class), anyLong(), any(InvokeCallback.class));
final CountDownLatch done = new CountDownLatch(1);
@@ -730,16 +779,13 @@ public void onException(Throwable e) {
@Test
public void testSetMessageRequestMode_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- RemotingCommand response = RemotingCommand.createResponseCommand(null);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
mqClientAPI.setMessageRequestMode(brokerAddr, topic, group, MessageRequestMode.POP, 8, 10 * 1000L);
@@ -747,16 +793,13 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testCreateSubscriptionGroup_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- RemotingCommand response = RemotingCommand.createResponseCommand(null);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
mqClientAPI.createSubscriptionGroup(brokerAddr, new SubscriptionGroupConfig(), 10000);
@@ -764,16 +807,13 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testCreateTopic_Success() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- RemotingCommand response = RemotingCommand.createResponseCommand(null);
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
mqClientAPI.createTopic(brokerAddr, topic, new TopicConfig(), 10000);
@@ -781,31 +821,28 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testViewMessage() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) throws Exception {
- RemotingCommand request = mock.getArgument(1);
-
- RemotingCommand response = RemotingCommand.createResponseCommand(null);
- MessageExt message = new MessageExt();
- message.setQueueId(0);
- message.setFlag(12);
- message.setQueueOffset(0L);
- message.setCommitLogOffset(100L);
- message.setSysFlag(0);
- message.setBornTimestamp(System.currentTimeMillis());
- message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
- message.setStoreTimestamp(System.currentTimeMillis());
- message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
- message.setBody("body".getBytes());
- message.setTopic(topic);
- message.putUserProperty("key", "value");
- response.setBody(MessageDecoder.encode(message, false));
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+ MessageExt message = new MessageExt();
+ message.setQueueId(0);
+ message.setFlag(12);
+ message.setQueueOffset(0L);
+ message.setCommitLogOffset(100L);
+ message.setSysFlag(0);
+ message.setBornTimestamp(System.currentTimeMillis());
+ message.setBornHost(new InetSocketAddress("127.0.0.1", 10));
+ message.setStoreTimestamp(System.currentTimeMillis());
+ message.setStoreHost(new InetSocketAddress("127.0.0.1", 11));
+ message.setBody("body".getBytes());
+ message.setTopic(topic);
+ message.putUserProperty("key", "value");
+ response.setBody(MessageDecoder.encode(message, false));
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
MessageExt messageExt = mqClientAPI.viewMessage(brokerAddr, "topic", 100L, 10000);
@@ -814,19 +851,16 @@ public RemotingCommand answer(InvocationOnMock mock) throws Exception {
@Test
public void testSearchOffset() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
-
- final RemotingCommand response = RemotingCommand.createResponseCommand(SearchOffsetResponseHeader.class);
- final SearchOffsetResponseHeader responseHeader = (SearchOffsetResponseHeader) response.readCustomHeader();
- responseHeader.setOffset(100L);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ final RemotingCommand response = RemotingCommand.createResponseCommand(SearchOffsetResponseHeader.class);
+ final SearchOffsetResponseHeader responseHeader = (SearchOffsetResponseHeader) response.readCustomHeader();
+ responseHeader.setOffset(100L);
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long offset = mqClientAPI.searchOffset(brokerAddr, topic, 0, System.currentTimeMillis() - 1000, 10000);
@@ -835,19 +869,16 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testGetMaxOffset() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
-
- final RemotingCommand response = RemotingCommand.createResponseCommand(GetMaxOffsetResponseHeader.class);
- final GetMaxOffsetResponseHeader responseHeader = (GetMaxOffsetResponseHeader) response.readCustomHeader();
- responseHeader.setOffset(100L);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ final RemotingCommand response = RemotingCommand.createResponseCommand(GetMaxOffsetResponseHeader.class);
+ final GetMaxOffsetResponseHeader responseHeader = (GetMaxOffsetResponseHeader) response.readCustomHeader();
+ responseHeader.setOffset(100L);
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long offset = mqClientAPI.getMaxOffset(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
@@ -856,19 +887,16 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testGetMinOffset() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
-
- final RemotingCommand response = RemotingCommand.createResponseCommand(GetMinOffsetResponseHeader.class);
- final GetMinOffsetResponseHeader responseHeader = (GetMinOffsetResponseHeader) response.readCustomHeader();
- responseHeader.setOffset(100L);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ final RemotingCommand response = RemotingCommand.createResponseCommand(GetMinOffsetResponseHeader.class);
+ final GetMinOffsetResponseHeader responseHeader = (GetMinOffsetResponseHeader) response.readCustomHeader();
+ responseHeader.setOffset(100L);
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long offset = mqClientAPI.getMinOffset(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
@@ -877,19 +905,16 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testGetEarliestMsgStoretime() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
-
- final RemotingCommand response = RemotingCommand.createResponseCommand(GetEarliestMsgStoretimeResponseHeader.class);
- final GetEarliestMsgStoretimeResponseHeader responseHeader = (GetEarliestMsgStoretimeResponseHeader) response.readCustomHeader();
- responseHeader.setTimestamp(100L);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
+
+ final RemotingCommand response = RemotingCommand.createResponseCommand(GetEarliestMsgStoretimeResponseHeader.class);
+ final GetEarliestMsgStoretimeResponseHeader responseHeader = (GetEarliestMsgStoretimeResponseHeader) response.readCustomHeader();
+ responseHeader.setTimestamp(100L);
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long t = mqClientAPI.getEarliestMsgStoretime(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
@@ -898,21 +923,18 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testQueryConsumerOffset() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- final RemotingCommand response =
+ final RemotingCommand response =
RemotingCommand.createResponseCommand(QueryConsumerOffsetResponseHeader.class);
- final QueryConsumerOffsetResponseHeader responseHeader =
+ final QueryConsumerOffsetResponseHeader responseHeader =
(QueryConsumerOffsetResponseHeader) response.readCustomHeader();
- responseHeader.setOffset(100L);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ responseHeader.setOffset(100L);
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long t = mqClientAPI.queryConsumerOffset(brokerAddr, new QueryConsumerOffsetRequestHeader(), 1000);
@@ -921,18 +943,15 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testUpdateConsumerOffset() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- final RemotingCommand response =
+ final RemotingCommand response =
RemotingCommand.createResponseCommand(UpdateConsumerOffsetResponseHeader.class);
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
mqClientAPI.updateConsumerOffset(brokerAddr, new UpdateConsumerOffsetRequestHeader(), 1000);
@@ -940,21 +959,18 @@ public RemotingCommand answer(InvocationOnMock mock) {
@Test
public void testGetConsumerIdListByGroup() throws Exception {
- doAnswer(new Answer() {
- @Override
- public RemotingCommand answer(InvocationOnMock mock) {
- RemotingCommand request = mock.getArgument(1);
+ doAnswer((Answer) mock -> {
+ RemotingCommand request = mock.getArgument(1);
- final RemotingCommand response =
+ final RemotingCommand response =
RemotingCommand.createResponseCommand(GetConsumerListByGroupResponseHeader.class);
- GetConsumerListByGroupResponseBody body = new GetConsumerListByGroupResponseBody();
- body.setConsumerIdList(Collections.singletonList("consumer1"));
- response.setBody(body.encode());
- response.makeCustomHeaderToNet();
- response.setCode(ResponseCode.SUCCESS);
- response.setOpaque(request.getOpaque());
- return response;
- }
+ GetConsumerListByGroupResponseBody body = new GetConsumerListByGroupResponseBody();
+ body.setConsumerIdList(Collections.singletonList("consumer1"));
+ response.setBody(body.encode());
+ response.makeCustomHeaderToNet();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setOpaque(request.getOpaque());
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
List consumerIdList = mqClientAPI.getConsumerIdListByGroup(brokerAddr, group, 10000);
assertThat(consumerIdList).size().isGreaterThan(0);
@@ -991,7 +1007,6 @@ private RemotingCommand createSuccessResponse4UpdateAclConfig(RemotingCommand re
response.setOpaque(request.getOpaque());
response.markResponseType();
response.setRemark(null);
-
return response;
}
@@ -1001,7 +1016,6 @@ private RemotingCommand createSuccessResponse4DeleteAclConfig(RemotingCommand re
response.setOpaque(request.getOpaque());
response.markResponseType();
response.setRemark(null);
-
return response;
}
@@ -1011,7 +1025,6 @@ private RemotingCommand createErrorResponse4UpdateAclConfig(RemotingCommand requ
response.setOpaque(request.getOpaque());
response.markResponseType();
response.setRemark("corresponding to accessConfig has been updated failed");
-
return response;
}
@@ -1021,12 +1034,10 @@ private RemotingCommand createErrorResponse4DeleteAclConfig(RemotingCommand requ
response.setOpaque(request.getOpaque());
response.markResponseType();
response.setRemark("corresponding to accessConfig has been deleted failed");
-
return response;
}
private PlainAccessConfig createUpdateAclConfig() {
-
PlainAccessConfig config = new PlainAccessConfig();
config.setAccessKey("Rocketmq111");
config.setSecretKey("123456789");
@@ -1049,21 +1060,18 @@ private SendMessageRequestHeader createSendMessageRequestHeader() {
@Test
public void testAddWritePermOfBroker() throws Exception {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
- RemotingCommand request = invocationOnMock.getArgument(1);
- if (request.getCode() != RequestCode.ADD_WRITE_PERM_OF_BROKER) {
- return null;
- }
-
- RemotingCommand response = RemotingCommand.createResponseCommand(AddWritePermOfBrokerResponseHeader.class);
- AddWritePermOfBrokerResponseHeader responseHeader = (AddWritePermOfBrokerResponseHeader) response.readCustomHeader();
- response.setCode(ResponseCode.SUCCESS);
- responseHeader.setAddTopicCount(7);
- response.addExtField("addTopicCount", String.valueOf(responseHeader.getAddTopicCount()));
- return response;
+ doAnswer(invocationOnMock -> {
+ RemotingCommand request = invocationOnMock.getArgument(1);
+ if (request.getCode() != RequestCode.ADD_WRITE_PERM_OF_BROKER) {
+ return null;
}
+
+ RemotingCommand response = RemotingCommand.createResponseCommand(AddWritePermOfBrokerResponseHeader.class);
+ AddWritePermOfBrokerResponseHeader responseHeader = (AddWritePermOfBrokerResponseHeader) response.readCustomHeader();
+ response.setCode(ResponseCode.SUCCESS);
+ responseHeader.setAddTopicCount(7);
+ response.addExtField("addTopicCount", String.valueOf(responseHeader.getAddTopicCount()));
+ return response;
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
int topicCnt = mqClientAPI.addWritePermOfBroker("127.0.0.1", "default-broker", 1000);
@@ -1091,4 +1099,979 @@ public void testCreateTopicList_Success() throws RemotingException, InterruptedE
mqClientAPI.createTopicList(brokerAddr, topicConfigList, 10000);
}
+ @Test
+ public void assertFetchNameServerAddr() throws NoSuchFieldException, IllegalAccessException {
+ setTopAddressing();
+ assertEquals(defaultNsAddr, mqClientAPI.fetchNameServerAddr());
+ }
+
+ @Test
+ public void assertOnNameServerAddressChange() {
+ assertEquals(defaultNsAddr, mqClientAPI.onNameServerAddressChange(defaultNsAddr));
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testUpdateGlobalWhiteAddrsConfig() throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
+ mqClientAPI.updateGlobalWhiteAddrsConfig(defaultNsAddr, "", "", defaultTimeout);
+ }
+
+ @Test
+ public void assertGetBrokerClusterAclInfo() throws MQBrokerException, RemotingException, InterruptedException {
+ mockInvokeSync();
+ GetBrokerAclConfigResponseHeader responseHeader = mock(GetBrokerAclConfigResponseHeader.class);
+ when(responseHeader.getBrokerName()).thenReturn(brokerName);
+ when(responseHeader.getBrokerAddr()).thenReturn(defaultBrokerAddr);
+ when(responseHeader.getClusterName()).thenReturn(clusterName);
+ when(responseHeader.getAllAclFileVersion()).thenReturn("{\"key\":{\"stateVersion\":1}}");
+ setResponseHeader(responseHeader);
+ ClusterAclVersionInfo actual = mqClientAPI.getBrokerClusterAclInfo(defaultNsAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(brokerName, actual.getBrokerName());
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ assertEquals(clusterName, actual.getClusterName());
+ assertEquals(1, actual.getAllAclConfigDataVersion().size());
+ assertNull(actual.getAclConfigDataVersion());
+ }
+
+ @Test
+ public void assertPullMessage() throws MQBrokerException, RemotingException, InterruptedException {
+ PullMessageRequestHeader requestHeader = mock(PullMessageRequestHeader.class);
+ mockInvokeSync();
+ PullCallback callback = mock(PullCallback.class);
+ PullMessageResponseHeader responseHeader = mock(PullMessageResponseHeader.class);
+ setResponseHeader(responseHeader);
+ when(responseHeader.getNextBeginOffset()).thenReturn(1L);
+ when(responseHeader.getMinOffset()).thenReturn(1L);
+ when(responseHeader.getMaxOffset()).thenReturn(10L);
+ when(responseHeader.getSuggestWhichBrokerId()).thenReturn(MixAll.MASTER_ID);
+ PullResult actual = mqClientAPI.pullMessage(defaultBrokerAddr, requestHeader, defaultTimeout, CommunicationMode.SYNC, callback);
+ assertNotNull(actual);
+ assertEquals(1L, actual.getNextBeginOffset());
+ assertEquals(1L, actual.getMinOffset());
+ assertEquals(10L, actual.getMaxOffset());
+ assertEquals(PullStatus.FOUND, actual.getPullStatus());
+ assertNull(actual.getMsgFoundList());
+ }
+
+ @Test
+ public void testBatchAckMessageAsync() throws MQBrokerException, RemotingException, InterruptedException {
+ AckCallback callback = mock(AckCallback.class);
+ List extraInfoList = new ArrayList<>();
+ extraInfoList.add(String.format("%s %s %s %s %s %s %d %d", "1", "2", "3", "4", "5", brokerName, 7, 8));
+ mqClientAPI.batchAckMessageAsync(defaultBrokerAddr, defaultTimeout, callback, defaultTopic, "", extraInfoList);
+ }
+
+ @Test
+ public void assertSearchOffset() throws MQBrokerException, RemotingException, InterruptedException {
+ mockInvokeSync();
+ SearchOffsetResponseHeader responseHeader = mock(SearchOffsetResponseHeader.class);
+ when(responseHeader.getOffset()).thenReturn(1L);
+ setResponseHeader(responseHeader);
+ assertEquals(1L, mqClientAPI.searchOffset(defaultBrokerAddr, new MessageQueue(), System.currentTimeMillis(), defaultTimeout));
+ }
+
+ @Test
+ public void testUpdateConsumerOffsetOneway() throws RemotingException, InterruptedException {
+ UpdateConsumerOffsetRequestHeader requestHeader = mock(UpdateConsumerOffsetRequestHeader.class);
+ mqClientAPI.updateConsumerOffsetOneway(defaultBrokerAddr, requestHeader, defaultTimeout);
+ }
+
+ @Test
+ public void assertSendHeartbeat() throws MQBrokerException, RemotingException, InterruptedException {
+ mockInvokeSync();
+ HeartbeatData heartbeatData = new HeartbeatData();
+ assertEquals(1, mqClientAPI.sendHeartbeat(defaultBrokerAddr, heartbeatData, defaultTimeout));
+ }
+
+ @Test
+ public void assertSendHeartbeatV2() throws MQBrokerException, RemotingException, InterruptedException {
+ mockInvokeSync();
+ HeartbeatData heartbeatData = new HeartbeatData();
+ HeartbeatV2Result actual = mqClientAPI.sendHeartbeatV2(defaultBrokerAddr, heartbeatData, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getVersion());
+ assertFalse(actual.isSubChange());
+ assertFalse(actual.isSupportV2());
+ }
+
+ @Test
+ public void testUnregisterClient() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.unregisterClient(defaultBrokerAddr, "", "", "", defaultTimeout);
+ }
+
+ @Test
+ public void testEndTransactionOneway() throws RemotingException, InterruptedException {
+ mockInvokeSync();
+ EndTransactionRequestHeader requestHeader = mock(EndTransactionRequestHeader.class);
+ mqClientAPI.endTransactionOneway(defaultBrokerAddr, requestHeader, "", defaultTimeout);
+ }
+
+ @Test
+ public void testQueryMessage() throws MQBrokerException, RemotingException, InterruptedException {
+ QueryMessageRequestHeader requestHeader = mock(QueryMessageRequestHeader.class);
+ InvokeCallback callback = mock(InvokeCallback.class);
+ mqClientAPI.queryMessage(defaultBrokerAddr, requestHeader, defaultTimeout, callback, false);
+ }
+
+ @Test
+ public void testRegisterClient() throws RemotingException, InterruptedException {
+ mockInvokeSync();
+ HeartbeatData heartbeatData = new HeartbeatData();
+ assertTrue(mqClientAPI.registerClient(defaultBrokerAddr, heartbeatData, defaultTimeout));
+ }
+
+ @Test
+ public void testConsumerSendMessageBack() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ MessageExt messageExt = mock(MessageExt.class);
+ mqClientAPI.consumerSendMessageBack(defaultBrokerAddr, brokerName, messageExt, "", 1, defaultTimeout, 1000);
+ }
+
+ @Test
+ public void assertLockBatchMQ() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ LockBatchRequestBody responseBody = new LockBatchRequestBody();
+ setResponseBody(responseBody);
+ Set actual = mqClientAPI.lockBatchMQ(defaultBrokerAddr, responseBody, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(0, actual.size());
+ }
+
+ @Test
+ public void testUnlockBatchMQ() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ UnlockBatchRequestBody unlockBatchRequestBody = new UnlockBatchRequestBody();
+ mqClientAPI.unlockBatchMQ(defaultBrokerAddr, unlockBatchRequestBody, defaultTimeout, false);
+ }
+
+ @Test
+ public void assertGetTopicStatsInfo() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ TopicStatsTable responseBody = new TopicStatsTable();
+ MessageQueue messageQueue = new MessageQueue();
+ TopicOffset topicOffset = new TopicOffset();
+ responseBody.getOffsetTable().put(messageQueue, topicOffset);
+ setResponseBody(responseBody);
+ TopicStatsTable actual = mqClientAPI.getTopicStatsInfo(defaultBrokerAddr, defaultTopic, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getOffsetTable().size());
+ }
+
+ @Test
+ public void assertGetConsumeStats() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ ConsumeStats responseBody = new ConsumeStats();
+ responseBody.setConsumeTps(1000);
+ setResponseBody(responseBody);
+ ConsumeStats actual = mqClientAPI.getConsumeStats(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1000, actual.getConsumeTps(), 0.0);
+ }
+
+ @Test
+ public void assertGetProducerConnectionList() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ ProducerConnection responseBody = new ProducerConnection();
+ responseBody.getConnectionSet().add(new Connection());
+ setResponseBody(responseBody);
+ ProducerConnection actual = mqClientAPI.getProducerConnectionList(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getConnectionSet().size());
+ }
+
+ @Test
+ public void assertGetAllProducerInfo() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ Map> data = new HashMap<>();
+ data.put("key", Collections.emptyList());
+ ProducerTableInfo responseBody = new ProducerTableInfo(data);
+ setResponseBody(responseBody);
+ ProducerTableInfo actual = mqClientAPI.getAllProducerInfo(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getData().size());
+ }
+
+ @Test
+ public void assertGetConsumerConnectionList() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ ConsumerConnection responseBody = new ConsumerConnection();
+ responseBody.setConsumeType(ConsumeType.CONSUME_ACTIVELY);
+ responseBody.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
+ responseBody.setMessageModel(MessageModel.CLUSTERING);
+ setResponseBody(responseBody);
+ ConsumerConnection actual = mqClientAPI.getConsumerConnectionList(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(ConsumeType.CONSUME_ACTIVELY, actual.getConsumeType());
+ assertEquals(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET, actual.getConsumeFromWhere());
+ assertEquals(MessageModel.CLUSTERING, actual.getMessageModel());
+ }
+
+ @Test
+ public void assertGetBrokerRuntimeInfo() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ KVTable responseBody = new KVTable();
+ responseBody.getTable().put("key", "value");
+ setResponseBody(responseBody);
+ KVTable actual = mqClientAPI.getBrokerRuntimeInfo(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTable().size());
+ }
+
+ @Test
+ public void testAddBroker() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.addBroker(defaultBrokerAddr, "", defaultTimeout);
+ }
+
+ @Test
+ public void testRemoveBroker() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.removeBroker(defaultBrokerAddr, clusterName, brokerName, MixAll.MASTER_ID, defaultTimeout);
+ }
+
+ @Test
+ public void testUpdateBrokerConfig() throws RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.updateBrokerConfig(defaultBrokerAddr, createProperties(), defaultTimeout);
+ }
+
+ @Test
+ public void assertGetBrokerConfig() throws RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
+ mockInvokeSync();
+ setResponseBody("{\"key\":\"value\"}");
+ Properties actual = mqClientAPI.getBrokerConfig(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ }
+
+ @Test
+ public void testUpdateColdDataFlowCtrGroupConfig() throws RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
+ mockInvokeSync();
+ Properties props = new Properties();
+ mqClientAPI.updateColdDataFlowCtrGroupConfig(defaultBrokerAddr, props, defaultTimeout);
+ }
+
+ @Test
+ public void testRemoveColdDataFlowCtrGroupConfig() throws RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
+ mockInvokeSync();
+ mqClientAPI.removeColdDataFlowCtrGroupConfig(defaultBrokerAddr, "", defaultTimeout);
+ }
+
+ @Test
+ public void assertGetColdDataFlowCtrInfo() throws RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
+ mockInvokeSync();
+ setResponseBody("{\"key\":\"value\"}");
+ String actual = mqClientAPI.getColdDataFlowCtrInfo(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("\"{\\\"key\\\":\\\"value\\\"}\"", actual);
+ }
+
+ @Test
+ public void assertSetCommitLogReadAheadMode() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ when(response.getRemark()).thenReturn("remark");
+ String actual = mqClientAPI.setCommitLogReadAheadMode(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("remark", actual);
+ }
+
+ @Test
+ public void assertGetBrokerClusterInfo() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ ClusterInfo responseBody = new ClusterInfo();
+ Map> clusterAddrTable = new HashMap<>();
+ clusterAddrTable.put(clusterName, new HashSet<>());
+ Map brokerAddrTable = new HashMap<>();
+ brokerAddrTable.put(brokerName, new BrokerData());
+ responseBody.setClusterAddrTable(clusterAddrTable);
+ responseBody.setBrokerAddrTable(brokerAddrTable);
+ setResponseBody(responseBody);
+ ClusterInfo actual = mqClientAPI.getBrokerClusterInfo(defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getClusterAddrTable().size());
+ assertEquals(1, actual.getBrokerAddrTable().size());
+ }
+
+ @Test
+ public void assertGetDefaultTopicRouteInfoFromNameServer() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicRouteData responseBody = new TopicRouteData();
+ responseBody.getQueueDatas().add(new QueueData());
+ responseBody.getBrokerDatas().add(new BrokerData());
+ responseBody.getFilterServerTable().put("key", Collections.emptyList());
+ Map topicQueueMappingByBroker = new HashMap<>();
+ topicQueueMappingByBroker.put("key", new TopicQueueMappingInfo());
+ responseBody.setTopicQueueMappingByBroker(topicQueueMappingByBroker);
+ setResponseBody(responseBody);
+ TopicRouteData actual = mqClientAPI.getDefaultTopicRouteInfoFromNameServer(defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getQueueDatas().size());
+ assertEquals(1, actual.getBrokerDatas().size());
+ assertEquals(1, actual.getFilterServerTable().size());
+ assertEquals(1, actual.getTopicQueueMappingByBroker().size());
+ }
+
+ @Test
+ public void assertGetTopicRouteInfoFromNameServer() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicRouteData responseBody = new TopicRouteData();
+ responseBody.getQueueDatas().add(new QueueData());
+ responseBody.getBrokerDatas().add(new BrokerData());
+ responseBody.getFilterServerTable().put("key", Collections.emptyList());
+ Map topicQueueMappingByBroker = new HashMap<>();
+ topicQueueMappingByBroker.put("key", new TopicQueueMappingInfo());
+ responseBody.setTopicQueueMappingByBroker(topicQueueMappingByBroker);
+ setResponseBody(responseBody);
+ TopicRouteData actual = mqClientAPI.getTopicRouteInfoFromNameServer(defaultTopic, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getQueueDatas().size());
+ assertEquals(1, actual.getBrokerDatas().size());
+ assertEquals(1, actual.getFilterServerTable().size());
+ assertEquals(1, actual.getTopicQueueMappingByBroker().size());
+ }
+
+ @Test
+ public void assertGetTopicListFromNameServer() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ responseBody.getTopicList().add(defaultTopic);
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getTopicListFromNameServer(defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicList().size());
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ }
+
+ @Test
+ public void assertWipeWritePermOfBroker() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ WipeWritePermOfBrokerResponseHeader responseHeader = mock(WipeWritePermOfBrokerResponseHeader.class);
+ when(responseHeader.getWipeTopicCount()).thenReturn(1);
+ setResponseHeader(responseHeader);
+ assertEquals(1, mqClientAPI.wipeWritePermOfBroker(defaultNsAddr, brokerName, defaultTimeout));
+ }
+
+ @Test
+ public void testDeleteTopicInBroker() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.deleteTopicInBroker(defaultBrokerAddr, defaultTopic, defaultTimeout);
+ }
+
+ @Test
+ public void testDeleteTopicInNameServer() throws RemotingException, InterruptedException, MQClientException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.deleteTopicInNameServer(defaultNsAddr, defaultTopic, defaultTimeout);
+ mqClientAPI.deleteTopicInNameServer(defaultNsAddr, clusterName, defaultTopic, defaultTimeout);
+ }
+
+ @Test
+ public void testDeleteSubscriptionGroup() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.deleteSubscriptionGroup(defaultBrokerAddr, "", true, defaultTimeout);
+ }
+
+ @Test
+ public void assertGetKVConfigValue() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ GetKVConfigResponseHeader responseHeader = mock(GetKVConfigResponseHeader.class);
+ when(responseHeader.getValue()).thenReturn("value");
+ setResponseHeader(responseHeader);
+ assertEquals("value", mqClientAPI.getKVConfigValue("", "", defaultTimeout));
+ }
+
+ @Test
+ public void testPutKVConfigValue() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.putKVConfigValue("", "", "", defaultTimeout);
+ }
+
+ @Test
+ public void testDeleteKVConfigValue() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.deleteKVConfigValue("", "", defaultTimeout);
+ }
+
+ @Test
+ public void assertGetKVListByNamespace() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ KVTable responseBody = new KVTable();
+ responseBody.getTable().put("key", "value");
+ setResponseBody(responseBody);
+ KVTable actual = mqClientAPI.getKVListByNamespace("", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTable().size());
+ }
+
+ @Test
+ public void assertInvokeBrokerToResetOffset() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ ResetOffsetBody responseBody = new ResetOffsetBody();
+ responseBody.getOffsetTable().put(new MessageQueue(), 1L);
+ setResponseBody(responseBody);
+ Map actual = mqClientAPI.invokeBrokerToResetOffset(defaultBrokerAddr, defaultTopic, "", System.currentTimeMillis(), false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ actual = mqClientAPI.invokeBrokerToResetOffset(defaultBrokerAddr, defaultTopic, "", System.currentTimeMillis(), 1, 1L, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ }
+
+ @Test
+ public void assertInvokeBrokerToGetConsumerStatus() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ GetConsumerStatusBody responseBody = new GetConsumerStatusBody();
+ responseBody.getConsumerTable().put("key", new HashMap<>());
+ responseBody.getMessageQueueTable().put(new MessageQueue(), 1L);
+ setResponseBody(responseBody);
+ Map> actual = mqClientAPI.invokeBrokerToGetConsumerStatus(defaultBrokerAddr, defaultTopic, "", "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ }
+
+ @Test
+ public void assertQueryTopicConsumeByWho() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ GroupList responseBody = new GroupList();
+ responseBody.getGroupList().add("");
+ setResponseBody(responseBody);
+ GroupList actual = mqClientAPI.queryTopicConsumeByWho(defaultBrokerAddr, defaultTopic, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getGroupList().size());
+ }
+
+ @Test
+ public void assertQueryTopicsByConsumer() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.getTopicList().add(defaultTopic);
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.queryTopicsByConsumer(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicList().size());
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ }
+
+ @Test
+ public void assertQuerySubscriptionByConsumer() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ QuerySubscriptionResponseBody responseBody = new QuerySubscriptionResponseBody();
+ SubscriptionData subscriptionData = new SubscriptionData();
+ subscriptionData.setTopic(defaultTopic);
+ responseBody.setSubscriptionData(subscriptionData);
+ setResponseBody(responseBody);
+ SubscriptionData actual = mqClientAPI.querySubscriptionByConsumer(defaultBrokerAddr, group, defaultTopic, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(defaultTopic, actual.getTopic());
+ }
+
+ @Test
+ public void assertQueryConsumeTimeSpan() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ QueryConsumeTimeSpanBody responseBody = new QueryConsumeTimeSpanBody();
+ responseBody.getConsumeTimeSpanSet().add(new QueueTimeSpan());
+ setResponseBody(responseBody);
+ List actual = mqClientAPI.queryConsumeTimeSpan(defaultBrokerAddr, defaultTopic, group, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ }
+
+ @Test
+ public void assertGetTopicsByCluster() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ responseBody.setTopicList(Collections.singleton(defaultTopic));
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getTopicsByCluster(clusterName, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ assertEquals(1, actual.getTopicList().size());
+ assertTrue(actual.getTopicList().contains(defaultTopic));
+ }
+
+ @Test
+ public void assertGetSystemTopicList() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ responseBody.setTopicList(Collections.singleton(defaultTopic));
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getSystemTopicList(defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ assertEquals(1, actual.getTopicList().size());
+ assertTrue(actual.getTopicList().contains(defaultTopic));
+ }
+
+ @Test
+ public void assertGetSystemTopicListFromBroker() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ responseBody.setTopicList(Collections.singleton(defaultTopic));
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getSystemTopicListFromBroker(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ assertEquals(1, actual.getTopicList().size());
+ assertTrue(actual.getTopicList().contains(defaultTopic));
+ }
+
+ @Test
+ public void assertCleanExpiredConsumeQueue() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ assertTrue(mqClientAPI.cleanExpiredConsumeQueue(defaultBrokerAddr, defaultTimeout));
+ }
+
+ @Test
+ public void assertDeleteExpiredCommitLog() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ assertTrue(mqClientAPI.deleteExpiredCommitLog(defaultBrokerAddr, defaultTimeout));
+ }
+
+ @Test
+ public void assertCleanUnusedTopicByAddr() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ assertTrue(mqClientAPI.cleanUnusedTopicByAddr(defaultBrokerAddr, defaultTimeout));
+ }
+
+ @Test
+ public void assertGetConsumerRunningInfo() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ ConsumerRunningInfo responseBody = new ConsumerRunningInfo();
+ responseBody.setJstack("jstack");
+ responseBody.getUserConsumerInfo().put("key", "value");
+ setResponseBody(responseBody);
+ ConsumerRunningInfo actual = mqClientAPI.getConsumerRunningInfo(defaultBrokerAddr, group, clientId, false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("jstack", actual.getJstack());
+ assertEquals(1, actual.getUserConsumerInfo().size());
+ }
+
+ @Test
+ public void assertConsumeMessageDirectly() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ ConsumeMessageDirectlyResult responseBody = new ConsumeMessageDirectlyResult();
+ responseBody.setAutoCommit(true);
+ responseBody.setRemark("remark");
+ setResponseBody(responseBody);
+ ConsumeMessageDirectlyResult actual = mqClientAPI.consumeMessageDirectly(defaultBrokerAddr, group, clientId, topic, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("remark", actual.getRemark());
+ assertTrue(actual.isAutoCommit());
+ }
+
+ @Test
+ public void assertQueryCorrectionOffset() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ QueryCorrectionOffsetBody responseBody = new QueryCorrectionOffsetBody();
+ responseBody.getCorrectionOffsets().put(1, 1L);
+ setResponseBody(responseBody);
+ Map actual = mqClientAPI.queryCorrectionOffset(defaultBrokerAddr, topic, group, new HashSet<>(), defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ assertTrue(actual.containsKey(1));
+ assertTrue(actual.containsValue(1L));
+ }
+
+ @Test
+ public void assertGetUnitTopicList() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.getTopicList().add(defaultTopic);
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getUnitTopicList(false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicList().size());
+ }
+
+ @Test
+ public void assertGetHasUnitSubTopicList() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.getTopicList().add(defaultTopic);
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getHasUnitSubTopicList(false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicList().size());
+ }
+
+ @Test
+ public void assertGetHasUnitSubUnUnitTopicList() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ TopicList responseBody = new TopicList();
+ responseBody.getTopicList().add(defaultTopic);
+ setResponseBody(responseBody);
+ TopicList actual = mqClientAPI.getHasUnitSubUnUnitTopicList(false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicList().size());
+ }
+
+ @Test
+ public void testCloneGroupOffset() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.cloneGroupOffset(defaultBrokerAddr, "", "", defaultTopic, false, defaultTimeout);
+ }
+
+ @Test
+ public void assertViewBrokerStatsData() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ BrokerStatsData responseBody = new BrokerStatsData();
+ responseBody.setStatsDay(new BrokerStatsItem());
+ setResponseBody(responseBody);
+ BrokerStatsData actual = mqClientAPI.viewBrokerStatsData(defaultBrokerAddr, "", "", defaultTimeout);
+ assertNotNull(actual);
+ assertNotNull(actual.getStatsDay());
+ }
+
+ @Test
+ public void assertGetClusterList() {
+ Set actual = mqClientAPI.getClusterList(topic, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(0, actual.size());
+ }
+
+ @Test
+ public void assertFetchConsumeStatsInBroker() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ ConsumeStatsList responseBody = new ConsumeStatsList();
+ responseBody.setBrokerAddr(defaultBrokerAddr);
+ responseBody.getConsumeStatsList().add(new HashMap<>());
+ setResponseBody(responseBody);
+ ConsumeStatsList actual = mqClientAPI.fetchConsumeStatsInBroker(defaultBrokerAddr, false, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getConsumeStatsList().size());
+ assertEquals(defaultBrokerAddr, actual.getBrokerAddr());
+ }
+
+ @Test
+ public void assertGetAllSubscriptionGroupForSubscriptionGroupWrapper() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ SubscriptionGroupWrapper responseBody = new SubscriptionGroupWrapper();
+ responseBody.getSubscriptionGroupTable().put("key", new SubscriptionGroupConfig());
+ setResponseBody(responseBody);
+ SubscriptionGroupWrapper actual = mqClientAPI.getAllSubscriptionGroup(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getSubscriptionGroupTable().size());
+ assertNotNull(actual.getDataVersion());
+ assertEquals(0, actual.getDataVersion().getStateVersion());
+ }
+
+ @Test
+ public void assertGetAllSubscriptionGroupForSubscriptionGroupConfig() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ SubscriptionGroupConfig responseBody = new SubscriptionGroupConfig();
+ responseBody.setGroupName(group);
+ responseBody.setBrokerId(MixAll.MASTER_ID);
+ setResponseBody(responseBody);
+ SubscriptionGroupConfig actual = mqClientAPI.getSubscriptionGroupConfig(defaultBrokerAddr, group, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(group, actual.getGroupName());
+ assertEquals(MixAll.MASTER_ID, actual.getBrokerId());
+ }
+
+ @Test
+ public void assertGetAllTopicConfig() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ TopicConfigSerializeWrapper responseBody = new TopicConfigSerializeWrapper();
+ responseBody.getTopicConfigTable().put("key", new TopicConfig());
+ setResponseBody(responseBody);
+ TopicConfigSerializeWrapper actual = mqClientAPI.getAllTopicConfig(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getTopicConfigTable().size());
+ assertNotNull(actual.getDataVersion());
+ assertEquals(0, actual.getDataVersion().getStateVersion());
+ }
+
+ @Test
+ public void testUpdateNameServerConfig() throws RemotingException, InterruptedException, MQClientException, UnsupportedEncodingException {
+ mockInvokeSync();
+ mqClientAPI.updateNameServerConfig(createProperties(), Collections.singletonList(defaultNsAddr), defaultTimeout);
+ }
+
+ @Test
+ public void assertGetNameServerConfig() throws RemotingException, InterruptedException, UnsupportedEncodingException, MQClientException {
+ mockInvokeSync();
+ setResponseBody("{\"key\":\"value\"}");
+ Map actual = mqClientAPI.getNameServerConfig(Collections.singletonList(defaultNsAddr), defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ assertTrue(actual.containsKey(defaultNsAddr));
+ }
+
+ @Test
+ public void assertQueryConsumeQueue() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ QueryConsumeQueueResponseBody responseBody = new QueryConsumeQueueResponseBody();
+ responseBody.setQueueData(Collections.singletonList(new ConsumeQueueData()));
+ setResponseBody(responseBody);
+ QueryConsumeQueueResponseBody actual = mqClientAPI.queryConsumeQueue(defaultBrokerAddr, defaultTopic, 1, 1, 1, group, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1, actual.getQueueData().size());
+ }
+
+ @Test
+ public void testCheckClientInBroker() throws RemotingException, InterruptedException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.checkClientInBroker(defaultBrokerAddr, group, clientId, new SubscriptionData(), defaultTimeout);
+ }
+
+ @Test
+ public void assertGetTopicConfig() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ TopicConfigAndQueueMapping responseBody = new TopicConfigAndQueueMapping(new TopicConfig(), new TopicQueueMappingDetail());
+ setResponseBody(responseBody);
+ TopicConfigAndQueueMapping actual = mqClientAPI.getTopicConfig(defaultBrokerAddr, defaultTopic, defaultTimeout);
+ assertNotNull(actual);
+ assertNotNull(actual.getMappingDetail());
+ }
+
+ @Test
+ public void testCreateStaticTopic() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.createStaticTopic(defaultBrokerAddr, defaultTopic, new TopicConfig(), new TopicQueueMappingDetail(), false, defaultTimeout);
+ }
+
+ @Test
+ public void assertUpdateAndGetGroupForbidden() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ GroupForbidden responseBody = new GroupForbidden();
+ responseBody.setGroup(group);
+ responseBody.setTopic(defaultTopic);
+ setResponseBody(responseBody);
+ GroupForbidden actual = mqClientAPI.updateAndGetGroupForbidden(defaultBrokerAddr, new UpdateGroupForbiddenRequestHeader(), defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(group, actual.getGroup());
+ assertEquals(defaultTopic, actual.getTopic());
+ }
+
+ @Test
+ public void testResetMasterFlushOffset() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.resetMasterFlushOffset(defaultBrokerAddr, 1L);
+ }
+
+ @Test
+ public void assertGetBrokerHAStatus() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ HARuntimeInfo responseBody = new HARuntimeInfo();
+ responseBody.setMaster(true);
+ responseBody.setMasterCommitLogMaxOffset(1L);
+ setResponseBody(responseBody);
+ HARuntimeInfo actual = mqClientAPI.getBrokerHAStatus(defaultBrokerAddr, defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1L, actual.getMasterCommitLogMaxOffset());
+ assertTrue(actual.isMaster());
+ }
+
+ @Test
+ public void assertGetControllerMetaData() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ GetMetaDataResponseHeader responseHeader = new GetMetaDataResponseHeader();
+ responseHeader.setGroup(group);
+ responseHeader.setIsLeader(true);
+ setResponseHeader(responseHeader);
+ GetMetaDataResponseHeader actual = mqClientAPI.getControllerMetaData(defaultBrokerAddr);
+ assertNotNull(actual);
+ assertEquals(group, actual.getGroup());
+ assertTrue(actual.isLeader());
+ }
+
+ @Test
+ public void assertGetInSyncStateData() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ BrokerReplicasInfo responseBody = new BrokerReplicasInfo();
+ BrokerReplicasInfo.ReplicasInfo replicasInfo = new BrokerReplicasInfo.ReplicasInfo(MixAll.MASTER_ID, defaultBrokerAddr, 1, 1, Collections.emptyList(), Collections.emptyList());
+ responseBody.getReplicasInfoTable().put("key", replicasInfo);
+ GetMetaDataResponseHeader responseHeader = new GetMetaDataResponseHeader();
+ responseHeader.setControllerLeaderAddress(defaultBrokerAddr);
+ setResponseHeader(responseHeader);
+ setResponseBody(responseBody);
+ BrokerReplicasInfo actual = mqClientAPI.getInSyncStateData(defaultBrokerAddr, Collections.singletonList(defaultBrokerAddr));
+ assertNotNull(actual);
+ assertEquals(1L, actual.getReplicasInfoTable().size());
+ }
+
+ @Test
+ public void assertGetBrokerEpochCache() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ EpochEntryCache responseBody = new EpochEntryCache(clusterName, brokerName, MixAll.MASTER_ID, Collections.emptyList(), 1);
+ setResponseBody(responseBody);
+ EpochEntryCache actual = mqClientAPI.getBrokerEpochCache(defaultBrokerAddr);
+ assertNotNull(actual);
+ assertEquals(1L, actual.getMaxOffset());
+ assertEquals(MixAll.MASTER_ID, actual.getBrokerId());
+ assertEquals(brokerName, actual.getBrokerName());
+ assertEquals(clusterName, actual.getClusterName());
+ }
+
+ @Test
+ public void assertGetControllerConfig() throws RemotingException, InterruptedException, UnsupportedEncodingException, MQClientException {
+ mockInvokeSync();
+ setResponseBody("{\"key\":\"value\"}");
+ Map actual = mqClientAPI.getControllerConfig(Collections.singletonList(defaultBrokerAddr), defaultTimeout);
+ assertNotNull(actual);
+ assertEquals(1L, actual.size());
+ }
+
+ @Test
+ public void testUpdateControllerConfig() throws RemotingException, InterruptedException, UnsupportedEncodingException, MQClientException {
+ mockInvokeSync();
+ mqClientAPI.updateControllerConfig(createProperties(), Collections.singletonList(defaultBrokerAddr), defaultTimeout);
+ }
+
+ @Test
+ public void assertElectMaster() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ BrokerMemberGroup responseBody = new BrokerMemberGroup();
+ setResponseBody(responseBody);
+ GetMetaDataResponseHeader getMetaDataResponseHeader = new GetMetaDataResponseHeader();
+ getMetaDataResponseHeader.setControllerLeaderAddress(defaultBrokerAddr);
+ when(response.decodeCommandCustomHeader(GetMetaDataResponseHeader.class)).thenReturn(getMetaDataResponseHeader);
+ ElectMasterResponseHeader responseHeader = new ElectMasterResponseHeader();
+ when(response.decodeCommandCustomHeader(ElectMasterResponseHeader.class)).thenReturn(responseHeader);
+ Pair actual = mqClientAPI.electMaster(defaultBrokerAddr, clusterName, brokerName, MixAll.MASTER_ID);
+ assertNotNull(actual);
+ assertEquals(responseHeader, actual.getObject1());
+ assertEquals(responseBody, actual.getObject2());
+ }
+
+ @Test
+ public void testCleanControllerBrokerData() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ GetMetaDataResponseHeader responseHeader = new GetMetaDataResponseHeader();
+ responseHeader.setControllerLeaderAddress(defaultBrokerAddr);
+ setResponseHeader(responseHeader);
+ mqClientAPI.cleanControllerBrokerData(defaultBrokerAddr, clusterName, brokerName, "", false);
+ }
+
+ @Test
+ public void testCreateUser() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.createUser(defaultBrokerAddr, new UserInfo(), defaultTimeout);
+ }
+
+ @Test
+ public void testUpdateUser() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.updateUser(defaultBrokerAddr, new UserInfo(), defaultTimeout);
+ }
+
+ @Test
+ public void testDeleteUser() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.deleteUser(defaultBrokerAddr, "", defaultTimeout);
+ }
+
+ @Test
+ public void assertGetUser() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ setResponseBody(createUserInfo());
+ UserInfo actual = mqClientAPI.getUser(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("username", actual.getUsername());
+ assertEquals("password", actual.getPassword());
+ assertEquals("userStatus", actual.getUserStatus());
+ assertEquals("userType", actual.getUserType());
+ }
+
+ @Test
+ public void assertListUser() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ setResponseBody(Collections.singletonList(createUserInfo()));
+ List actual = mqClientAPI.listUser(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("username", actual.get(0).getUsername());
+ assertEquals("password", actual.get(0).getPassword());
+ assertEquals("userStatus", actual.get(0).getUserStatus());
+ assertEquals("userType", actual.get(0).getUserType());
+ }
+
+ @Test
+ public void testCreateAcl() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.createAcl(defaultBrokerAddr, new AclInfo(), defaultTimeout);
+ }
+
+ @Test
+ public void testUpdateAcl() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.updateAcl(defaultBrokerAddr, new AclInfo(), defaultTimeout);
+ }
+
+ @Test
+ public void testDeleteAcl() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ mqClientAPI.deleteAcl(defaultBrokerAddr, "", "", defaultTimeout);
+ }
+
+ @Test
+ public void assertGetAcl() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ setResponseBody(createAclInfo());
+ AclInfo actual = mqClientAPI.getAcl(defaultBrokerAddr, "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("subject", actual.getSubject());
+ assertEquals(1, actual.getPolicies().size());
+ }
+
+ @Test
+ public void assertListAcl() throws RemotingException, InterruptedException, MQBrokerException {
+ mockInvokeSync();
+ setResponseBody(Collections.singletonList(createAclInfo()));
+ List actual = mqClientAPI.listAcl(defaultBrokerAddr, "", "", defaultTimeout);
+ assertNotNull(actual);
+ assertEquals("subject", actual.get(0).getSubject());
+ assertEquals(1, actual.get(0).getPolicies().size());
+ }
+
+ private Properties createProperties() {
+ Properties result = new Properties();
+ result.put("key", "value");
+ return result;
+ }
+
+ private AclInfo createAclInfo() {
+ return AclInfo.of("subject", Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), "");
+ }
+
+ private UserInfo createUserInfo() {
+ UserInfo result = new UserInfo();
+ result.setUsername("username");
+ result.setPassword("password");
+ result.setUserStatus("userStatus");
+ result.setUserType("userType");
+ return result;
+ }
+
+ private void setResponseHeader(CommandCustomHeader responseHeader) throws RemotingCommandException {
+ when(response.decodeCommandCustomHeader(any())).thenReturn(responseHeader);
+ }
+
+ private void setResponseBody(Object responseBody) {
+ when(response.getBody()).thenReturn(RemotingSerializable.encode(responseBody));
+ }
+
+ private void mockInvokeSync() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, InterruptedException {
+ when(response.getCode()).thenReturn(ResponseCode.SUCCESS);
+ when(response.getVersion()).thenReturn(1);
+ when(remotingClient.invokeSync(any(), any(), anyLong())).thenReturn(response);
+ when(remotingClient.getNameServerAddressList()).thenReturn(Collections.singletonList(defaultNsAddr));
+ }
+
+ private void setTopAddressing() throws NoSuchFieldException, IllegalAccessException {
+ TopAddressing topAddressing = mock(TopAddressing.class);
+ setField(mqClientAPI, "topAddressing", topAddressing);
+ when(topAddressing.fetchNSAddr()).thenReturn(defaultNsAddr);
+ }
+
+ private void setField(final Object target, final String fieldName, final Object newValue) throws NoSuchFieldException, IllegalAccessException {
+ Class> clazz = target.getClass();
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(target, newValue);
+ }
}
diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java
new file mode 100644
index 00000000000..71682fb52c0
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java
@@ -0,0 +1,559 @@
+/*
+ * 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.rocketmq.client.impl.admin;
+
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.remoting.RemotingClient;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
+import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
+import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
+import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
+import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
+import org.apache.rocketmq.remoting.protocol.body.GroupList;
+import org.apache.rocketmq.remoting.protocol.body.QueryConsumeTimeSpanBody;
+import org.apache.rocketmq.remoting.protocol.body.QueueTimeSpan;
+import org.apache.rocketmq.remoting.protocol.body.ResetOffsetBody;
+import org.apache.rocketmq.remoting.protocol.body.TopicList;
+import org.apache.rocketmq.remoting.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.CreateTopicRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.DeleteSubscriptionGroupRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.DeleteTopicRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumeStatsRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerConnectionListRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetConsumerRunningInfoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.GetTopicStatsInfoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryConsumeTimeSpanRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QuerySubscriptionByConsumerRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryTopicConsumeByWhoRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.QueryTopicsByConsumerRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ResetOffsetRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.ViewMessageRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.namesrv.DeleteKVConfigRequestHeader;
+import org.apache.rocketmq.remoting.protocol.header.namesrv.DeleteTopicFromNamesrvRequestHeader;
+import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MqClientAdminImplTest {
+
+ @Mock
+ private RemotingClient remotingClient;
+
+ @Mock
+ private RemotingCommand response;
+
+ private MqClientAdminImpl mqClientAdminImpl;
+
+ private final String defaultTopic = "defaultTopic";
+
+ private final String defaultBrokerAddr = "127.0.0.1:10911";
+
+ private final long defaultTimeout = 3000L;
+
+ @Before
+ public void init() throws RemotingException, InterruptedException, MQClientException {
+ mqClientAdminImpl = new MqClientAdminImpl(remotingClient);
+ when(remotingClient.invoke(any(String.class), any(RemotingCommand.class), any(Long.class))).thenReturn(CompletableFuture.completedFuture(response));
+ }
+
+ @Test
+ public void assertQueryMessageWithSuccess() throws Exception {
+ setResponseSuccess(getMessageResult());
+ QueryMessageRequestHeader requestHeader = mock(QueryMessageRequestHeader.class);
+ when(requestHeader.getTopic()).thenReturn(defaultTopic);
+ when(requestHeader.getKey()).thenReturn("keys");
+ CompletableFuture> actual = mqClientAdminImpl.queryMessage(defaultBrokerAddr, false, false, requestHeader, defaultTimeout);
+ List messageExtList = actual.get();
+ assertNotNull(messageExtList);
+ assertEquals(1, messageExtList.size());
+ }
+
+ @Test
+ public void assertQueryMessageWithNotFound() throws Exception {
+ when(response.getCode()).thenReturn(ResponseCode.QUERY_NOT_FOUND);
+ QueryMessageRequestHeader requestHeader = mock(QueryMessageRequestHeader.class);
+ CompletableFuture> actual = mqClientAdminImpl.queryMessage(defaultBrokerAddr, false, false, requestHeader, defaultTimeout);
+ List messageExtList = actual.get();
+ assertNotNull(messageExtList);
+ assertEquals(0, messageExtList.size());
+ }
+
+ @Test
+ public void assertQueryMessageWithError() {
+ setResponseError();
+ QueryMessageRequestHeader requestHeader = mock(QueryMessageRequestHeader.class);
+ CompletableFuture> actual = mqClientAdminImpl.queryMessage(defaultBrokerAddr, false, false, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertGetTopicStatsInfoWithSuccess() throws Exception {
+ TopicStatsTable responseBody = new TopicStatsTable();
+ setResponseSuccess(RemotingSerializable.encode(responseBody));
+ GetTopicStatsInfoRequestHeader requestHeader = mock(GetTopicStatsInfoRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.getTopicStatsInfo(defaultBrokerAddr, requestHeader, defaultTimeout);
+ TopicStatsTable topicStatsTable = actual.get();
+ assertNotNull(topicStatsTable);
+ assertEquals(0, topicStatsTable.getOffsetTable().size());
+ }
+
+ @Test
+ public void assertGetTopicStatsInfoWithError() {
+ setResponseError();
+ GetTopicStatsInfoRequestHeader requestHeader = mock(GetTopicStatsInfoRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.getTopicStatsInfo(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertQueryConsumeTimeSpanWithSuccess() throws Exception {
+ QueryConsumeTimeSpanBody responseBody = new QueryConsumeTimeSpanBody();
+ setResponseSuccess(RemotingSerializable.encode(responseBody));
+ QueryConsumeTimeSpanRequestHeader requestHeader = mock(QueryConsumeTimeSpanRequestHeader.class);
+ CompletableFuture> actual = mqClientAdminImpl.queryConsumeTimeSpan(defaultBrokerAddr, requestHeader, defaultTimeout);
+ List queueTimeSpans = actual.get();
+ assertNotNull(queueTimeSpans);
+ assertEquals(0, queueTimeSpans.size());
+ }
+
+ @Test
+ public void assertQueryConsumeTimeSpanWithError() {
+ setResponseError();
+ QueryConsumeTimeSpanRequestHeader requestHeader = mock(QueryConsumeTimeSpanRequestHeader.class);
+ CompletableFuture> actual = mqClientAdminImpl.queryConsumeTimeSpan(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertUpdateOrCreateTopicWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ CreateTopicRequestHeader requestHeader = mock(CreateTopicRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.updateOrCreateTopic(defaultBrokerAddr, requestHeader, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertUpdateOrCreateTopicWithError() {
+ setResponseError();
+ CreateTopicRequestHeader requestHeader = mock(CreateTopicRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.updateOrCreateTopic(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertUpdateOrCreateSubscriptionGroupWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ SubscriptionGroupConfig config = mock(SubscriptionGroupConfig.class);
+ CompletableFuture actual = mqClientAdminImpl.updateOrCreateSubscriptionGroup(defaultBrokerAddr, config, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertUpdateOrCreateSubscriptionGroupWithError() {
+ setResponseError();
+ SubscriptionGroupConfig config = mock(SubscriptionGroupConfig.class);
+ CompletableFuture actual = mqClientAdminImpl.updateOrCreateSubscriptionGroup(defaultBrokerAddr, config, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertDeleteTopicInBrokerWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ DeleteTopicRequestHeader requestHeader = mock(DeleteTopicRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteTopicInBroker(defaultBrokerAddr, requestHeader, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertDeleteTopicInBrokerWithError() {
+ setResponseError();
+ DeleteTopicRequestHeader requestHeader = mock(DeleteTopicRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteTopicInBroker(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertDeleteTopicInNameserverWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ DeleteTopicFromNamesrvRequestHeader requestHeader = mock(DeleteTopicFromNamesrvRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteTopicInNameserver(defaultBrokerAddr, requestHeader, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertDeleteTopicInNameserverWithError() {
+ setResponseError();
+ DeleteTopicFromNamesrvRequestHeader requestHeader = mock(DeleteTopicFromNamesrvRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteTopicInNameserver(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertDeleteKvConfigWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ DeleteKVConfigRequestHeader requestHeader = mock(DeleteKVConfigRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteKvConfig(defaultBrokerAddr, requestHeader, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertDeleteKvConfigWithError() {
+ setResponseError();
+ DeleteKVConfigRequestHeader requestHeader = mock(DeleteKVConfigRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteKvConfig(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertDeleteSubscriptionGroupWithSuccess() throws Exception {
+ setResponseSuccess(null);
+ DeleteSubscriptionGroupRequestHeader requestHeader = mock(DeleteSubscriptionGroupRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteSubscriptionGroup(defaultBrokerAddr, requestHeader, defaultTimeout);
+ assertNull(actual.get());
+ }
+
+ @Test
+ public void assertDeleteSubscriptionGroupWithError() {
+ setResponseError();
+ DeleteSubscriptionGroupRequestHeader requestHeader = mock(DeleteSubscriptionGroupRequestHeader.class);
+ CompletableFuture actual = mqClientAdminImpl.deleteSubscriptionGroup(defaultBrokerAddr, requestHeader, defaultTimeout);
+ Throwable thrown = assertThrows(ExecutionException.class, actual::get);
+ assertTrue(thrown.getCause() instanceof MQClientException);
+ MQClientException mqException = (MQClientException) thrown.getCause();
+ assertEquals(ResponseCode.SYSTEM_ERROR, mqException.getResponseCode());
+ assertTrue(mqException.getMessage().contains("CODE: 1 DESC: null"));
+ }
+
+ @Test
+ public void assertInvokeBrokerToResetOffsetWithSuccess() throws Exception {
+ ResetOffsetBody responseBody = new ResetOffsetBody();
+ setResponseSuccess(RemotingSerializable.encode(responseBody));
+ ResetOffsetRequestHeader requestHeader = mock(ResetOffsetRequestHeader.class);
+ CompletableFuture