Coverage Report

Created: 2026-07-22 08:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_backend_service.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 "cloud/cloud_backend_service.h"
19
20
#include <brpc/controller.h>
21
#include <gen_cpp/BackendService_types.h>
22
23
#include "cloud/cloud_storage_engine.h"
24
#include "cloud/cloud_tablet.h"
25
#include "cloud/cloud_tablet_hotspot.h"
26
#include "cloud/cloud_tablet_mgr.h"
27
#include "cloud/cloud_warm_up_manager.h"
28
#include "common/config.h"
29
#include "common/logging.h"
30
#include "common/status.h"
31
#include "io/cache/block_file_cache_downloader.h"
32
#include "io/cache/block_file_cache_factory.h"
33
#include "load/stream_load/stream_load_context.h"
34
#include "load/stream_load/stream_load_recorder.h"
35
#include "util/brpc_client_cache.h" // BrpcClientCache
36
#include "util/stack_util.h"
37
#include "util/thrift_server.h"
38
39
namespace doris {
40
41
bvar::Adder<uint64_t> g_file_cache_warm_up_cache_async_submitted_segment_num(
42
        "file_cache_warm_up_cache_async_submitted_segment_num");
43
bvar::Adder<uint64_t> g_file_cache_warm_up_cache_async_submitted_task_num(
44
        "file_cache_warm_up_cache_async_submitted_task_num");
45
bvar::Adder<uint64_t> g_file_cache_warm_up_cache_async_submitted_tablet_num(
46
        "file_cache_warm_up_cache_async_submitted_tablet_num");
47
48
CloudBackendService::CloudBackendService(CloudStorageEngine& engine, ExecEnv* exec_env)
49
1
        : BaseBackendService(exec_env), _engine(engine) {}
50
51
0
CloudBackendService::~CloudBackendService() = default;
52
53
1
Status CloudBackendService::start_thrift_dependencies() {
54
1
    _agent_server->cloud_start_workers(_engine, _exec_env);
55
1
    return Status::OK();
56
1
}
57
58
void CloudBackendService::sync_load_for_tablets(TSyncLoadForTabletsResponse&,
59
0
                                                const TSyncLoadForTabletsRequest& request) {
60
0
    auto f = [this, tablet_ids = request.tablet_ids]() {
61
0
        std::for_each(tablet_ids.cbegin(), tablet_ids.cend(), [this](int64_t tablet_id) {
62
0
            CloudTabletSPtr tablet;
63
0
            auto result = _engine.tablet_mgr().get_tablet(tablet_id, true);
64
0
            if (!result.has_value()) {
65
0
                return;
66
0
            }
67
0
            SyncOptions options;
68
0
            options.warmup_delta_data = true;
69
0
            Status st = result.value()->sync_rowsets(options);
70
0
            if (!st.ok()) {
71
0
                LOG_WARNING("failed to sync load for tablet").error(st);
72
0
            }
73
0
        });
74
0
    };
75
0
    static_cast<void>(_engine.sync_load_for_tablets_thread_pool().submit_func(std::move(f)));
76
0
}
77
78
void CloudBackendService::get_top_n_hot_partitions(TGetTopNHotPartitionsResponse& response,
79
1
                                                   const TGetTopNHotPartitionsRequest& request) {
80
1
    _engine.tablet_hotspot().get_top_n_hot_partition(&response.hot_tables);
81
1
    response.file_cache_size = io::FileCacheFactory::instance()->get_capacity();
82
1
    response.__isset.hot_tables = !response.hot_tables.empty();
83
1
}
84
85
void CloudBackendService::warm_up_tablets(TWarmUpTabletsResponse& response,
86
0
                                          const TWarmUpTabletsRequest& request) {
87
0
    Status st;
88
0
    auto& manager = _engine.cloud_warm_up_manager();
89
0
    switch (request.type) {
90
0
    case TWarmUpTabletsRequestType::SET_JOB: {
91
0
        LOG_INFO("receive the warm up request.")
92
0
                .tag("request_type", "SET_JOB")
93
0
                .tag("job_id", request.job_id);
94
0
        if (request.__isset.event) {
95
0
            const std::vector<int64_t>* table_ids_ptr = nullptr;
96
0
            if (request.__isset.table_ids) {
97
0
                table_ids_ptr = &request.table_ids;
98
0
            }
99
0
            st = manager.set_event(request.job_id, request.event, false, table_ids_ptr);
100
0
            if (st.ok()) {
101
0
                break;
102
0
            }
103
0
        } else {
104
0
            st = manager.check_and_set_job_id(request.job_id);
105
0
        }
106
0
        if (!st.ok()) {
107
0
            LOG_WARNING("SET_JOB failed.").error(st);
108
0
            break;
109
0
        }
110
0
        [[fallthrough]];
111
0
    }
112
0
    case TWarmUpTabletsRequestType::SET_BATCH: {
113
0
        LOG_INFO("receive the warm up request.")
114
0
                .tag("request_type", "SET_BATCH")
115
0
                .tag("job_id", request.job_id)
116
0
                .tag("batch_id", request.batch_id)
117
0
                .tag("jobs size", request.job_metas.size())
118
0
                .tag("tablet num of first meta",
119
0
                     request.job_metas.empty() ? 0 : request.job_metas[0].tablet_ids.size());
120
0
        bool retry = false;
121
0
        st = manager.check_and_set_batch_id(request.job_id, request.batch_id, &retry);
122
0
        if (!retry && st) {
123
0
            manager.add_job(request.job_metas);
124
0
        } else {
125
0
            if (retry) {
126
0
                LOG_WARNING("retry the job.")
127
0
                        .tag("job_id", request.job_id)
128
0
                        .tag("batch_id", request.batch_id);
129
0
            } else {
130
0
                LOG_WARNING("SET_BATCH failed.").error(st);
131
0
            }
132
0
        }
133
0
        break;
134
0
    }
135
0
    case TWarmUpTabletsRequestType::GET_CURRENT_JOB_STATE_AND_LEASE: {
136
0
        auto [job_id, batch_id, pending_job_size, finish_job_size] =
137
0
                manager.get_current_job_state();
138
0
        LOG_INFO("receive the warm up request.")
139
0
                .tag("request_type", "GET_CURRENT_JOB_STATE_AND_LEASE")
140
0
                .tag("job_id", job_id)
141
0
                .tag("batch_id", batch_id)
142
0
                .tag("pending_job_size", pending_job_size)
143
0
                .tag("finish_job_size", finish_job_size);
144
0
        response.__set_job_id(job_id);
145
0
        response.__set_batch_id(batch_id);
146
0
        response.__set_pending_job_size(pending_job_size);
147
0
        response.__set_finish_job_size(finish_job_size);
148
0
        break;
149
0
    }
150
0
    case TWarmUpTabletsRequestType::CLEAR_JOB: {
151
0
        LOG_INFO("receive the warm up request.")
152
0
                .tag("request_type", "CLEAR_JOB")
153
0
                .tag("job_id", request.job_id);
154
0
        if (request.__isset.event) {
155
0
            st = manager.set_event(request.job_id, request.event, /* clear: */ true);
156
0
        } else {
157
0
            st = manager.clear_job(request.job_id);
158
0
        }
159
0
        break;
160
0
    }
161
0
    default:
162
0
        DCHECK(false);
163
0
    };
164
0
    st.to_thrift(&response.status);
165
0
}
166
167
static Status run_rpc_get_file_cache_meta(std::shared_ptr<PBackendService_Stub> brpc_stub,
168
                                          const std::string& brpc_addr,
169
                                          PGetFileCacheMetaRequest brpc_request,
170
0
                                          PGetFileCacheMetaResponse& brpc_response) {
171
0
    brpc::Controller cntl;
172
0
    cntl.set_timeout_ms(20 * 1000); // 20s
173
0
    brpc_stub->get_file_cache_meta_by_tablet_id(&cntl, &brpc_request, &brpc_response, nullptr);
174
0
    if (cntl.Failed()) {
175
0
        LOG(WARNING) << "warm_up_cache_async: brpc call failed, addr=" << brpc_addr
176
0
                     << ", error=" << cntl.ErrorText() << ", error code=" << cntl.ErrorCode();
177
0
        return Status::RpcError("{} isn't connected, error code={}", brpc_addr, cntl.ErrorCode());
178
0
    }
179
0
    VLOG_DEBUG << "warm_up_cache_async: request=" << brpc_request.DebugString()
180
0
               << ", response=" << brpc_response.DebugString();
181
0
    g_file_cache_warm_up_cache_async_submitted_segment_num
182
0
            << brpc_response.file_cache_block_metas().size();
183
0
    return Status::OK();
184
0
}
185
186
0
void CloudBackendService::_warm_up_cache(const TWarmUpCacheAsyncRequest& request) {
187
0
    std::ostringstream oss;
188
0
    oss << "[";
189
0
    for (size_t i = 0; i < request.tablet_ids.size() && i < 10; ++i) {
190
0
        if (i > 0) oss << ",";
191
0
        oss << request.tablet_ids[i];
192
0
    }
193
0
    oss << "]";
194
0
    g_file_cache_warm_up_cache_async_submitted_tablet_num << request.tablet_ids.size();
195
0
    LOG(INFO) << "warm_up_cache_async: enter, request=" << request.host << ":" << request.brpc_port
196
0
              << ", tablets num=" << request.tablet_ids.size() << ", tablet_ids=" << oss.str();
197
198
0
    auto& manager = ExecEnv::GetInstance()->storage_engine().to_cloud().cloud_warm_up_manager();
199
    // Record each tablet in manager
200
0
    std::string compute_group_id;
201
0
    if (request.__isset.cloud_compute_group_id) {
202
0
        compute_group_id = request.cloud_compute_group_id;
203
0
    }
204
0
    for (int64_t tablet_id : request.tablet_ids) {
205
0
        manager.record_balanced_tablet(tablet_id, request.host, request.brpc_port,
206
0
                                       compute_group_id);
207
0
    }
208
209
0
    std::string host = request.host;
210
0
    auto* dns_cache = ExecEnv::GetInstance()->dns_cache();
211
0
    if (dns_cache == nullptr) {
212
0
        LOG(WARNING) << "DNS cache is not initialized, skipping hostname resolve";
213
0
    } else if (!is_valid_ip(request.host)) {
214
0
        Status status = dns_cache->get(request.host, &host);
215
0
        if (!status.ok()) {
216
0
            LOG(WARNING) << "failed to get ip from host " << request.host << ": "
217
0
                         << status.to_string();
218
0
            return;
219
0
        }
220
0
    }
221
0
    std::string brpc_addr = get_host_port(host, request.brpc_port);
222
0
    std::shared_ptr<PBackendService_Stub> brpc_stub =
223
0
            _exec_env->brpc_internal_client_cache()->get_new_client_no_cache(brpc_addr);
224
0
    if (!brpc_stub) {
225
0
        LOG(WARNING) << "warm_up_cache_async: failed to get brpc_stub for addr " << brpc_addr;
226
0
        return;
227
0
    }
228
0
    PGetFileCacheMetaRequest brpc_request;
229
0
    PGetFileCacheMetaResponse brpc_response;
230
0
    for (int64_t tablet_id : request.tablet_ids) {
231
0
        brpc_request.add_tablet_ids(tablet_id);
232
0
    }
233
234
0
    Status rpc_status = run_rpc_get_file_cache_meta(brpc_stub, brpc_addr, std::move(brpc_request),
235
0
                                                    brpc_response);
236
0
    if (rpc_status.ok()) {
237
0
        auto& file_cache_block_metas = *brpc_response.mutable_file_cache_block_metas();
238
0
        if (!file_cache_block_metas.empty()) {
239
0
            _engine.file_cache_block_downloader().submit_download_task(
240
0
                    std::move(file_cache_block_metas));
241
0
            LOG(INFO) << "warm_up_cache_async: successfully submitted download task for tablets="
242
0
                      << oss.str();
243
0
        } else {
244
            // Source BE is reachable but has no hot blocks to download right now. Keep the
245
            // peer mapping so future peer reads can still try the source.
246
0
            LOG(INFO) << "warm_up_cache_async: no file cache block meta found, addr=" << brpc_addr
247
0
                      << ", keeping peer mapping for future peer reads";
248
0
        }
249
0
    } else {
250
0
        LOG(WARNING) << "warm_up_cache_async: rpc failed for addr=" << brpc_addr
251
0
                     << ", status=" << rpc_status;
252
0
    }
253
0
}
254
255
void CloudBackendService::warm_up_cache_async(TWarmUpCacheAsyncResponse& response,
256
0
                                              const TWarmUpCacheAsyncRequest& request) {
257
    // just submit the task to the thread pool, no need to wait for the result
258
0
    auto do_warm_up = [this, request]() { this->_warm_up_cache(request); };
259
0
    g_file_cache_warm_up_cache_async_submitted_task_num << 1;
260
0
    Status submit_st = _engine.warmup_cache_async_thread_pool().submit_func(std::move(do_warm_up));
261
0
    if (!submit_st.ok()) {
262
0
        LOG(WARNING) << "warm_up_cache_async: fail to submit heavy task to "
263
0
                        "warmup_cache_async_thread_pool, status="
264
0
                     << submit_st.to_string() << ", execute synchronously";
265
0
        do_warm_up();
266
0
    }
267
0
    TStatus t_status;
268
0
    submit_st.to_thrift(&t_status);
269
0
    response.status = std::move(t_status);
270
0
}
271
272
void CloudBackendService::check_warm_up_cache_async(TCheckWarmUpCacheAsyncResponse& response,
273
0
                                                    const TCheckWarmUpCacheAsyncRequest& request) {
274
0
    std::ostringstream oss;
275
0
    oss << "[";
276
0
    for (size_t i = 0; i < request.tablets.size() && i < 10; ++i) {
277
0
        if (i > 0) oss << ",";
278
0
        oss << request.tablets[i];
279
0
    }
280
0
    oss << "]";
281
0
    LOG(INFO) << "check_warm_up_cache_async: enter, request tablets num=" << request.tablets.size()
282
0
              << ", tablet_ids=" << oss.str();
283
0
    std::map<int64_t, bool> task_done;
284
0
    _engine.file_cache_block_downloader().check_download_task(request.tablets, &task_done);
285
0
    DBUG_EXECUTE_IF("CloudBackendService.check_warm_up_cache_async.return_task_false", {
286
0
        for (auto& it : task_done) {
287
0
            it.second = false;
288
0
        }
289
0
    });
290
0
    response.__set_task_done(task_done);
291
292
0
    for (const auto& [tablet_id, done] : task_done) {
293
0
        VLOG_DEBUG << "check_warm_up_cache_async: tablet_id=" << tablet_id << ", done=" << done;
294
0
    }
295
296
0
    Status st = Status::OK();
297
0
    TStatus t_status;
298
0
    st.to_thrift(&t_status);
299
0
    response.status = t_status;
300
0
}
301
302
void CloudBackendService::get_stream_load_record(TStreamLoadRecordResult& result,
303
33
                                                 int64_t last_stream_record_time) {
304
33
    BaseBackendService::get_stream_load_record(result, last_stream_record_time,
305
33
                                               _engine.get_stream_load_recorder());
306
33
}
307
308
0
void CloudBackendService::check_storage_format(TCheckStorageFormatResult& result) {
309
0
    for (const auto& tablet : _engine.tablet_mgr().get_all_tablet()) {
310
0
        result.v2_tablets.push_back(tablet->tablet_id());
311
0
        switch (tablet->tablet_schema()->get_inverted_index_storage_format()) {
312
0
        case InvertedIndexStorageFormatPB::V1:
313
0
            result.inverted_index_v1_tablets.push_back(tablet->tablet_id());
314
0
            break;
315
0
        case InvertedIndexStorageFormatPB::V2:
316
0
            result.inverted_index_v2_tablets.push_back(tablet->tablet_id());
317
0
            break;
318
0
        case InvertedIndexStorageFormatPB::V3:
319
0
            result.inverted_index_v3_tablets.push_back(tablet->tablet_id());
320
0
            break;
321
0
        default:
322
0
            DCHECK(false) << "invalid inverted index storage format";
323
0
        }
324
0
    }
325
0
    result.__isset.v1_tablets = true;
326
0
    result.__isset.v2_tablets = true;
327
0
    result.__isset.inverted_index_v1_tablets = true;
328
0
    result.__isset.inverted_index_v2_tablets = true;
329
0
    result.__isset.inverted_index_v3_tablets = true;
330
0
}
331
332
} // namespace doris