This is an automated email from the ASF dual-hosted git repository.
dataroaring pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 8679095e5c [feature](debug) support debug point used in debug code
(#24502)
8679095e5c is described below
commit 8679095e5c8370f46a089ea0d0d3b5b7f6f392c1
Author: yujun <[email protected]>
AuthorDate: Mon Sep 25 17:56:12 2023 +0800
[feature](debug) support debug point used in debug code (#24502)
---
be/src/common/config.cpp | 2 +
be/src/common/config.h | 2 +
be/src/http/action/debug_point_action.cpp | 93 ++++++++++
.../action/debug_point_action.h} | 43 +++--
be/src/http/action/download_action.cpp | 2 +
be/src/http/ev_http_server.h | 2 +-
be/src/service/http_service.cpp | 23 ++-
be/src/service/http_service.h | 3 +
be/src/util/debug_points.cpp | 93 ++++++++++
be/src/util/debug_points.h | 67 ++++++++
be/test/http/http_auth_test.cpp | 2 +
.../testutil/http_utils.cpp} | 32 +---
be/test/testutil/http_utils.h | 37 ++++
be/test/testutil/run_all_tests.cpp | 7 +
be/test/util/debug_points_test.cpp | 59 +++++++
.../http-actions/fe/debug-point-action.md | 185 ++++++++++++++++++++
docs/sidebars.json | 1 +
.../http-actions/fe/debug-point-action.md | 187 +++++++++++++++++++++
.../main/java/org/apache/doris/common/Config.java | 4 +
.../apache/doris/common/util/DebugPointUtil.java | 81 +++++++++
.../apache/doris/httpv2/rest/DebugPointAction.java | 105 ++++++++++++
.../doris/common/util/DebugPointUtilTest.java | 70 ++++++++
22 files changed, 1052 insertions(+), 48 deletions(-)
diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp
index 5df93c8798..c3d3193c2a 100644
--- a/be/src/common/config.cpp
+++ b/be/src/common/config.cpp
@@ -953,6 +953,8 @@ DEFINE_Bool(enable_java_support, "true");
// Set config randomly to check more issues in github workflow
DEFINE_Bool(enable_fuzzy_mode, "false");
+DEFINE_Bool(enable_debug_points, "false");
+
DEFINE_Int32(pipeline_executor_size, "0");
DEFINE_Bool(enable_workload_group_for_scan, "false");
diff --git a/be/src/common/config.h b/be/src/common/config.h
index e2b8481d02..fba48bf3f4 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -1002,6 +1002,8 @@ DECLARE_Bool(enable_java_support);
// Set config randomly to check more issues in github workflow
DECLARE_Bool(enable_fuzzy_mode);
+DECLARE_Bool(enable_debug_points);
+
DECLARE_Int32(pipeline_executor_size);
DECLARE_Bool(enable_workload_group_for_scan);
diff --git a/be/src/http/action/debug_point_action.cpp
b/be/src/http/action/debug_point_action.cpp
new file mode 100644
index 0000000000..08b1e116b2
--- /dev/null
+++ b/be/src/http/action/debug_point_action.cpp
@@ -0,0 +1,93 @@
+// 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/debug_point_action.h"
+
+#include "common/config.h"
+#include "http/http_channel.h"
+#include "http/http_status.h"
+#include "util/debug_points.h"
+
+namespace doris {
+
+void BaseDebugPointAction::handle(HttpRequest* req) {
+ LOG(INFO) << "accept one request " << req->debug_string();
+ Status status;
+ if (config::enable_debug_points) {
+ status = _handle(req);
+ } else {
+ status = Status::InternalError(
+ "Disable debug points. please check
config::enable_debug_points");
+ }
+ std::string result = status.to_json();
+ LOG(INFO) << "handle request result:" << result;
+ if (status.ok()) {
+ HttpChannel::send_reply(req, HttpStatus::OK, result);
+ } else {
+ HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR,
result);
+ }
+}
+
+Status AddDebugPointAction::_handle(HttpRequest* req) {
+ std::string debug_point = req->param("debug_point");
+ std::string execute = req->param("execute");
+ std::string timeout = req->param("timeout");
+ if (debug_point.empty()) {
+ return Status::InternalError("Empty debug point name");
+ }
+ int64_t execute_limit = -1;
+ int64_t timeout_second = -1;
+ try {
+ if (!execute.empty()) {
+ execute_limit = std::stol(execute);
+ }
+ } catch (const std::exception& e) {
+ return Status::InternalError("Invalid execute limit format, execute
{}, err {}", execute,
+ e.what());
+ }
+ try {
+ if (!timeout.empty()) {
+ timeout_second = std::stol(timeout);
+ }
+ } catch (const std::exception& e) {
+ return Status::InternalError("Invalid timeout format, timeout {}, err
{}", timeout,
+ e.what());
+ }
+
+ DebugPoints::instance()->add(debug_point, execute_limit, timeout_second);
+
+ return Status::OK();
+}
+
+Status RemoveDebugPointAction::_handle(HttpRequest* req) {
+ std::string debug_point = req->param("debug_point");
+ if (debug_point.empty()) {
+ return Status::InternalError("Empty debug point name");
+ }
+
+ DebugPoints::instance()->remove(debug_point);
+
+ return Status::OK();
+}
+
+Status ClearDebugPointsAction::_handle(HttpRequest* req) {
+ DebugPoints::instance()->clear();
+
+ return Status::OK();
+}
+
+} // namespace doris
diff --git a/be/src/service/http_service.h
b/be/src/http/action/debug_point_action.h
similarity index 52%
copy from be/src/service/http_service.h
copy to be/src/http/action/debug_point_action.h
index 05d36fdd75..47b3620456 100644
--- a/be/src/service/http_service.h
+++ b/be/src/http/action/debug_point_action.h
@@ -17,34 +17,43 @@
#pragma once
-#include <memory>
-
-#include "common/object_pool.h"
#include "common/status.h"
+#include "http/http_handler_with_auth.h"
+#include "http/http_request.h"
namespace doris {
-class ExecEnv;
-class EvHttpServer;
-class WebPageHandler;
+class BaseDebugPointAction : public HttpHandlerWithAuth {
+public:
+ using HttpHandlerWithAuth::HttpHandlerWithAuth;
+ void handle(HttpRequest* req) override;
+
+private:
+ virtual Status _handle(HttpRequest* req) = 0;
+};
-// HTTP service for Doris BE
-class HttpService {
+class AddDebugPointAction : public BaseDebugPointAction {
public:
- HttpService(ExecEnv* env, int port, int num_threads);
- ~HttpService();
+ using BaseDebugPointAction::BaseDebugPointAction;
+
+private:
+ Status _handle(HttpRequest* req) override;
+};
- Status start();
- void stop();
+class RemoveDebugPointAction : public BaseDebugPointAction {
+public:
+ using BaseDebugPointAction::BaseDebugPointAction;
private:
- ExecEnv* _env;
- ObjectPool _pool;
+ Status _handle(HttpRequest* req) override;
+};
- std::unique_ptr<EvHttpServer> _ev_http_server;
- std::unique_ptr<WebPageHandler> _web_page_handler;
+class ClearDebugPointsAction : public BaseDebugPointAction {
+public:
+ using BaseDebugPointAction::BaseDebugPointAction;
- bool stopped = false;
+private:
+ Status _handle(HttpRequest* req) override;
};
} // namespace doris
diff --git a/be/src/http/action/download_action.cpp
b/be/src/http/action/download_action.cpp
index 4323b67112..7e0083c5eb 100644
--- a/be/src/http/action/download_action.cpp
+++ b/be/src/http/action/download_action.cpp
@@ -59,7 +59,9 @@ DownloadAction::DownloadAction(ExecEnv* exec_env, const
std::vector<std::string>
DownloadAction::DownloadAction(ExecEnv* exec_env, const std::string&
error_log_root_dir)
: _exec_env(exec_env), _download_type(ERROR_LOG), _num_workers(0) {
+#ifndef BE_TEST
io::global_local_filesystem()->canonicalize(error_log_root_dir,
&_error_log_root_dir);
+#endif
}
void DownloadAction::handle_normal(HttpRequest* req, const std::string&
file_param) {
diff --git a/be/src/http/ev_http_server.h b/be/src/http/ev_http_server.h
index bf6b200bdc..e7ad1c052a 100644
--- a/be/src/http/ev_http_server.h
+++ b/be/src/http/ev_http_server.h
@@ -53,7 +53,7 @@ public:
int on_header(struct evhttp_request* ev_req);
// get real port
- int get_real_port() { return _real_port; }
+ int get_real_port() const { return _real_port; }
private:
Status _bind();
diff --git a/be/src/service/http_service.cpp b/be/src/service/http_service.cpp
index fd8c640d20..fd9f348506 100644
--- a/be/src/service/http_service.cpp
+++ b/be/src/service/http_service.cpp
@@ -27,6 +27,7 @@
#include "http/action/checksum_action.h"
#include "http/action/compaction_action.h"
#include "http/action/config_action.h"
+#include "http/action/debug_point_action.h"
#include "http/action/download_action.h"
#include "http/action/download_binlog_action.h"
#include "http/action/file_cache_action.h"
@@ -232,7 +233,23 @@ Status HttpService::start() {
PadRowsetAction* pad_rowset_action =
_pool.add(new PadRowsetAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN));
- _ev_http_server->register_handler(HttpMethod::POST, "api/pad_rowset",
pad_rowset_action);
+ _ev_http_server->register_handler(HttpMethod::POST, "/api/pad_rowset",
pad_rowset_action);
+
+ // debug point
+ AddDebugPointAction* add_debug_point_action =
+ _pool.add(new AddDebugPointAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN));
+ _ev_http_server->register_handler(HttpMethod::POST,
"/api/debug_point/add/{debug_point}",
+ add_debug_point_action);
+
+ RemoveDebugPointAction* remove_debug_point_action = _pool.add(
+ new RemoveDebugPointAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN));
+ _ev_http_server->register_handler(HttpMethod::POST,
"/api/debug_point/remove/{debug_point}",
+ remove_debug_point_action);
+
+ ClearDebugPointsAction* clear_debug_points_action = _pool.add(
+ new ClearDebugPointsAction(_env, TPrivilegeHier::GLOBAL,
TPrivilegeType::ADMIN));
+ _ev_http_server->register_handler(HttpMethod::POST,
"/api/debug_point/clear",
+ clear_debug_points_action);
_ev_http_server->start();
return Status::OK();
@@ -247,4 +264,8 @@ void HttpService::stop() {
stopped = true;
}
+int HttpService::get_real_port() const {
+ return _ev_http_server->get_real_port();
+}
+
} // namespace doris
diff --git a/be/src/service/http_service.h b/be/src/service/http_service.h
index 05d36fdd75..46b5b3c5b3 100644
--- a/be/src/service/http_service.h
+++ b/be/src/service/http_service.h
@@ -37,6 +37,9 @@ public:
Status start();
void stop();
+ // get real port
+ int get_real_port() const;
+
private:
ExecEnv* _env;
ObjectPool _pool;
diff --git a/be/src/util/debug_points.cpp b/be/src/util/debug_points.cpp
new file mode 100644
index 0000000000..587f8c944a
--- /dev/null
+++ b/be/src/util/debug_points.cpp
@@ -0,0 +1,93 @@
+// 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 "util/debug_points.h"
+
+#include "common/logging.h"
+#include "util/time.h"
+
+namespace doris {
+
+DebugPoints::DebugPoints() : _debug_points(std::make_shared<const
DebugPointMap>()) {}
+
+DebugPoints* DebugPoints::instance() {
+ static DebugPoints instance;
+ return &instance;
+}
+
+bool DebugPoints::is_enable(const std::string& name) {
+ if (!config::enable_debug_points) {
+ return false;
+ }
+ auto map_ptr = std::atomic_load_explicit(&_debug_points,
std::memory_order_relaxed);
+ auto it = map_ptr->find(name);
+ if (it == map_ptr->end()) {
+ return false;
+ }
+
+ auto& debug_point = *(it->second);
+ if ((debug_point.expire_ms > 0 && MonotonicMillis() >=
debug_point.expire_ms) ||
+ (debug_point.execute_limit > 0 &&
+ debug_point.execute_num.fetch_add(1, std::memory_order_relaxed) >=
+ debug_point.execute_limit)) {
+ remove(name);
+ return false;
+ }
+
+ return true;
+}
+
+void DebugPoints::add(const std::string& name, int64_t execute_limit, int64_t
timeout_second) {
+ auto debug_point = std::make_shared<DebugPoint>();
+ debug_point->execute_limit = execute_limit;
+ if (timeout_second > 0) {
+ debug_point->expire_ms = MonotonicMillis() + timeout_second *
MILLIS_PER_SEC;
+ }
+ update([&](DebugPointMap& new_points) { new_points[name] = debug_point; });
+
+ LOG(INFO) << "add debug point: name=" << name << ", execute=" <<
execute_limit
+ << ", timeout=" << timeout_second;
+}
+
+void DebugPoints::remove(const std::string& name) {
+ bool exists = false;
+ update([&](DebugPointMap& new_points) { exists = new_points.erase(name) >
0; });
+
+ LOG(INFO) << "remove debug point: name=" << name << ", exists=" << exists;
+}
+
+void DebugPoints::update(std::function<void(DebugPointMap&)>&& handler) {
+ auto old_points = std::atomic_load_explicit(&_debug_points,
std::memory_order_relaxed);
+ while (true) {
+ auto new_points = std::make_shared<DebugPointMap>(*old_points);
+ handler(*new_points);
+ if (std::atomic_compare_exchange_strong_explicit(
+ &_debug_points, &old_points,
+ std::static_pointer_cast<const DebugPointMap>(new_points),
+ std::memory_order_relaxed, std::memory_order_relaxed)) {
+ break;
+ }
+ }
+}
+
+void DebugPoints::clear() {
+ std::atomic_store_explicit(&_debug_points, std::make_shared<const
DebugPointMap>(),
+ std::memory_order_relaxed);
+ LOG(INFO) << "clear debug points";
+}
+
+} // namespace doris
diff --git a/be/src/util/debug_points.h b/be/src/util/debug_points.h
new file mode 100644
index 0000000000..704405689c
--- /dev/null
+++ b/be/src/util/debug_points.h
@@ -0,0 +1,67 @@
+// 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 <atomic>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "common/compiler_util.h"
+#include "common/config.h"
+
+#define DBUG_EXECUTE_IF(debug_point, code) \
+ if (UNLIKELY(config::enable_debug_points)) { \
+ if (DebugPoints::instance()->is_enable(debug_point)) { \
+ code; \
+ } \
+ }
+
+namespace doris {
+
+struct DebugPoint {
+ std::atomic<int64_t> execute_num {0};
+ int64_t execute_limit = -1;
+ int64_t expire_ms = -1;
+};
+
+class DebugPoints {
+public:
+ bool is_enable(const std::string& name);
+ void add(const std::string& name, int64_t execute_limit, int64_t
timeout_second);
+ void remove(const std::string& name);
+ void clear();
+
+ static DebugPoints* instance();
+
+private:
+ DebugPoints();
+
+ using DebugPointMap = std::map<std::string, std::shared_ptr<DebugPoint>>;
+
+ // handler(new_debug_points)
+ void update(std::function<void(DebugPointMap&)>&& handler);
+
+private:
+ /// TODO: replace atomic_load/store() on shared_ptr (which is deprecated
as of C++20) by C++20 std::atomic<std::shared_ptr>.
+ /// Clang 15 currently does not support it.
+ std::shared_ptr<const DebugPointMap> _debug_points;
+};
+
+} // namespace doris
diff --git a/be/test/http/http_auth_test.cpp b/be/test/http/http_auth_test.cpp
index c530d654ee..6077f871b7 100644
--- a/be/test/http/http_auth_test.cpp
+++ b/be/test/http/http_auth_test.cpp
@@ -25,6 +25,7 @@
#include "http/http_headers.h"
#include "http/http_request.h"
#include "http/utils.h"
+#include "util/defer_op.h"
namespace doris {
@@ -58,6 +59,7 @@ TEST_F(HttpAuthTest, disable_auth) {
}
TEST_F(HttpAuthTest, enable_all_http_auth) {
+ Defer defer {[]() { config::enable_all_http_auth = false; }};
config::enable_all_http_auth = true;
// 1. empty auth info
diff --git a/be/src/service/http_service.h b/be/test/testutil/http_utils.cpp
similarity index 60%
copy from be/src/service/http_service.h
copy to be/test/testutil/http_utils.cpp
index 05d36fdd75..bd6ec08066 100644
--- a/be/src/service/http_service.h
+++ b/be/test/testutil/http_utils.cpp
@@ -15,36 +15,10 @@
// specific language governing permissions and limitations
// under the License.
-#pragma once
-
-#include <memory>
-
-#include "common/object_pool.h"
-#include "common/status.h"
+#include "testutil/http_utils.h"
namespace doris {
-class ExecEnv;
-class EvHttpServer;
-class WebPageHandler;
-
-// HTTP service for Doris BE
-class HttpService {
-public:
- HttpService(ExecEnv* env, int port, int num_threads);
- ~HttpService();
-
- Status start();
- void stop();
-
-private:
- ExecEnv* _env;
- ObjectPool _pool;
-
- std::unique_ptr<EvHttpServer> _ev_http_server;
- std::unique_ptr<WebPageHandler> _web_page_handler;
-
- bool stopped = false;
-};
+std::string global_test_http_host = "Not init http server.";
-} // namespace doris
+}
diff --git a/be/test/testutil/http_utils.h b/be/test/testutil/http_utils.h
new file mode 100644
index 0000000000..3bfbbc9e68
--- /dev/null
+++ b/be/test/testutil/http_utils.h
@@ -0,0 +1,37 @@
+// 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 "http/http_client.h"
+
+#define POST_HTTP_TO_TEST_SERVER(uri)
\
+ {
\
+ std::string response;
\
+ HttpClient client;
\
+ std::string full_uri = global_test_http_host + uri;
\
+ auto status = client.init(full_uri);
\
+ ASSERT_TRUE(status.ok()) << "uri=" << full_uri << ", err=" <<
status.to_string(); \
+ status = client.execute_post_request("{}", &response);
\
+ ASSERT_TRUE(status.ok()) << status.to_string();
\
+ }
+
+namespace doris {
+
+extern std::string global_test_http_host;
+
+}
diff --git a/be/test/testutil/run_all_tests.cpp
b/be/test/testutil/run_all_tests.cpp
index 9a242e3431..bf496c35d9 100644
--- a/be/test/testutil/run_all_tests.cpp
+++ b/be/test/testutil/run_all_tests.cpp
@@ -32,6 +32,8 @@
#include "runtime/memory/thread_mem_tracker_mgr.h"
#include "runtime/thread_context.h"
#include "service/backend_options.h"
+#include "service/http_service.h"
+#include "testutil/http_utils.h"
#include "util/cpu_info.h"
#include "util/disk_info.h"
#include "util/mem_info.h"
@@ -56,6 +58,11 @@ int main(int argc, char** argv) {
doris::DiskInfo::init();
doris::MemInfo::init();
doris::BackendOptions::init();
+
+ auto service =
std::make_unique<doris::HttpService>(doris::ExecEnv::GetInstance(), 0, 1);
+ service->start();
+ doris::global_test_http_host = "http://127.0.0.1:" +
std::to_string(service->get_real_port());
+
int res = RUN_ALL_TESTS();
doris::ExecEnv::GetInstance()->get_tablet_schema_cache()->stop();
return res;
diff --git a/be/test/util/debug_points_test.cpp
b/be/test/util/debug_points_test.cpp
new file mode 100644
index 0000000000..c2cf2bdedf
--- /dev/null
+++ b/be/test/util/debug_points_test.cpp
@@ -0,0 +1,59 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "util/debug_points.h"
+
+#include <gtest/gtest-message.h>
+#include <gtest/gtest-test-part.h>
+
+#include <chrono>
+#include <thread>
+
+#include "gtest/gtest_pred_impl.h"
+#include "testutil/http_utils.h"
+
+namespace doris {
+
+TEST(DebugPointsTest, BaseTest) {
+ config::enable_debug_points = true;
+ DebugPoints::instance()->clear();
+
+ EXPECT_FALSE(DebugPoints::instance()->is_enable("dbug1"));
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/add/dbug1");
+ EXPECT_TRUE(DebugPoints::instance()->is_enable("dbug1"));
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/remove/dbug1");
+ EXPECT_FALSE(DebugPoints::instance()->is_enable("dbug1"));
+
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/add/dbug2");
+ EXPECT_TRUE(DebugPoints::instance()->is_enable("dbug2"));
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/clear");
+ EXPECT_FALSE(DebugPoints::instance()->is_enable("dbug2"));
+
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/add/dbug3?execute=3");
+ for (int i = 0; i < 3; i++) {
+ EXPECT_TRUE(DebugPoints::instance()->is_enable("dbug3"));
+ }
+ EXPECT_FALSE(DebugPoints::instance()->is_enable("dbug3"));
+
+ POST_HTTP_TO_TEST_SERVER("/api/debug_point/add/dbug4?timeout=1");
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ EXPECT_TRUE(DebugPoints::instance()->is_enable("dbug4"));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+ EXPECT_FALSE(DebugPoints::instance()->is_enable("dbug4"));
+}
+
+} // namespace doris
diff --git a/docs/en/docs/admin-manual/http-actions/fe/debug-point-action.md
b/docs/en/docs/admin-manual/http-actions/fe/debug-point-action.md
new file mode 100644
index 0000000000..cac2afdcd2
--- /dev/null
+++ b/docs/en/docs/admin-manual/http-actions/fe/debug-point-action.md
@@ -0,0 +1,185 @@
+---
+{
+ "title": "Debug Point",
+ "language": "en"
+}
+---
+
+<!--
+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.
+-->
+
+# Debug Point
+
+Debug point is used in code test. When enabling a debug point, it can run
related code.
+
+Both FE and BE support debug points.
+
+## Code Example
+
+FE example
+
+```java
+private Status foo() {
+ // dbug_fe_foo_do_nothing is the debug point name.
+ // When it's active,DebugPointUtil.isEnable("dbug_fe_foo_do_nothing")
will return true.
+ if (DebugPointUtil.isEnable("dbug_fe_foo_do_nothing")) {
+ return Status.Nothing;
+ }
+
+ do_foo_action();
+
+ return Status.Ok;
+}
+```
+
+BE 桩子示例代码
+
+```c++
+void Status foo() {
+ // dbug_be_foo_do_nothing is the debug point name.
+ // When it's active,DEBUG_EXECUTE_IF will execute the code block.
+ DEBUG_EXECUTE_IF("dbug_be_foo_do_nothing", { return Status.Nothing; });
+
+ do_foo_action();
+
+ return Status.Ok;
+}
+```
+
+## Global config
+
+To activate debug points, need set `enable_debug_points` to true.
+
+`enable_debug_points` was located in FE's fe.conf and BE's be.conf。
+
+
+## Enable Debug Point
+
+### API
+
+```
+ POST
/api/debug_point/add/{debug_point_name}[?timeout=<int>&execute=<int>]
+```
+
+
+### Query Parameters
+
+* `debug_point_name`
+ Debug point name. Require.
+
+* `timeout`
+ Timeout in seconds. When timeout, the debug point will be disable. Default
is -1, not timeout. Optional.
+
+* `execute`
+ Max active times。Default is -1, unlimit active times. Optional.
+
+
+### Request body
+
+None
+
+### Response
+
+ ```
+ {
+ msg: "OK",
+ code: 0
+ }
+ ```
+
+### Examples
+
+
+Enable debug point `foo`, activate no more than five times.
+
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/add/foo?execute=5"
+
+ ```
+
+## Disable Debug Point
+
+### API
+
+```
+ POST /api/debug_point/remove/{debug_point_name}
+```
+
+
+### Query Parameters
+
+* `debug_point_name`
+ Debug point name. Require.
+
+
+
+### Request body
+
+None
+
+### Response
+
+```
+ {
+ msg: "OK",
+ code: 0
+ }
+```
+
+### Examples
+
+
+Disable debug point `foo`。
+
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/remove/foo"
+
+ ```
+
+## Clear Debug Points
+
+### API
+
+```
+ POST /api/debug_point/clear
+```
+
+
+
+### Request body
+
+None
+
+### Response
+
+ ```
+ {
+ msg: "OK",
+ code: 0
+ }
+ ```
+
+### Examples
+
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/clear"
+ ```
\ No newline at end of file
diff --git a/docs/sidebars.json b/docs/sidebars.json
index 409dad1310..cfc10dbb51 100644
--- a/docs/sidebars.json
+++ b/docs/sidebars.json
@@ -1257,6 +1257,7 @@
"admin-manual/http-actions/fe/upload-action",
"admin-manual/http-actions/fe/import-action",
"admin-manual/http-actions/fe/meta-info-action-V2",
+
"admin-manual/http-actions/fe/debug-point-action",
"admin-manual/http-actions/fe/statistic-action"
]
},
diff --git a/docs/zh-CN/docs/admin-manual/http-actions/fe/debug-point-action.md
b/docs/zh-CN/docs/admin-manual/http-actions/fe/debug-point-action.md
new file mode 100644
index 0000000000..a1c9a59a35
--- /dev/null
+++ b/docs/zh-CN/docs/admin-manual/http-actions/fe/debug-point-action.md
@@ -0,0 +1,187 @@
+---
+{
+ "title": "代码打桩",
+ "language": "zh-CN"
+}
+---
+
+<!--
+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.
+-->
+
+# 代码打桩
+
+代码打桩是代码测试使用的。激活木桩后,可以执行木桩代码。木桩的名字是任意取的。
+
+FE 和 BE 都支持代码打桩。
+
+## 木桩代码示例
+
+FE 桩子示例代码
+
+```java
+private Status foo() {
+ // dbug_fe_foo_do_nothing 是一个木桩名字,
+ // 打开这个木桩之后,DebugPointUtil.isEnable("dbug_fe_foo_do_nothing") 将会返回true
+ if (DebugPointUtil.isEnable("dbug_fe_foo_do_nothing")) {
+ return Status.Nothing;
+ }
+
+ do_foo_action();
+
+ return Status.Ok;
+}
+```
+
+BE 桩子示例代码
+
+```c++
+void Status foo() {
+
+ // dbug_be_foo_do_nothing 是一个木桩名字,
+ // 打开这个木桩之后,DEBUG_EXECUTE_IF 将会执行宏参数中的代码块
+ DEBUG_EXECUTE_IF("dbug_be_foo_do_nothing", { return Status.Nothing; });
+
+ do_foo_action();
+
+ return Status.Ok;
+}
+```
+
+## 总开关
+
+需要把木桩总开关 `enable_debug_points` 打开之后,才能激活木桩。默认情况下,木桩总开关是关闭的。
+
+总开关`enable_debug_points` 分别在 FE 的 fe.conf 和 BE 的 be.conf 中配置。
+
+
+## 打开木桩
+
+### API
+
+```
+ POST
/api/debug_point/add/{debug_point_name}[?timeout=<int>&execute=<int>]
+```
+
+
+### 参数
+
+* `debug_point_name`
+ 木桩名字。必填。
+
+* `timeout`
+ 超时时间,单位为秒。超时之后,木桩失活。默认值-1表示永远不超时。可填。
+
+* `execute`
+ 木桩最大激活次数。默认值-1表示不限激活次数。可填。
+
+
+### Request body
+
+无
+
+### Response
+
+ ```
+ {
+ msg: "OK",
+ code: 0
+ }
+ ```
+
+### Examples
+
+
+打开木桩 `foo`,最多激活5次。
+
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/add/foo?execute=5"
+
+ ```
+
+## 关闭木桩
+
+### API
+
+```
+ POST /api/debug_point/remove/{debug_point_name}
+```
+
+
+### 参数
+
+* `debug_point_name`
+ 木桩名字。必填。
+
+
+### Request body
+
+无
+
+### Response
+
+```
+ {
+ msg: "OK",
+ code: 0
+ }
+```
+
+### Examples
+
+
+关闭木桩`foo`。
+
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/remove/foo"
+
+ ```
+
+## 清除所有木桩
+
+### API
+
+```
+ POST /api/debug_point/clear
+```
+
+
+
+### Request body
+
+无
+
+### Response
+
+ ```
+ {
+ msg: "OK",
+ code: 0
+ }
+ ```
+
+### Examples
+
+
+清除所有木桩。
+
+ ```
+ curl -X POST "http://127.0.0.1:8030/api/debug_point/clear"
+ ```
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index 09b9e4abef..0df35d567f 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -1293,6 +1293,10 @@ public class Config extends ConfigBase {
@ConfField
public static boolean enable_bdbje_debug_mode = false;
+ @ConfField(mutable = false, masterOnly = true, description = {"是否开启debug
point模式,测试使用",
+ "is enable debug points, use in test."})
+ public static boolean enable_debug_points = false;
+
/**
* This config is used to try skip broker when access bos or other cloud
storage via broker
*/
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/util/DebugPointUtil.java
b/fe/fe-core/src/main/java/org/apache/doris/common/util/DebugPointUtil.java
new file mode 100644
index 0000000000..aab9b8f2ba
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/DebugPointUtil.java
@@ -0,0 +1,81 @@
+// 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.doris.common.util;
+
+import org.apache.doris.common.Config;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Use for manage debug points.
+ **/
+public class DebugPointUtil {
+ private static final Logger LOG =
LogManager.getLogger(DebugPointUtil.class);
+
+ private static final Map<String, DebugPoint> debugPoints = new
ConcurrentHashMap<>();
+
+ private static class DebugPoint {
+ public AtomicInteger executeNum = new AtomicInteger(0);
+ public int executeLimit = -1;
+ public long expireTime = -1;
+ }
+
+ public static boolean isEnable(String debugPointName) {
+ if (!Config.enable_debug_points) {
+ return false;
+ }
+
+ DebugPoint debugPoint = debugPoints.get(debugPointName);
+ if (debugPoint == null) {
+ return false;
+ }
+
+ if ((debugPoint.expireTime > 0 && System.currentTimeMillis() >=
debugPoint.expireTime)
+ || (debugPoint.executeLimit > 0 &&
debugPoint.executeNum.incrementAndGet() > debugPoint.executeLimit)) {
+ debugPoints.remove(debugPointName);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static void addDebugPoint(String name, int executeLimit, long
timeoutSecond) {
+ DebugPoint debugPoint = new DebugPoint();
+ debugPoint.executeLimit = executeLimit;
+ if (timeoutSecond > 0) {
+ debugPoint.expireTime = System.currentTimeMillis() + timeoutSecond
* 1000;
+ }
+ debugPoints.put(name, debugPoint);
+ LOG.info("add debug point: name={}, execute={}, timeout seconds={}",
name, executeLimit, timeoutSecond);
+ }
+
+ public static void removeDebugPoint(String name) {
+ DebugPoint debugPoint = debugPoints.remove(name);
+ LOG.info("remove debug point: name={}, exists={}", name, debugPoint !=
null);
+ }
+
+ public static void clearDebugPoints() {
+ debugPoints.clear();
+ LOG.info("clear debug points");
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/DebugPointAction.java
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/DebugPointAction.java
new file mode 100644
index 0000000000..25ee7a5d0a
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/DebugPointAction.java
@@ -0,0 +1,105 @@
+// 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.doris.httpv2.rest;
+
+import org.apache.doris.common.Config;
+import org.apache.doris.common.util.DebugPointUtil;
+import org.apache.doris.httpv2.entity.ResponseEntityBuilder;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.common.base.Strings;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * This class is used to add, remove or clear debug points.
+ */
+@RestController
+public class DebugPointAction extends RestBaseController {
+
+ @RequestMapping(path = "/api/debug_point/add/{debugPoint}", method =
RequestMethod.POST)
+ protected Object addDebugPoint(@PathVariable("debugPoint") String
debugPoint,
+ @RequestParam(name = "execute", required = false, defaultValue =
"") String execute,
+ @RequestParam(name = "timeout", required = false, defaultValue =
"") String timeout,
+ HttpServletRequest request, HttpServletResponse response) {
+ if (!Config.enable_debug_points) {
+ return ResponseEntityBuilder.internalError(
+ "Disable debug points. please check
Config.enable_debug_points");
+ }
+ executeCheckPassword(request, response);
+ checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(),
PrivPredicate.ADMIN);
+ if (Strings.isNullOrEmpty(debugPoint)) {
+ return ResponseEntityBuilder.badRequest("Empty debug point name.");
+ }
+ int executeLimit = -1;
+ if (!Strings.isNullOrEmpty(execute)) {
+ try {
+ executeLimit = Integer.valueOf(execute);
+ } catch (Exception e) {
+ return ResponseEntityBuilder.badRequest(
+ "Invalid execute format: " + execute + ", err " +
e.getMessage());
+ }
+ }
+ long timeoutSeconds = -1;
+ if (!Strings.isNullOrEmpty(timeout)) {
+ try {
+ timeoutSeconds = Long.valueOf(timeout);
+ } catch (Exception e) {
+ return ResponseEntityBuilder.badRequest(
+ "Invalid timeout format: " + timeout + ", err " +
e.getMessage());
+ }
+ }
+ DebugPointUtil.addDebugPoint(debugPoint, executeLimit, timeoutSeconds);
+ return ResponseEntityBuilder.ok();
+ }
+
+ @RequestMapping(path = "/api/debug_point/remove/{debugPoint}", method =
RequestMethod.POST)
+ protected Object removeDebugPoint(@PathVariable("debugPoint") String
debugPoint,
+ HttpServletRequest request, HttpServletResponse response) {
+ if (!Config.enable_debug_points) {
+ return ResponseEntityBuilder.internalError(
+ "Disable debug points. please check
Config.enable_debug_points");
+ }
+ executeCheckPassword(request, response);
+ checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(),
PrivPredicate.ADMIN);
+ if (Strings.isNullOrEmpty(debugPoint)) {
+ return ResponseEntityBuilder.badRequest("Empty debug point name.");
+ }
+ DebugPointUtil.removeDebugPoint(debugPoint);
+ return ResponseEntityBuilder.ok();
+ }
+
+ @RequestMapping(path = "/api/debug_point/clear", method =
RequestMethod.POST)
+ protected Object clearDebugPoints(HttpServletRequest request,
HttpServletResponse response) {
+ if (!Config.enable_debug_points) {
+ return ResponseEntityBuilder.internalError(
+ "Disable debug points. please check
Config.enable_debug_points");
+ }
+ executeCheckPassword(request, response);
+ checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(),
PrivPredicate.ADMIN);
+ DebugPointUtil.clearDebugPoints();
+ return ResponseEntityBuilder.ok();
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/common/util/DebugPointUtilTest.java
b/fe/fe-core/src/test/java/org/apache/doris/common/util/DebugPointUtilTest.java
new file mode 100644
index 0000000000..2845cec922
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/common/util/DebugPointUtilTest.java
@@ -0,0 +1,70 @@
+// 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.doris.common.util;
+
+import org.apache.doris.common.Config;
+import org.apache.doris.http.DorisHttpTestCase;
+
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DebugPointUtilTest extends DorisHttpTestCase {
+
+ @Test
+ public void testDebugPoint() throws Exception {
+ Config.enable_debug_points = true;
+
+ Assert.assertFalse(DebugPointUtil.isEnable("dbug1"));
+ sendRequest("/api/debug_point/add/dbug1");
+ Assert.assertTrue(DebugPointUtil.isEnable("dbug1"));
+ sendRequest("/api/debug_point/remove/dbug1");
+ Assert.assertFalse(DebugPointUtil.isEnable("dbug1"));
+
+ sendRequest("/api/debug_point/add/dbug2");
+ Assert.assertTrue(DebugPointUtil.isEnable("dbug2"));
+ sendRequest("/api/debug_point/clear");
+ Assert.assertFalse(DebugPointUtil.isEnable("dbug2"));
+
+ sendRequest("/api/debug_point/add/dbug3?execute=3");
+ for (int i = 0; i < 3; i++) {
+ Assert.assertTrue(DebugPointUtil.isEnable("dbug3"));
+ }
+ Assert.assertFalse(DebugPointUtil.isEnable("dbug3"));
+
+ sendRequest("/api/debug_point/add/dbug4?timeout=1");
+ Thread.sleep(200);
+ Assert.assertTrue(DebugPointUtil.isEnable("dbug4"));
+ Thread.sleep(1000);
+ Assert.assertFalse(DebugPointUtil.isEnable("dbug4"));
+ }
+
+ private void sendRequest(String uri) throws Exception {
+ Request request = new Request.Builder()
+ .post(RequestBody.create(JSON, "{}"))
+ .addHeader("Authorization", rootAuth)
+ .url("http://localhost:" + HTTP_PORT + uri)
+ .build();
+
+ Response response = networkClient.newCall(request).execute();
+ Assert.assertNotNull(response.body());
+ Assert.assertEquals(200, response.code());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]