This is an automated email from the ASF dual-hosted git repository.
gavinchou 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 e917993c6dc [feat](http) Support update and show config for recycler
and meta-service (#60816)
e917993c6dc is described below
commit e917993c6dcd65a091e330651105ce7c6c650373
Author: Yixuan Wang <[email protected]>
AuthorDate: Wed Mar 18 17:19:23 2026 +0800
[feat](http) Support update and show config for recycler and meta-service
(#60816)
- add `show_config` endpoint to MetaService HTTP API (including v1 path)
- refactor RecyclerService HTTP handlers and add
`show_config`/`update_config` support
- expose recycler thread pool group accessor used by HTTP service
- add cloud regression test for update_config + show_config roundtrip
- add `recycleServiceHttpAddress` in cloud_p0 custom regression config
example:
```
curl
"http://localhost:5678/MetaService/http/show_config?token=greedisgood9999&conf_key=compacted_rowset_retention_seconds"
{
"code": "OK",
"msg": "",
"result": [
[
"compacted_rowset_retention_seconds",
"int64_t",
"1080",
true
]
]
}
curl
"http://localhost:5678/MetaService/http/update_config?configs=compacted_rowset_retention_seconds=108&token=greedisgood9999"
{
"code": "OK",
"msg": ""
}
curl
"http://localhost:5678/MetaService/http/show_config?token=greedisgood9999&conf_key=compacted_rowset_retention_seconds"
{
"code": "OK",
"msg": "",
"result": [
[
"compacted_rowset_retention_seconds",
"int64_t",
"108",
true
]
]
}
```
---
cloud/src/meta-service/meta_service_http.cpp | 34 ++
cloud/src/recycler/recycler.h | 2 +
cloud/src/recycler/recycler_service.cpp | 400 ++++++++++++---------
cloud/src/recycler/recycler_service.h | 6 +-
.../cloud_p0/conf/regression-conf-custom.groovy | 1 +
.../test_update_and_show_cloud_config.groovy | 111 ++++++
6 files changed, 375 insertions(+), 179 deletions(-)
diff --git a/cloud/src/meta-service/meta_service_http.cpp
b/cloud/src/meta-service/meta_service_http.cpp
index 29b4b9287ad..2656b63f8bd 100644
--- a/cloud/src/meta-service/meta_service_http.cpp
+++ b/cloud/src/meta-service/meta_service_http.cpp
@@ -460,6 +460,38 @@ static HttpResponse
process_query_rate_limit(MetaServiceImpl* service, brpc::Con
return http_json_reply(MetaServiceCode::OK, "", sb.GetString());
}
+static HttpResponse process_show_config(MetaServiceImpl*, brpc::Controller*
cntl) {
+ auto& uri = cntl->http_request().uri();
+ std::string_view conf_name = http_query(uri, "conf_key");
+
+ if (config::full_conf_map == nullptr) {
+ return http_json_reply(MetaServiceCode::UNDEFINED_ERR, "config map not
initialized");
+ }
+
+ rapidjson::Document d;
+ d.SetArray();
+
+ for (auto& [name, field] : *config::Register::_s_field_map) {
+ if (!conf_name.empty() && name != conf_name) {
+ continue;
+ }
+ auto it = config::full_conf_map->find(name);
+ std::string value = (it != config::full_conf_map->end()) ? it->second
: "";
+
+ rapidjson::Value entry(rapidjson::kArrayType);
+ entry.PushBack(rapidjson::Value(name.c_str(), d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(field.type, d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(value.c_str(), d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(field.valmutable), d.GetAllocator());
+ d.PushBack(entry, d.GetAllocator());
+ }
+
+ rapidjson::StringBuffer sb;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
+ d.Accept(writer);
+ return http_json_reply(MetaServiceCode::OK, "", sb.GetString());
+}
+
static HttpResponse process_update_config(MetaServiceImpl* service,
brpc::Controller* cntl) {
const auto& uri = cntl->http_request().uri();
bool persist = (http_query(uri, "persist") == "true");
@@ -975,6 +1007,7 @@ void
MetaServiceImpl::http(::google::protobuf::RpcController* controller,
{"adjust_rate_limit", process_adjust_rate_limit},
{"list_rate_limit", process_query_rate_limit},
{"update_config", process_update_config},
+ {"show_config", process_show_config},
{"v1/abort_txn", process_abort_txn},
{"v1/abort_tablet_job", process_abort_tablet_job},
{"v1/alter_ram_user", process_alter_ram_user},
@@ -982,6 +1015,7 @@ void
MetaServiceImpl::http(::google::protobuf::RpcController* controller,
{"v1/adjust_rate_limit", process_adjust_rate_limit},
{"v1/list_rate_limit", process_query_rate_limit},
{"v1/update_config", process_update_config},
+ {"v1/show_config", process_show_config},
};
auto* cntl = static_cast<brpc::Controller*>(controller);
diff --git a/cloud/src/recycler/recycler.h b/cloud/src/recycler/recycler.h
index 98b142e4003..c63d15e77ea 100644
--- a/cloud/src/recycler/recycler.h
+++ b/cloud/src/recycler/recycler.h
@@ -88,6 +88,8 @@ public:
bool stopped() const { return stopped_.load(std::memory_order_acquire); }
+ RecyclerThreadPoolGroup& thread_pool_group() { return _thread_pool_group; }
+
private:
void recycle_callback();
diff --git a/cloud/src/recycler/recycler_service.cpp
b/cloud/src/recycler/recycler_service.cpp
index 4b33ef76c95..d9342f7ee7b 100644
--- a/cloud/src/recycler/recycler_service.cpp
+++ b/cloud/src/recycler/recycler_service.cpp
@@ -22,6 +22,9 @@
#include <fmt/format.h>
#include <gen_cpp/cloud.pb.h>
#include <google/protobuf/util/json_util.h>
+#include <rapidjson/document.h>
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/stringbuffer.h>
#include <algorithm>
#include <functional>
@@ -32,10 +35,13 @@
#include <vector>
#include "common/config.h"
+#include "common/configbase.h"
#include "common/defer.h"
#include "common/logging.h"
+#include "common/string_util.h"
#include "common/util.h"
#include "cpp/s3_rate_limiter.h"
+#include "meta-service/meta_service_http.h"
#include "meta-store/keys.h"
#include "meta-store/txn_kv_error.h"
#include "recycler/checker.h"
@@ -47,8 +53,6 @@
namespace doris::cloud {
-extern std::tuple<int, std::string_view>
convert_ms_code_to_http_code(MetaServiceCode ret);
-
RecyclerServiceImpl::RecyclerServiceImpl(std::shared_ptr<TxnKv> txn_kv,
Recycler* recycler,
Checker* checker,
std::shared_ptr<TxnLazyCommitter>
txn_lazy_committer)
@@ -503,208 +507,248 @@ void check_meta(const std::shared_ptr<TxnKv>& txn_kv,
const std::string& instanc
#endif
}
-void RecyclerServiceImpl::http(::google::protobuf::RpcController* controller,
- const ::doris::cloud::MetaServiceHttpRequest*
request,
- ::doris::cloud::MetaServiceHttpResponse*
response,
- ::google::protobuf::Closure* done) {
- auto cntl = static_cast<brpc::Controller*>(controller);
- LOG(INFO) << "rpc from " << cntl->remote_side() << " request: " <<
request->DebugString();
- brpc::ClosureGuard closure_guard(done);
+static HttpResponse process_recycle_instance(RecyclerServiceImpl* service,
brpc::Controller* cntl) {
+ std::string request_body = cntl->request_attachment().to_string();
+ RecycleInstanceRequest req;
+ auto st = google::protobuf::util::JsonStringToMessage(request_body, &req);
+ if (!st.ok()) {
+ std::string msg = "failed to RecycleInstanceRequest, error: " +
st.message().ToString();
+ LOG(WARNING) << msg;
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, msg);
+ }
+ RecycleInstanceResponse res;
+ service->recycle_instance(cntl, &req, &res, nullptr);
+ return http_text_reply(res.status(), res.status().msg());
+}
+
+static HttpResponse process_statistics_recycle(RecyclerServiceImpl* service,
+ brpc::Controller* cntl) {
+ std::string request_body = cntl->request_attachment().to_string();
+ StatisticsRecycleRequest req;
+ auto st = google::protobuf::util::JsonStringToMessage(request_body, &req);
+ if (!st.ok()) {
+ std::string msg = "failed to StatisticsRecycleRequest, error: " +
st.message().ToString();
+ LOG(WARNING) << msg;
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, msg);
+ }
MetaServiceCode code = MetaServiceCode::OK;
- int status_code = 200;
- std::string msg = "OK";
- std::string req;
- std::string response_body;
- std::string request_body;
- DORIS_CLOUD_DEFER {
- status_code = std::get<0>(convert_ms_code_to_http_code(code));
- LOG(INFO) << (code == MetaServiceCode::OK ? "succ to " : "failed to ")
<< "http"
- << " " << cntl->remote_side() << " request=\n"
- << req << "\n ret=" << code << " msg=" << msg;
- cntl->http_response().set_status_code(status_code);
- cntl->response_attachment().append(response_body);
- cntl->response_attachment().append("\n");
- };
+ std::string msg;
+ service->statistics_recycle(req, code, msg);
+ return http_text_reply(code, msg, msg);
+}
- // Prepare input request info
- auto unresolved_path = cntl->http_request().unresolved_path();
- auto uri = cntl->http_request().uri();
- std::stringstream ss;
- ss << "\nuri_path=" << uri.path();
- ss << "\nunresolved_path=" << unresolved_path;
- ss << "\nmethod=" << brpc::HttpMethod2Str(cntl->http_request().method());
- ss << "\nquery strings:";
- for (auto it = uri.QueryBegin(); it != uri.QueryEnd(); ++it) {
- ss << "\n" << it->first << "=" << it->second;
- }
- ss << "\nheaders:";
- for (auto it = cntl->http_request().HeaderBegin(); it !=
cntl->http_request().HeaderEnd();
- ++it) {
- ss << "\n" << it->first << ":" << it->second;
- }
- req = ss.str();
- ss.clear();
- request_body = cntl->request_attachment().to_string(); // Just copy
+static HttpResponse process_recycle_copy_jobs(RecyclerServiceImpl* service,
+ brpc::Controller* cntl) {
+ const auto* instance_id =
cntl->http_request().uri().GetQuery("instance_id");
+ if (instance_id == nullptr || instance_id->empty()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "no instance
id");
+ }
+ MetaServiceCode code = MetaServiceCode::OK;
+ std::string msg;
+ recycle_copy_jobs(service->txn_kv(), *instance_id, code, msg,
+ service->recycler()->thread_pool_group(),
service->txn_lazy_committer());
+ return http_text_reply(code, msg, msg);
+}
- // Auth
- auto token = uri.GetQuery("token");
- if (token == nullptr || *token != config::http_token) {
- msg = "incorrect token, token=" + (token == nullptr ?
std::string("(not given)") : *token);
- response_body = "incorrect token";
- status_code = 403;
- return;
+static HttpResponse process_recycle_job_info(RecyclerServiceImpl* service,
brpc::Controller* cntl) {
+ const auto* instance_id =
cntl->http_request().uri().GetQuery("instance_id");
+ if (instance_id == nullptr || instance_id->empty()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "no instance
id");
}
+ MetaServiceCode code = MetaServiceCode::OK;
+ std::string msg;
+ std::string key;
+ job_recycle_key({*instance_id}, &key);
+ recycle_job_info(service->txn_kv(), *instance_id, key, code, msg);
+ return http_text_reply(code, msg, msg);
+}
- if (unresolved_path == "recycle_instance") {
- RecycleInstanceRequest req;
- auto st = google::protobuf::util::JsonStringToMessage(request_body,
&req);
- if (!st.ok()) {
- msg = "failed to RecycleInstanceRequest, error: " +
st.message().ToString();
- response_body = msg;
- LOG(WARNING) << msg;
- return;
- }
- RecycleInstanceResponse res;
- recycle_instance(cntl, &req, &res, nullptr);
- code = res.status().code();
- msg = res.status().msg();
- response_body = msg;
- return;
+static HttpResponse process_check_instance(RecyclerServiceImpl* service,
brpc::Controller* cntl) {
+ const auto* instance_id =
cntl->http_request().uri().GetQuery("instance_id");
+ if (instance_id == nullptr || instance_id->empty()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "no instance
id");
+ }
+ if (!service->checker()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "checker not
enabled");
}
+ MetaServiceCode code = MetaServiceCode::OK;
+ std::string msg;
+ service->check_instance(*instance_id, code, msg);
+ return http_text_reply(code, msg, msg);
+}
- if (unresolved_path == "statistics_recycle") {
- StatisticsRecycleRequest req;
- auto st = google::protobuf::util::JsonStringToMessage(request_body,
&req);
- if (!st.ok()) {
- msg = "failed to StatisticsRecycleRequest, error: " +
st.message().ToString();
- response_body = msg;
- LOG(WARNING) << msg;
- return;
- }
- statistics_recycle(req, code, msg);
- response_body = msg;
- return;
+static HttpResponse process_check_job_info(RecyclerServiceImpl* service,
brpc::Controller* cntl) {
+ const auto* instance_id =
cntl->http_request().uri().GetQuery("instance_id");
+ if (instance_id == nullptr || instance_id->empty()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "no instance
id");
}
+ MetaServiceCode code = MetaServiceCode::OK;
+ std::string msg;
+ std::string key;
+ job_check_key({*instance_id}, &key);
+ recycle_job_info(service->txn_kv(), *instance_id, key, code, msg);
+ return http_text_reply(code, msg, msg);
+}
- if (unresolved_path == "recycle_copy_jobs") {
- auto instance_id = uri.GetQuery("instance_id");
- if (instance_id == nullptr || instance_id->empty()) {
- msg = "no instance id";
- response_body = msg;
- status_code = 400;
- return;
- }
- recycle_copy_jobs(txn_kv_, *instance_id, code, msg,
recycler_->_thread_pool_group,
- txn_lazy_committer_);
+static HttpResponse process_check_meta(RecyclerServiceImpl* service,
brpc::Controller* cntl) {
+ const auto& uri = cntl->http_request().uri();
+ const auto* instance_id = uri.GetQuery("instance_id");
+ const auto* host = uri.GetQuery("host");
+ const auto* port = uri.GetQuery("port");
+ const auto* user = uri.GetQuery("user");
+ const auto* password = uri.GetQuery("password");
+ if (instance_id == nullptr || instance_id->empty() || host == nullptr ||
host->empty() ||
+ port == nullptr || port->empty() || password == nullptr || user ==
nullptr ||
+ user->empty()) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT,
+ "no instance id or mysql conn str info");
+ }
+ LOG(INFO) << " host " << *host << " port " << *port << " user " << *user
<< " instance "
+ << *instance_id;
+ std::string msg;
+ check_meta(service->txn_kv(), *instance_id, *host, *port, *user,
*password, msg);
+ return http_text_reply(MetaServiceCode::OK, msg, msg);
+}
- response_body = msg;
- return;
+static HttpResponse process_adjust_rate_limiter(RecyclerServiceImpl*,
brpc::Controller* cntl) {
+ const auto& uri = cntl->http_request().uri();
+ const auto* type_string = uri.GetQuery("type");
+ const auto* speed = uri.GetQuery("speed");
+ const auto* burst = uri.GetQuery("burst");
+ const auto* limit = uri.GetQuery("limit");
+ if (type_string == nullptr || type_string->empty() || speed == nullptr ||
burst == nullptr ||
+ limit == nullptr || (*type_string != "get" && *type_string != "put")) {
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, "argument
not suitable");
+ }
+ auto max_speed = speed->empty() ? 0 : std::stoul(*speed);
+ auto max_burst = burst->empty() ? 0 : std::stoul(*burst);
+ auto max_limit = limit->empty() ? 0 : std::stoul(*limit);
+ if (0 != reset_s3_rate_limiter(string_to_s3_rate_limit_type(*type_string),
max_speed, max_burst,
+ max_limit)) {
+ return http_json_reply(MetaServiceCode::UNDEFINED_ERR, "adjust
failed");
}
+ return http_json_reply(MetaServiceCode::OK, "");
+}
- if (unresolved_path == "recycle_job_info") {
- auto instance_id = uri.GetQuery("instance_id");
- if (instance_id == nullptr || instance_id->empty()) {
- msg = "no instance id";
- response_body = msg;
- status_code = 400;
- return;
- }
- std::string key;
- job_recycle_key({*instance_id}, &key);
- recycle_job_info(txn_kv_, *instance_id, key, code, msg);
- response_body = msg;
- return;
+static HttpResponse process_show_config(RecyclerServiceImpl*,
brpc::Controller* cntl) {
+ const auto* conf_key_ptr = cntl->http_request().uri().GetQuery("conf_key");
+ std::string conf_name = conf_key_ptr ? *conf_key_ptr : "";
+
+ if (config::full_conf_map == nullptr) {
+ return http_json_reply(MetaServiceCode::UNDEFINED_ERR, "config map not
initialized");
}
- if (unresolved_path == "check_instance") {
- auto instance_id = uri.GetQuery("instance_id");
- if (instance_id == nullptr || instance_id->empty()) {
- msg = "no instance id";
- response_body = msg;
- status_code = 400;
- return;
- }
- if (!checker_) {
- msg = "checker not enabled";
- response_body = msg;
- status_code = 400;
- return;
+ rapidjson::Document d;
+ d.SetArray();
+ for (auto& [name, field] : *config::Register::_s_field_map) {
+ if (!conf_name.empty() && name != conf_name) {
+ continue;
}
- check_instance(*instance_id, code, msg);
- response_body = msg;
- return;
+ auto it = config::full_conf_map->find(name);
+ std::string value = (it != config::full_conf_map->end()) ? it->second
: "";
+
+ rapidjson::Value entry(rapidjson::kArrayType);
+ entry.PushBack(rapidjson::Value(name.c_str(), d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(field.type, d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(value.c_str(), d.GetAllocator()),
d.GetAllocator());
+ entry.PushBack(rapidjson::Value(field.valmutable), d.GetAllocator());
+ d.PushBack(entry, d.GetAllocator());
}
+ rapidjson::StringBuffer sb;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
+ d.Accept(writer);
+ return http_json_reply(MetaServiceCode::OK, "", sb.GetString());
+}
- if (unresolved_path == "check_job_info") {
- auto instance_id = uri.GetQuery("instance_id");
- if (instance_id == nullptr || instance_id->empty()) {
- msg = "no instance id";
- response_body = msg;
- status_code = 400;
- return;
+static HttpResponse process_update_config(RecyclerServiceImpl*,
brpc::Controller* cntl) {
+ const auto& uri = cntl->http_request().uri();
+ bool persist = (uri.GetQuery("persist") != nullptr &&
*uri.GetQuery("persist") == "true");
+ const auto* configs_ptr = uri.GetQuery("configs");
+ const auto* reason_ptr = uri.GetQuery("reason");
+ std::string configs = configs_ptr ? *configs_ptr : "";
+ std::string reason = reason_ptr ? *reason_ptr : "";
+ LOG(INFO) << "modify configs for reason=" << reason << ", configs=" <<
configs
+ << ", persist=" << persist;
+ if (configs.empty()) {
+ LOG(WARNING) << "query param `configs` should not be empty";
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT,
+ "query param `configs` should not be empty");
+ }
+ std::unordered_map<std::string, std::string> conf_map;
+ auto conf_list = split(configs, ',');
+ for (const auto& conf : conf_list) {
+ auto conf_pair = split(conf, '=');
+ if (conf_pair.size() != 2) {
+ LOG(WARNING) << "failed to split config=[" << conf << "] from
`k=v` pattern";
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT,
+ fmt::format("config {} is invalid", conf));
}
- std::string key;
- job_check_key({*instance_id}, &key);
- recycle_job_info(txn_kv_, *instance_id, key, code, msg);
- response_body = msg;
- return;
+ trim(conf_pair[0]);
+ trim(conf_pair[1]);
+ conf_map.emplace(std::move(conf_pair[0]), std::move(conf_pair[1]));
+ }
+ if (auto [succ, cause] =
+ config::set_config(std::move(conf_map), persist,
config::custom_conf_path);
+ !succ) {
+ LOG(WARNING) << cause;
+ return http_json_reply(MetaServiceCode::INVALID_ARGUMENT, cause);
}
+ return http_json_reply(MetaServiceCode::OK, "");
+}
- if (unresolved_path == "check_meta") {
- auto instance_id = uri.GetQuery("instance_id");
- auto host = uri.GetQuery("host");
- auto port = uri.GetQuery("port");
- auto user = uri.GetQuery("user");
- auto password = uri.GetQuery("password");
- if (instance_id == nullptr || instance_id->empty() || host == nullptr
|| host->empty() ||
- port == nullptr || port->empty() || password == nullptr || user ==
nullptr ||
- user->empty()) {
- msg = "no instance id or mysql conn str info";
- response_body = msg;
- status_code = 400;
- return;
- }
- LOG(INFO) << " host " << *host;
- LOG(INFO) << " port " << *port;
- LOG(INFO) << " user " << *user;
- LOG(INFO) << " instance " << *instance_id;
- check_meta(txn_kv_, *instance_id, *host, *port, *user, *password, msg);
- status_code = 200;
- response_body = msg;
+void RecyclerServiceImpl::http(::google::protobuf::RpcController* controller,
+ const ::doris::cloud::MetaServiceHttpRequest*,
+ ::doris::cloud::MetaServiceHttpResponse*,
+ ::google::protobuf::Closure* done) {
+ using HttpHandler = HttpResponse (*)(RecyclerServiceImpl*,
brpc::Controller*);
+ static const std::unordered_map<std::string_view, HttpHandler>
http_handlers {
+ {"recycle_instance", process_recycle_instance},
+ {"statistics_recycle", process_statistics_recycle},
+ {"recycle_copy_jobs", process_recycle_copy_jobs},
+ {"recycle_job_info", process_recycle_job_info},
+ {"check_instance", process_check_instance},
+ {"check_job_info", process_check_job_info},
+ {"check_meta", process_check_meta},
+ {"adjust_rate_limiter", process_adjust_rate_limiter},
+ {"show_config", process_show_config},
+ {"update_config", process_update_config},
+ };
+
+ auto* cntl = static_cast<brpc::Controller*>(controller);
+ LOG(INFO) << "rpc from " << cntl->remote_side()
+ << " request: " << cntl->http_request().uri().path();
+ brpc::ClosureGuard closure_guard(done);
+
+ // Auth
+ const auto* token = cntl->http_request().uri().GetQuery("token");
+ if (token == nullptr || *token != config::http_token) {
+ std::string msg = "incorrect token, token=" +
+ (token == nullptr ? std::string("(not given)") :
*token);
+ cntl->http_response().set_status_code(403);
+ cntl->response_attachment().append(msg);
+ cntl->response_attachment().append("\n");
+ LOG(WARNING) << "failed to handle http from " << cntl->remote_side()
<< " msg: " << msg;
return;
}
- if (unresolved_path == "adjust_rate_limiter") {
- auto type_string = uri.GetQuery("type");
- auto speed = uri.GetQuery("speed");
- auto burst = uri.GetQuery("burst");
- auto limit = uri.GetQuery("limit");
- if (type_string->empty() || speed->empty() || burst->empty() ||
limit->empty() ||
- (*type_string != "get" && *type_string != "put")) {
- msg = "argument not suitable";
- response_body = msg;
- status_code = 400;
- return;
- }
- auto max_speed = speed->empty() ? 0 : std::stoul(*speed);
- auto max_burst = burst->empty() ? 0 : std::stoul(*burst);
- auto max_limit = burst->empty() ? 0 : std::stoul(*limit);
- if (0 !=
reset_s3_rate_limiter(string_to_s3_rate_limit_type(*type_string), max_speed,
- max_burst, max_limit)) {
- msg = "adjust failed";
- response_body = msg;
- status_code = 400;
- return;
- }
-
- status_code = 200;
- response_body = msg;
+ const auto& unresolved_path = cntl->http_request().unresolved_path();
+ auto it = http_handlers.find(unresolved_path);
+ if (it == http_handlers.end()) {
+ std::string msg = "http path " + cntl->http_request().uri().path() +
+ " not found, it may be not implemented";
+ cntl->http_response().set_status_code(404);
+ cntl->response_attachment().append(msg);
+ cntl->response_attachment().append("\n");
return;
}
- status_code = 404;
- msg = "http path " + uri.path() + " not found, it may be not implemented";
- response_body = msg;
+ auto [status_code, msg, body] = it->second(this, cntl);
+ cntl->http_response().set_status_code(status_code);
+ cntl->response_attachment().append(body);
+ cntl->response_attachment().append("\n");
+
+ LOG(INFO) << (status_code == 200 ? "succ to " : "failed to ") <<
__PRETTY_FUNCTION__ << " "
+ << cntl->remote_side() << " ret=" << status_code << " msg=" <<
msg;
}
} // namespace doris::cloud
diff --git a/cloud/src/recycler/recycler_service.h
b/cloud/src/recycler/recycler_service.h
index 6890d7049bd..a9a9f739f1e 100644
--- a/cloud/src/recycler/recycler_service.h
+++ b/cloud/src/recycler/recycler_service.h
@@ -44,11 +44,15 @@ public:
::doris::cloud::MetaServiceHttpResponse* response,
::google::protobuf::Closure* done) override;
-private:
void statistics_recycle(StatisticsRecycleRequest& req, MetaServiceCode&
code, std::string& msg);
void check_instance(const std::string& instance_id, MetaServiceCode& code,
std::string& msg);
+ std::shared_ptr<TxnKv> txn_kv() { return txn_kv_; }
+ Recycler* recycler() { return recycler_; }
+ Checker* checker() { return checker_; }
+ std::shared_ptr<TxnLazyCommitter> txn_lazy_committer() { return
txn_lazy_committer_; }
+
private:
std::shared_ptr<TxnKv> txn_kv_;
Recycler* recycler_; // Ref
diff --git
a/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy
b/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy
index f9f1a3b9183..2cc8ffaa4a2 100644
--- a/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy
+++ b/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy
@@ -108,3 +108,4 @@ enableTrinoConnectorTest = false
s3Source = "aliyun"
s3Endpoint = "oss-cn-hongkong-internal.aliyuncs.com"
+recycleServiceHttpAddress = "127.0.0.1:6000"
\ No newline at end of file
diff --git
a/regression-test/suites/cloud_p0/test_update_and_show_cloud_config.groovy
b/regression-test/suites/cloud_p0/test_update_and_show_cloud_config.groovy
new file mode 100644
index 00000000000..bcbc450e8ab
--- /dev/null
+++ b/regression-test/suites/cloud_p0/test_update_and_show_cloud_config.groovy
@@ -0,0 +1,111 @@
+// 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.
+
+import org.apache.doris.regression.suite.ClusterOptions
+import groovy.json.JsonSlurper
+
+suite('test_update_and_show_cloud_config') {
+ if (!isCloudMode()) {
+ return
+ }
+
+ def token = context.config.metaServiceToken ?: "greedisgood9999"
+ def configKey = "recycle_interval_seconds"
+ def newValue = "999"
+
+ // Helper: call show_config on a service and return the value for
configKey, or null if not found.
+ def showConfig = { serviceAddr, servicePath ->
+ def result = null
+ httpTest {
+ endpoint serviceAddr
+ uri
"${servicePath}/show_config?token=${token}&conf_key=${configKey}"
+ op "get"
+ check { respCode, body ->
+ assertEquals(200, respCode)
+ // Response is {"code":"OK","msg":"","result":[[name, type,
value, is_mutable], ...]}
+ def parsed = new JsonSlurper().parseText(body.trim())
+ assertEquals("OK", parsed.code, "show_config should return
code=OK, got: ${body}")
+ assertTrue(parsed.result instanceof List, "show_config result
should be a JSON array")
+ def entry = parsed.result.find { it[0] == configKey }
+ assertNotNull(entry, "Config key '${configKey}' not found in
show_config response")
+ result = entry[2] // value is the 3rd element
+ }
+ }
+ return result
+ }
+
+ // Helper: call update_config on a service and assert success.
+ def updateConfig = { serviceAddr, servicePath, value ->
+ httpTest {
+ endpoint serviceAddr
+ uri
"${servicePath}/update_config?token=${token}&configs=${configKey}=${value}"
+ op "get"
+ check { respCode, body ->
+ logger.info("update_config response: respCode=${respCode},
body=${body}")
+ assertEquals(200, respCode)
+ def parsed = new JsonSlurper().parseText(body.trim())
+ assertEquals("OK", parsed.code, "update_config should return
code=OK, got: ${body}")
+ }
+ }
+ }
+
+ // ── Meta Service
──────────────────────────────────────────────────────────
+ def msAddr = context.config.metaServiceHttpAddress
+ def msPath = "/MetaService/http"
+
+ // 1. Read the original value so we can restore it afterwards.
+ def originalMsValue = showConfig(msAddr, msPath)
+ logger.info("meta-service original ${configKey}=${originalMsValue}")
+
+ try {
+ // 2. Update to newValue.
+ updateConfig(msAddr, msPath, newValue)
+
+ // 3. Verify show_config reflects the new value.
+ def updatedMsValue = showConfig(msAddr, msPath)
+ logger.info("meta-service updated ${configKey}=${updatedMsValue}")
+ assertEquals(newValue, updatedMsValue,
+ "meta-service: show_config should return updated value
${newValue}, got ${updatedMsValue}")
+ } finally {
+ // 4. Restore original value.
+ if (originalMsValue != null) {
+ updateConfig(msAddr, msPath, originalMsValue)
+ logger.info("meta-service restored
${configKey}=${originalMsValue}")
+ }
+ }
+
+ // ── Recycler Service
──────────────────────────────────────────────────────
+ def recyclerAddr = context.config.recycleServiceHttpAddress
+ def recyclerPath = "/RecyclerService/http"
+
+ def originalRecyclerValue = showConfig(recyclerAddr, recyclerPath)
+ logger.info("recycler original ${configKey}=${originalRecyclerValue}")
+
+ try {
+ updateConfig(recyclerAddr, recyclerPath, newValue)
+
+ def updatedRecyclerValue = showConfig(recyclerAddr, recyclerPath)
+ logger.info("recycler updated ${configKey}=${updatedRecyclerValue}")
+ assertEquals(newValue, updatedRecyclerValue,
+ "recycler: show_config should return updated value
${newValue}, got ${updatedRecyclerValue}")
+ } finally {
+ if (originalRecyclerValue != null) {
+ updateConfig(recyclerAddr, recyclerPath, originalRecyclerValue)
+ logger.info("recycler restored
${configKey}=${originalRecyclerValue}")
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]