diff --git a/cloud/src/common/bvars.cpp b/cloud/src/common/bvars.cpp index 4485fb9f5d6022..757507b72053e1 100644 --- a/cloud/src/common/bvars.cpp +++ b/cloud/src/common/bvars.cpp @@ -383,4 +383,7 @@ mBvarInt64Adder g_bvar_rpc_kv_clean_txn_label_del_counter("rpc_kv_clean_txn_labe // get_txn_id mBvarInt64Adder g_bvar_rpc_kv_get_txn_id_get_counter("rpc_kv_get_txn_id_get_counter",{"instance_id"}); +// meta ranges +mBvarStatus g_bvar_fdb_kv_ranges_count("fdb_kv_ranges_count", {"category","instance_id", "sub_category"}); + // clang-format on diff --git a/cloud/src/common/bvars.h b/cloud/src/common/bvars.h index 83ab481764f926..cae7a7133d7934 100644 --- a/cloud/src/common/bvars.h +++ b/cloud/src/common/bvars.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -139,8 +140,7 @@ class mBvarWrapper { void put(const std::initializer_list& dim_values, ValType value) { BvarType* stats = counter_.get_stats(std::list(dim_values)); if (stats) { - if constexpr (std::is_same_v> || - std::is_same_v>) { + if constexpr (is_bvar_status::value) { stats->set_value(value); } else { *stats << value; @@ -170,6 +170,10 @@ class mBvarWrapper { struct is_valid_bvar_type> : std::true_type {}; template <> struct is_valid_bvar_type : std::true_type {}; + template + struct is_bvar_status : std::false_type {}; + template + struct is_bvar_status> : std::true_type {}; bvar::MultiDimension counter_; }; @@ -464,3 +468,6 @@ extern mBvarInt64Adder g_bvar_rpc_kv_clean_txn_label_get_counter; extern mBvarInt64Adder g_bvar_rpc_kv_clean_txn_label_put_counter; extern mBvarInt64Adder g_bvar_rpc_kv_clean_txn_label_del_counter; extern mBvarInt64Adder g_bvar_rpc_kv_get_txn_id_get_counter; + +// meta ranges +extern mBvarStatus g_bvar_fdb_kv_ranges_count; diff --git a/cloud/src/common/metric.cpp b/cloud/src/common/metric.cpp index 124a5f26a063af..ac0193a9921ec3 100644 --- a/cloud/src/common/metric.cpp +++ b/cloud/src/common/metric.cpp @@ -25,16 +25,20 @@ #include #include #include +#include #include #include +#include #include #include "common/bvars.h" #include "common/logging.h" +#include "meta-store/keys.h" #include "meta-store/txn_kv.h" #include "meta-store/txn_kv_error.h" namespace doris::cloud { +extern std::set get_key_prefix_contants(); // The format of the output is shown in "test/fdb_metric_example.json" static const std::string FDB_STATUS_KEY = "\xff\xff/status/json"; @@ -298,10 +302,95 @@ static void export_fdb_status_details(const std::string& status_str) { get_process_metric("memory"); } +// boundaries include the key category{meta, txn, recycle...}, instance_id and sub_category{rowset, txn_label...} +// encode look like +// 0x01 "txn" ${instance_id} "txn_label" ${db_id} ${label} +// 0x01 "meta" ${instance_id} "rowset" ${tablet_id} ${version} +// the func count same key to hashmap kv_range_count +// exmaple: +// kv_range_boundaries: meta|instance1|rowset|..., meta|instance1|rowset|..., meta|instance2|rowset|..., txn|instance1|txn_label|... +// kv_range_count output: , , +void get_kv_range_boundaries_count(std::vector& kv_range_boundaries, + std::unordered_map& kv_range_count) { + size_t prefix_size = FdbTxnKv::fdb_partition_key_prefix().size(); + for (auto&& boundary : kv_range_boundaries) { + if (boundary.size() < prefix_size + 1 || boundary[prefix_size] != CLOUD_USER_KEY_SPACE01) { + continue; + } + + std::string_view user_key(boundary); + user_key.remove_prefix(prefix_size + 1); // Skip the KEY_SPACE prefix. + std::vector, int, int>> out; + decode_key(&user_key, &out); // ignore any error, since the boundary key might be truncated. + + auto visitor = [](auto&& arg) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return arg; + } else { + return std::to_string(arg); + } + }; + + if (!out.empty()) { + std::string key; + // whatever the boundary's category have similar encode part: + // category, instance_id, sub_category + // we can distinguish boundary using the three parts + // some boundaries do not contain all three parts, so restrictions based on size are also necessary + for (size_t i = 0; i < 3 && i < out.size(); ++i) { + key += std::visit(visitor, std::get<0>(out[i])) + '|'; + } + key.pop_back(); + kv_range_count[key]++; + } + } +} + +static void export_fdb_kv_ranges_details(TxnKv* kv) { + auto* txn_kv = dynamic_cast(kv); + if (!txn_kv) { + LOG(WARNING) << "this method only support fdb txn kv"; + return; + } + + std::vector partition_boundaries; + TxnErrorCode code = txn_kv->get_partition_boundaries(&partition_boundaries); + if (code != TxnErrorCode::TXN_OK) { + auto msg = fmt::format("failed to get boundaries, code={}", code); + return; + } + + std::unordered_map partition_count; + get_kv_range_boundaries_count(partition_boundaries, partition_count); + + auto key_prefix_set = get_key_prefix_contants(); + std::unordered_map category_count; + for (auto&& [key, count] : partition_count) { + std::vector keys; + size_t pos {}; + // split key with '|' + do { + size_t p = std::min(key.size(), key.find('|', pos)); + keys.emplace_back(key.substr(pos, p - pos)); + pos = p + 1; + } while (pos < key.size()); + keys.resize(3); + if (key_prefix_set.contains(keys[0])) { + category_count[keys[0]] += count; + g_bvar_fdb_kv_ranges_count.put({keys[0], keys[1], keys[2]}, count); + } else { + LOG(WARNING) << fmt::format("Unknow meta range type: {}", keys[0]); + continue; + } + } +} + void FdbMetricExporter::export_fdb_metrics(TxnKv* txn_kv) { int64_t busyness = 0; std::string fdb_status = get_fdb_status(txn_kv); export_fdb_status_details(fdb_status); + export_fdb_kv_ranges_details(txn_kv); if (auto* kv = dynamic_cast(txn_kv); kv != nullptr) { busyness = static_cast(kv->get_client_thread_busyness() * 100); g_bvar_fdb_client_thread_busyness_percent.set_value(busyness); diff --git a/cloud/src/common/metric.h b/cloud/src/common/metric.h index ad918482daa460..72f508c84e7e74 100644 --- a/cloud/src/common/metric.h +++ b/cloud/src/common/metric.h @@ -28,6 +28,8 @@ #include "meta-store/txn_kv.h" namespace doris::cloud { +extern void get_kv_range_boundaries_count(std::vector& partition_boundaries, + std::unordered_map& partition_count); class FdbMetricExporter { public: diff --git a/cloud/src/meta-service/meta_service_http.cpp b/cloud/src/meta-service/meta_service_http.cpp index 21db2ec0357b64..7a78b97eaa6f45 100644 --- a/cloud/src/meta-service/meta_service_http.cpp +++ b/cloud/src/meta-service/meta_service_http.cpp @@ -75,6 +75,9 @@ extern int decrypt_instance_info(InstanceInfoPB& instance, const std::string& in MetaServiceCode& code, std::string& msg, std::shared_ptr& txn); +extern void get_kv_range_boundaries_count(std::vector& partition_boundaries, + std::unordered_map& partition_count); + template static google::protobuf::util::Status parse_json_message(const std::string& unresolved_path, const std::string& body, Message* req) { @@ -532,38 +535,8 @@ static HttpResponse process_show_meta_ranges(MetaServiceImpl* service, brpc::Con auto msg = fmt::format("failed to get boundaries, code={}", code); return http_json_reply(MetaServiceCode::UNDEFINED_ERR, msg); } - std::unordered_map partition_count; - size_t prefix_size = FdbTxnKv::fdb_partition_key_prefix().size(); - for (auto&& boundary : partition_boundaries) { - if (boundary.size() < prefix_size + 1 || boundary[prefix_size] != CLOUD_USER_KEY_SPACE01) { - continue; - } - - std::string_view user_key(boundary); - user_key.remove_prefix(prefix_size + 1); // Skip the KEY_SPACE prefix. - std::vector, int, int>> out; - decode_key(&user_key, &out); // ignore any error, since the boundary key might be truncated. - - auto visitor = [](auto&& arg) -> std::string { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return arg; - } else { - return std::to_string(arg); - } - }; - - if (!out.empty()) { - std::string key; - for (size_t i = 0; i < 3 && i < out.size(); ++i) { - key += std::visit(visitor, std::get<0>(out[i])); - key += '|'; - } - key.pop_back(); // omit the last '|' - partition_count[key]++; - } - } + get_kv_range_boundaries_count(partition_boundaries, partition_count); // sort ranges by count std::vector> meta_ranges; @@ -575,7 +548,7 @@ static HttpResponse process_show_meta_ranges(MetaServiceImpl* service, brpc::Con std::sort(meta_ranges.begin(), meta_ranges.end(), [](const auto& lhs, const auto& rhs) { return lhs.second > rhs.second; }); - std::string body = fmt::format("total partitions: {}\n", partition_boundaries.size()); + std::string body = fmt::format("total meta ranges: {}\n", partition_boundaries.size()); for (auto&& [key, count] : meta_ranges) { body += fmt::format("{}: {}\n", key, count); } diff --git a/cloud/src/meta-store/keys.cpp b/cloud/src/meta-store/keys.cpp index dff688c22d75d5..83ba12deba7614 100644 --- a/cloud/src/meta-store/keys.cpp +++ b/cloud/src/meta-store/keys.cpp @@ -17,6 +17,8 @@ #include "meta-store/keys.h" +#include + #include "meta-store/codec.h" namespace doris::cloud { @@ -526,5 +528,20 @@ int decode_key(std::string_view* in, } return 0; } - +//================================================================================== +// Key Prefix Map +//================================================================================== +std::set get_key_prefix_contants() { + std::set key_prefix_set; + key_prefix_set.insert(INSTANCE_KEY_PREFIX); + key_prefix_set.insert(TXN_KEY_PREFIX); + key_prefix_set.insert(VERSION_KEY_PREFIX); + key_prefix_set.insert(META_KEY_PREFIX); + key_prefix_set.insert(RECYCLE_KEY_PREFIX); + key_prefix_set.insert(STATS_KEY_PREFIX); + key_prefix_set.insert(JOB_KEY_PREFIX); + key_prefix_set.insert(COPY_KEY_PREFIX); + key_prefix_set.insert(VAULT_KEY_PREFIX); + return key_prefix_set; +} } // namespace doris::cloud