Coverage Report

Created: 2026-04-15 19:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/service/http/action/show_hotspot_action.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "service/http/action/show_hotspot_action.h"
19
20
#include <queue>
21
#include <string>
22
23
#include "cloud/cloud_tablet_mgr.h"
24
#include "service/http/http_channel.h"
25
#include "service/http/http_request.h"
26
27
namespace doris {
28
29
namespace {
30
31
enum class Metrics {
32
    READ_BLOCK = 0,
33
    WRITE = 1,
34
    COMPACTION = 2,
35
    NUM_ROWSETS = 3,
36
    NUM_BASE_ROWSETS = 4,
37
    NUM_CUMU_ROWSETS = 5,
38
    UNKNOWN = 100000,
39
};
40
41
0
Status check_param(HttpRequest* req, size_t& top_n, Metrics& metrics) {
42
0
    const std::string TOPN_PARAM = "topn";
43
44
0
    auto& topn_str = req->param(TOPN_PARAM);
45
0
    if (!topn_str.empty()) {
46
0
        try {
47
0
            top_n = std::stoul(topn_str);
48
0
        } catch (const std::exception& e) {
49
0
            return Status::InternalError("convert topn failed, {}", e.what());
50
0
        }
51
0
    }
52
53
0
    const std::string METRICS_PARAM = "metrics";
54
0
    auto& metrics_str = req->param(METRICS_PARAM);
55
0
    if (metrics_str.empty()) {
56
0
        return Status::InternalError("metrics must be specified");
57
0
    }
58
59
0
    if (metrics_str == "read_block") {
60
0
        metrics = Metrics::READ_BLOCK;
61
0
    } else if (metrics_str == "write") {
62
0
        metrics = Metrics::WRITE;
63
0
    } else if (metrics_str == "compaction") {
64
0
        metrics = Metrics::COMPACTION;
65
0
    } else if (metrics_str == "num_rowsets") {
66
0
        metrics = Metrics::NUM_ROWSETS;
67
0
    } else if (metrics_str == "num_cumu_rowsets") {
68
0
        metrics = Metrics::NUM_CUMU_ROWSETS;
69
0
    } else if (metrics_str == "num_base_rowsets") {
70
0
        metrics = Metrics::NUM_BASE_ROWSETS;
71
0
    } else {
72
0
        return Status::InternalError("unknown metrics: {}", metrics_str);
73
0
    }
74
75
0
    return Status::OK();
76
0
}
77
78
struct TabletCounter {
79
    int64_t tablet_id {0};
80
    int64_t count {0};
81
};
82
83
struct Comparator {
84
0
    constexpr bool operator()(const TabletCounter& lhs, const TabletCounter& rhs) const {
85
0
        return lhs.count > rhs.count;
86
0
    }
87
};
88
89
using MinHeap = std::priority_queue<TabletCounter, std::vector<TabletCounter>, Comparator>;
90
91
} // namespace
92
93
0
void ShowHotspotAction::handle(HttpRequest* req) {
94
0
    size_t topn = 0;
95
0
    Metrics metrics {Metrics::UNKNOWN};
96
0
    auto st = check_param(req, topn, metrics);
97
0
    if (!st.ok()) [[unlikely]] {
98
0
        HttpChannel::send_reply(req, HttpStatus::BAD_REQUEST, st.to_string());
99
0
        return;
100
0
    }
101
102
0
    std::function<int64_t(CloudTablet&)> count_fn;
103
0
    switch (metrics) {
104
0
    case Metrics::READ_BLOCK:
105
0
        count_fn = [](auto&& t) { return t.read_block_count.load(std::memory_order_relaxed); };
106
0
        break;
107
0
    case Metrics::WRITE:
108
0
        count_fn = [](auto&& t) { return t.write_count.load(std::memory_order_relaxed); };
109
0
        break;
110
0
    case Metrics::COMPACTION:
111
0
        count_fn = [](auto&& t) { return t.compaction_count.load(std::memory_order_relaxed); };
112
0
        break;
113
0
    case Metrics::NUM_ROWSETS:
114
0
        count_fn = [](auto&& t) { return t.fetch_add_approximate_num_rowsets(0); };
115
0
        break;
116
0
    case Metrics::NUM_BASE_ROWSETS:
117
0
        count_fn = [](auto&& t) {
118
0
            return t.fetch_add_approximate_num_rowsets(0) -
119
0
                   t.fetch_add_approximate_cumu_num_rowsets(0);
120
0
        };
121
0
        break;
122
0
    case Metrics::NUM_CUMU_ROWSETS:
123
0
        count_fn = [](auto&& t) { return t.fetch_add_approximate_cumu_num_rowsets(0); };
124
0
        break;
125
0
    default:
126
0
        break;
127
0
    }
128
129
0
    if (!count_fn) {
130
0
        HttpChannel::send_reply(req, HttpStatus::BAD_REQUEST, "metrics not specified");
131
0
        return;
132
0
    }
133
134
0
    auto tablets = _storage_engine.tablet_mgr().get_weak_tablets();
135
0
    std::vector<TabletCounter> buffer;
136
0
    buffer.reserve(tablets.size());
137
0
    for (auto&& t : tablets) {
138
0
        if (auto tablet = t.lock(); tablet) {
139
0
            buffer.push_back({tablet->tablet_id(), count_fn(*tablet)});
140
0
        }
141
0
    }
142
143
0
    if (topn <= 0) {
144
0
        topn = tablets.size();
145
0
    }
146
147
0
    MinHeap min_heap;
148
0
    for (auto&& counter : buffer) {
149
0
        min_heap.push(counter);
150
0
        if (min_heap.size() > topn) {
151
0
            min_heap.pop();
152
0
        }
153
0
    }
154
155
0
    buffer.resize(0);
156
0
    while (!min_heap.empty()) {
157
0
        buffer.push_back(min_heap.top());
158
0
        min_heap.pop();
159
0
    }
160
161
0
    std::string res;
162
0
    res.reserve(buffer.size() * 20);
163
    // Descending order
164
0
    std::for_each(buffer.rbegin(), buffer.rend(), [&res](auto&& counter) {
165
0
        res += fmt::format("{} {}\n", counter.tablet_id, counter.count);
166
0
    });
167
168
0
    HttpChannel::send_reply(req, HttpStatus::OK, res);
169
0
}
170
171
} // namespace doris