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]


Reply via email to