This is an automated email from the ASF dual-hosted git repository.
dataroaring pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new f1a9d803fd2 [branch-2.0](publish version) fix publish version failed
but return ok #28425 (#28791)
f1a9d803fd2 is described below
commit f1a9d803fd29889b571ff360a9b484f526993c53
Author: yujun <[email protected]>
AuthorDate: Tue Dec 26 17:38:40 2023 +0800
[branch-2.0](publish version) fix publish version failed but return ok
#28425 (#28791)
Co-authored-by: Kang <[email protected]>
---
be/src/http/action/report_action.cpp | 41 ++++++++++++
be/src/http/action/report_action.h | 38 +++++++++++
be/src/olap/storage_engine.cpp | 5 +-
be/src/olap/storage_engine.h | 2 +-
be/src/olap/task/engine_publish_version_task.cpp | 34 ++++++++--
be/src/olap/task/engine_publish_version_task.h | 2 +
be/src/service/http_service.cpp | 16 +++++
.../insert_p0/test_be_inject_publish_txn_fail.out | 13 ++++
.../apache/doris/regression/util/DebugPoint.groovy | 28 +++-----
.../org/apache/doris/regression/util/Http.groovy | 27 +++++++-
.../plugins/plugin_curl_requester.groovy | 45 +++++++++++++
.../test_be_inject_publish_txn_fail.groovy | 77 ++++++++++++++++++++++
.../plugin_p0/test_plugin_curl_requester.groovy | 30 +++++++++
13 files changed, 330 insertions(+), 28 deletions(-)
diff --git a/be/src/http/action/report_action.cpp
b/be/src/http/action/report_action.cpp
new file mode 100644
index 00000000000..cf5621f5791
--- /dev/null
+++ b/be/src/http/action/report_action.cpp
@@ -0,0 +1,41 @@
+// 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.
+
+#include "http/action/report_action.h"
+
+#include "common/status.h"
+#include "http/http_channel.h"
+#include "olap/storage_engine.h"
+
+namespace doris {
+
+ReportAction::ReportAction(ExecEnv* exec_env, TPrivilegeHier::type hier,
TPrivilegeType::type type,
+ TaskWorkerPool::TaskWorkerType report_type)
+ : HttpHandlerWithAuth(exec_env, hier, type), _report_type(report_type)
{}
+
+void ReportAction::handle(HttpRequest* req) {
+ if (StorageEngine::instance()->notify_listener(_report_type)) {
+ HttpChannel::send_reply(req, HttpStatus::OK, Status::OK().to_json());
+ } else {
+ HttpChannel::send_reply(
+ req, HttpStatus::INTERNAL_SERVER_ERROR,
+ Status::InternalError("unknown reporter with name: " +
std::to_string(_report_type))
+ .to_json());
+ }
+}
+
+} // namespace doris
diff --git a/be/src/http/action/report_action.h
b/be/src/http/action/report_action.h
new file mode 100644
index 00000000000..e4a3b9e5f98
--- /dev/null
+++ b/be/src/http/action/report_action.h
@@ -0,0 +1,38 @@
+// 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.
+
+#pragma once
+
+#include "agent/task_worker_pool.h"
+#include "http/http_handler_with_auth.h"
+#include "http/http_request.h"
+
+namespace doris {
+
+// use in regression test, just for test.
+
+class ReportAction : public HttpHandlerWithAuth {
+public:
+ ReportAction(ExecEnv* exec_env, TPrivilegeHier::type hier,
TPrivilegeType::type type,
+ TaskWorkerPool::TaskWorkerType report_type);
+ void handle(HttpRequest* req) override;
+
+private:
+ const TaskWorkerPool::TaskWorkerType _report_type;
+};
+
+} // namespace doris
diff --git a/be/src/olap/storage_engine.cpp b/be/src/olap/storage_engine.cpp
index a4ecf33a0bb..e22cb94040f 100644
--- a/be/src/olap/storage_engine.cpp
+++ b/be/src/olap/storage_engine.cpp
@@ -1232,13 +1232,16 @@ void StorageEngine::notify_listeners() {
}
}
-void StorageEngine::notify_listener(TaskWorkerPool::TaskWorkerType
task_worker_type) {
+bool StorageEngine::notify_listener(TaskWorkerPool::TaskWorkerType
task_worker_type) {
+ bool found = false;
std::lock_guard<std::mutex> l(_report_mtx);
for (auto& listener : _report_listeners) {
if (listener->task_worker_type() == task_worker_type) {
listener->notify_thread();
+ found = true;
}
}
+ return found;
}
Status StorageEngine::execute_task(EngineTask* task) {
diff --git a/be/src/olap/storage_engine.h b/be/src/olap/storage_engine.h
index 7215e2bb484..244e134b512 100644
--- a/be/src/olap/storage_engine.h
+++ b/be/src/olap/storage_engine.h
@@ -139,7 +139,7 @@ public:
void register_report_listener(TaskWorkerPool* listener);
void deregister_report_listener(TaskWorkerPool* listener);
void notify_listeners();
- void notify_listener(TaskWorkerPool::TaskWorkerType task_worker_type);
+ bool notify_listener(TaskWorkerPool::TaskWorkerType task_worker_type);
Status execute_task(EngineTask* task);
diff --git a/be/src/olap/task/engine_publish_version_task.cpp
b/be/src/olap/task/engine_publish_version_task.cpp
index e1a151c1973..d70877c4c48 100644
--- a/be/src/olap/task/engine_publish_version_task.cpp
+++ b/be/src/olap/task/engine_publish_version_task.cpp
@@ -108,6 +108,14 @@ Status EnginePublishVersionTask::finish() {
StorageEngine::instance()->tablet_publish_txn_thread_pool()->new_token(
ThreadPool::ExecutionMode::CONCURRENT);
std::unordered_map<int64_t, int64_t> tablet_id_to_num_delta_rows;
+
+#ifndef NDEBUG
+ if (UNLIKELY(_publish_version_req.partition_version_infos.empty())) {
+ LOG(WARNING) << "transaction_id: " << transaction_id << " empty
partition_version_infos";
+ }
+#endif
+
+ std::vector<std::shared_ptr<TabletPublishTxnTask>> tablet_tasks;
// each partition
for (auto& par_ver_info : _publish_version_req.partition_version_infos) {
int64_t partition_id = par_ver_info.partition_id;
@@ -230,12 +238,22 @@ Status EnginePublishVersionTask::finish() {
auto tablet_publish_txn_ptr =
std::make_shared<TabletPublishTxnTask>(
this, tablet, rowset, partition_id, transaction_id,
version, tablet_info);
+ tablet_tasks.push_back(tablet_publish_txn_ptr);
auto submit_st = token->submit_func([=]() {
tablet_publish_txn_ptr->handle(); });
CHECK(submit_st.ok()) << submit_st;
}
}
token->wait();
+ if (res.ok()) {
+ for (const auto& tablet_task : tablet_tasks) {
+ res = tablet_task->result();
+ if (!res.ok()) {
+ break;
+ }
+ }
+ }
+
_succ_tablets->clear();
// check if the related tablet remained all have the version
for (auto& par_ver_info : _publish_version_req.partition_version_infos) {
@@ -324,24 +342,24 @@ void TabletPublishTxnTask::handle() {
rowset_update_lock.lock();
}
_stats.schedule_time_us = MonotonicMicros() - _stats.submit_time_us;
- auto publish_status =
StorageEngine::instance()->txn_manager()->publish_txn(
+ _result = StorageEngine::instance()->txn_manager()->publish_txn(
_partition_id, _tablet, _transaction_id, _version, &_stats);
- if (publish_status != Status::OK()) {
+ if (!_result.ok()) {
LOG(WARNING) << "failed to publish version. rowset_id=" <<
_rowset->rowset_id()
<< ", tablet_id=" << _tablet_info.tablet_id << ",
txn_id=" << _transaction_id
- << ", res=" << publish_status;
+ << ", res=" << _result;
_engine_publish_version_task->add_error_tablet_id(_tablet_info.tablet_id);
return;
}
// add visible rowset to tablet
int64_t t1 = MonotonicMicros();
- publish_status = _tablet->add_inc_rowset(_rowset);
+ _result = _tablet->add_inc_rowset(_rowset);
_stats.add_inc_rowset_us = MonotonicMicros() - t1;
- if (publish_status != Status::OK() &&
!publish_status.is<PUSH_VERSION_ALREADY_EXIST>()) {
+ if (!_result.ok() && !_result.is<PUSH_VERSION_ALREADY_EXIST>()) {
LOG(WARNING) << "fail to add visible rowset to tablet. rowset_id=" <<
_rowset->rowset_id()
<< ", tablet_id=" << _tablet_info.tablet_id << ",
txn_id=" << _transaction_id
- << ", res=" << publish_status;
+ << ", res=" << _result;
_engine_publish_version_task->add_error_tablet_id(_tablet_info.tablet_id);
return;
}
@@ -351,9 +369,11 @@ void TabletPublishTxnTask::handle() {
LOG(INFO) << "publish version successfully on tablet"
<< ", table_id=" << _tablet->table_id() << ", tablet=" <<
_tablet->full_name()
<< ", transaction_id=" << _transaction_id << ", version=" <<
_version.first
- << ", num_rows=" << _rowset->num_rows() << ", res=" <<
publish_status
+ << ", num_rows=" << _rowset->num_rows() << ", res=" << _result
<< ", cost: " << cost_us << "(us) "
<< (cost_us > 500 * 1000 ? _stats.to_string() : "");
+
+ _result = Status::OK();
}
void AsyncTabletPublishTask::handle() {
diff --git a/be/src/olap/task/engine_publish_version_task.h
b/be/src/olap/task/engine_publish_version_task.h
index 8f3790574a2..dc84dd73923 100644
--- a/be/src/olap/task/engine_publish_version_task.h
+++ b/be/src/olap/task/engine_publish_version_task.h
@@ -69,6 +69,7 @@ public:
~TabletPublishTxnTask() = default;
void handle();
+ Status result() { return _result; }
private:
EnginePublishVersionTask* _engine_publish_version_task;
@@ -80,6 +81,7 @@ private:
Version _version;
TabletInfo _tablet_info;
TabletPublishStatistics _stats;
+ Status _result;
};
class EnginePublishVersionTask : public EngineTask {
diff --git a/be/src/service/http_service.cpp b/be/src/service/http_service.cpp
index 556d101b05c..547b88878a1 100644
--- a/be/src/service/http_service.cpp
+++ b/be/src/service/http_service.cpp
@@ -41,6 +41,7 @@
#include "http/action/pad_rowset_action.h"
#include "http/action/pprof_actions.h"
#include "http/action/reload_tablet_action.h"
+#include "http/action/report_action.h"
#include "http/action/reset_rpc_channel_action.h"
#include "http/action/restore_tablet_action.h"
#include "http/action/snapshot_action.h"
@@ -276,6 +277,21 @@ Status HttpService::start() {
_ev_http_server->register_handler(HttpMethod::POST,
"/api/debug_point/clear",
clear_debug_points_action);
+ ReportAction* report_tablet_action =
+ _pool.add(new ReportAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN,
+
TaskWorkerPool::TaskWorkerType::REPORT_OLAP_TABLE));
+ _ev_http_server->register_handler(HttpMethod::GET, "/api/report/tablet",
report_tablet_action);
+
+ ReportAction* report_disk_action =
+ _pool.add(new ReportAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN,
+
TaskWorkerPool::TaskWorkerType::REPORT_DISK_STATE));
+ _ev_http_server->register_handler(HttpMethod::GET, "/api/report/disk",
report_disk_action);
+
+ ReportAction* report_task_action =
+ _pool.add(new ReportAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN,
+
TaskWorkerPool::TaskWorkerType::REPORT_TASK));
+ _ev_http_server->register_handler(HttpMethod::GET, "/api/report/task",
report_task_action);
+
_ev_http_server->start();
return Status::OK();
}
diff --git a/regression-test/data/insert_p0/test_be_inject_publish_txn_fail.out
b/regression-test/data/insert_p0/test_be_inject_publish_txn_fail.out
new file mode 100644
index 00000000000..35378c9914e
--- /dev/null
+++ b/regression-test/data/insert_p0/test_be_inject_publish_txn_fail.out
@@ -0,0 +1,13 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select_1 --
+
+-- !select_2 --
+100
+
+-- !select_1 --
+100
+
+-- !select_2 --
+100
+200
+
diff --git
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/DebugPoint.groovy
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/DebugPoint.groovy
index c30f7fbbcbe..65e96cc41ba 100644
---
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/DebugPoint.groovy
+++
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/DebugPoint.groovy
@@ -40,13 +40,13 @@ class DebugPoint {
* name: debug point name
* params: timeout, execute, or other customized input params
*/
- static def enableDebugPoint(String host, String httpPort, NodeType type,
String name, Map<String, String> params = null) {
+ static def enableDebugPoint(String host, int httpPort, NodeType type,
String name, Map<String, String> params = null) {
def url = 'http://' + host + ':' + httpPort + '/api/debug_point/add/'
+ name
if (params != null && params.size() > 0) {
url += '?' + params.collect((k, v) -> k + '=' + v).join('&')
}
- def result = Http.http_post(url, null, true)
- checkHttpResult(result, type)
+ def result = Http.POST(url, null, true)
+ Http.checkHttpResult(result, type)
}
/* Disable debug point in regression
@@ -56,10 +56,10 @@ class DebugPoint {
* type: NodeType.BE or NodeType.FE
* name: debug point name
*/
- static def disableDebugPoint(String host, String httpPort, NodeType type,
String name) {
+ static def disableDebugPoint(String host, int httpPort, NodeType type,
String name) {
def url = 'http://' + host + ':' + httpPort +
'/api/debug_point/remove/' + name
- def result = Http.http_post(url, null, true)
- checkHttpResult(result, type)
+ def result = Http.POST(url, null, true)
+ Http.checkHttpResult(result, type)
}
/* Disable all debug points in regression
@@ -68,10 +68,10 @@ class DebugPoint {
* httpPort: http port of target node
* type: NodeType.BE or NodeType.FE
*/
- static def clearDebugPoints(String host, String httpPort, NodeType type) {
+ static def clearDebugPoints(String host, int httpPort, NodeType type) {
def url = 'http://' + host + ':' + httpPort + '/api/debug_point/clear'
- def result = Http.http_post(url, null, true)
- checkHttpResult(result, type)
+ def result = Http.POST(url, null, true)
+ Http.checkHttpResult(result, type)
}
def operateDebugPointForAllBEs(Closure closure) {
@@ -79,7 +79,7 @@ class DebugPoint {
def portList = [:]
(ipList, portList) = getBEHostAndHTTPPort()
ipList.each { beid, ip ->
- closure.call(ip, portList[beid])
+ closure.call(ip, portList[beid] as int)
}
}
@@ -127,13 +127,5 @@ class DebugPoint {
def clearDebugPointsForAllFEs() {
assert false : 'not implemented yet'
}
-
- static void checkHttpResult(Object result, NodeType type) {
- if (type == NodeType.FE) {
- assert result.code == 0 : result.toString()
- } else if (type == NodeType.BE) {
- assert result.status == 'OK' : result.toString()
- }
- }
}
diff --git
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/Http.groovy
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/Http.groovy
index 1daabf469b5..cd688a1fcfc 100644
---
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/Http.groovy
+++
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/Http.groovy
@@ -39,7 +39,32 @@ class Http {
final static Logger logger = LoggerFactory.getLogger(this.class)
- static Object http_post(url, data = null, isJson = false) {
+ static void checkHttpResult(Object result, NodeType type) {
+ if (type == NodeType.FE) {
+ assert result.code == 0 : result.toString()
+ } else if (type == NodeType.BE) {
+ assert result.status == 'OK' : result.toString()
+ }
+ }
+
+ static Object GET(url, isJson = false) {
+ def conn = new URL(url).openConnection()
+ conn.setRequestMethod('GET')
+ conn.setRequestProperty('Authorization', 'Basic cm9vdDo=') //token for
root
+ def code = conn.responseCode
+ def text = conn.content.text
+ logger.info("http post url=${url}, isJson=${isJson}, response
code=${code}, text=${text}")
+ Assert.assertEquals(200, code)
+ if (isJson) {
+ def json = new JsonSlurper()
+ def result = json.parseText(text)
+ return result
+ } else {
+ return text
+ }
+ }
+
+ static Object POST(url, data = null, isJson = false) {
def conn = new URL(url).openConnection()
conn.setRequestMethod('POST')
conn.setRequestProperty('Authorization', 'Basic cm9vdDo=') //token for
root
diff --git a/regression-test/plugins/plugin_curl_requester.groovy
b/regression-test/plugins/plugin_curl_requester.groovy
index aed5e518efe..51e1a893796 100644
--- a/regression-test/plugins/plugin_curl_requester.groovy
+++ b/regression-test/plugins/plugin_curl_requester.groovy
@@ -16,6 +16,8 @@
// under the License.
import org.apache.doris.regression.suite.Suite
+import org.apache.doris.regression.util.Http
+import org.apache.doris.regression.util.NodeType
import org.codehaus.groovy.runtime.IOGroovyMethods
Suite.metaClass.curl = { String method, String url /* param */->
@@ -76,3 +78,46 @@ Suite.metaClass.update_be_config = { String ip, String port,
String key, String
}
logger.info("Added 'update_be_config' function to Suite")
+
+Suite.metaClass.update_all_be_config = { String key, Object value ->
+ def backendId_to_backendIP = [:]
+ def backendId_to_backendHttpPort = [:]
+ getBackendIpHttpPort(backendId_to_backendIP, backendId_to_backendHttpPort);
+ backendId_to_backendIP.each { beId, beIp ->
+ def port = backendId_to_backendHttpPort.get(beId)
+ def url = "http://${beIp}:${port}/api/update_config?${key}=${value}"
+ def result = Http.POST(url, null, true)
+ assert result.size() == 1, result.toString()
+ assert result[0].status == "OK", result.toString()
+ }
+}
+
+logger.info("Added 'update_all_be_config' function to Suite")
+
+
+Suite.metaClass._be_report = { String ip, int port, String reportName ->
+ def url = "http://${ip}:${port}/api/report/${reportName}"
+ def result = Http.GET(url, true)
+ Http.checkHttpResult(result, NodeType.BE)
+}
+
+// before report, be need random sleep 5s
+Suite.metaClass.be_report_disk = { String ip, int port ->
+ _be_report(ip, port, "disk")
+}
+
+logger.info("Added 'be_report_disk' function to Suite")
+
+// before report, be need random sleep 5s
+Suite.metaClass.be_report_tablet = { String ip, int port ->
+ _be_report(ip, port, "tablet")
+}
+
+logger.info("Added 'be_report_tablet' function to Suite")
+
+// before report, be need random sleep 5s
+Suite.metaClass.be_report_task = { String ip, int port ->
+ _be_report(ip, port, "task")
+}
+
+logger.info("Added 'be_report_task' function to Suite")
diff --git
a/regression-test/suites/insert_p0/test_be_inject_publish_txn_fail.groovy
b/regression-test/suites/insert_p0/test_be_inject_publish_txn_fail.groovy
new file mode 100644
index 00000000000..bd7fe9d8a2f
--- /dev/null
+++ b/regression-test/suites/insert_p0/test_be_inject_publish_txn_fail.groovy
@@ -0,0 +1,77 @@
+// 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.
+
+suite('test_be_inject_publish_txn_fail', 'nonConcurrent') {
+ def tbl = 'test_be_inject_publish_txn_fail_tbl'
+ def dbug1 = 'TxnManager.publish_txn.random_failed_before_save_rs_meta'
+ def dbug2 = 'TxnManager.publish_txn.random_failed_after_save_rs_meta'
+
+ def allBeReportTask = { ->
+ def backendId_to_backendIP = [:]
+ def backendId_to_backendHttpPort = [:]
+ getBackendIpHttpPort(backendId_to_backendIP,
backendId_to_backendHttpPort)
+ backendId_to_backendIP.each { beId, beIp ->
+ def port = backendId_to_backendHttpPort.get(beId) as int
+ be_report_task(beIp, port)
+ }
+ }
+
+ def testInsertValue = { dbug, value ->
+ // insert succ but not visible
+ GetDebugPoint().enableDebugPointForAllBEs(dbug, [percent : 1.0])
+ sql "INSERT INTO ${tbl} VALUES (${value})"
+ sleep(6000)
+ order_qt_select_1 "SELECT * FROM ${tbl}"
+
+ GetDebugPoint().disableDebugPointForAllBEs(dbug)
+
+ // be report publish fail to fe, then fe will not remove its task.
+ // after be report its tasks, fe will resend publish version task to
be.
+ // the txn will visible
+ allBeReportTask()
+ sleep(8000)
+ order_qt_select_2 "SELECT * FROM ${tbl}"
+ }
+
+ try {
+ sql "DROP TABLE IF EXISTS ${tbl} FORCE"
+ sql "CREATE TABLE ${tbl} (k INT) DISTRIBUTED BY HASH(k) BUCKETS 5
PROPERTIES ('replication_num' = '1')"
+
+ sql "ADMIN SET FRONTEND CONFIG ('agent_task_resend_wait_time_ms' =
'1000')"
+ sql 'SET insert_visible_timeout_ms = 2000'
+
+ testInsertValue dbug1, 100
+ testInsertValue dbug2, 200
+ } finally {
+ try {
+ sql "ADMIN SET FRONTEND CONFIG ('agent_task_resend_wait_time_ms' =
'5000')"
+ } catch (Throwable e) {
+ }
+
+ try {
+ GetDebugPoint().disableDebugPointForAllBEs(dbug1)
+ } catch (Throwable e) {
+ }
+
+ try {
+ GetDebugPoint().disableDebugPointForAllBEs(dbug2)
+ } catch (Throwable e) {
+ }
+
+ sql "DROP TABLE IF EXISTS ${tbl} FORCE"
+ }
+}
diff --git a/regression-test/suites/plugin_p0/test_plugin_curl_requester.groovy
b/regression-test/suites/plugin_p0/test_plugin_curl_requester.groovy
new file mode 100644
index 00000000000..31d5bc36671
--- /dev/null
+++ b/regression-test/suites/plugin_p0/test_plugin_curl_requester.groovy
@@ -0,0 +1,30 @@
+// 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.
+
+suite('test_plugin_curl_requester') {
+ def backendId_to_backendIP = [:]
+ def backendId_to_backendHttpPort = [:]
+ getBackendIpHttpPort(backendId_to_backendIP, backendId_to_backendHttpPort)
+
+ backendId_to_backendIP.each { beId, beIp ->
+ def port = backendId_to_backendHttpPort.get(beId) as int
+ be_report_disk(beIp, port)
+ be_report_task(beIp, port)
+ be_report_tablet(beIp, port)
+ }
+}
+
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]