be/src/service/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 "service/backend_service.h" |
19 | | |
20 | | #include <absl/strings/str_split.h> |
21 | | #include <arrow/record_batch.h> |
22 | | #include <fmt/format.h> |
23 | | #include <gen_cpp/BackendService.h> |
24 | | #include <gen_cpp/BackendService_types.h> |
25 | | #include <gen_cpp/Data_types.h> |
26 | | #include <gen_cpp/DorisExternalService_types.h> |
27 | | #include <gen_cpp/FrontendService_types.h> |
28 | | #include <gen_cpp/Metrics_types.h> |
29 | | #include <gen_cpp/PaloInternalService_types.h> |
30 | | #include <gen_cpp/Planner_types.h> |
31 | | #include <gen_cpp/Status_types.h> |
32 | | #include <gen_cpp/Types_types.h> |
33 | | #include <sys/types.h> |
34 | | #include <thrift/concurrency/ThreadFactory.h> |
35 | | #include <thrift/protocol/TDebugProtocol.h> |
36 | | #include <time.h> |
37 | | |
38 | | #include <cstdint> |
39 | | #include <map> |
40 | | #include <memory> |
41 | | #include <ostream> |
42 | | #include <ranges> |
43 | | #include <string> |
44 | | #include <thread> |
45 | | #include <unordered_map> |
46 | | #include <utility> |
47 | | #include <vector> |
48 | | |
49 | | #include "absl/strings/substitute.h" |
50 | | #include "cloud/config.h" |
51 | | #include "common/config.h" |
52 | | #include "common/logging.h" |
53 | | #include "common/status.h" |
54 | | #include "exprs/function/dictionary_factory.h" |
55 | | #include "format/arrow/arrow_row_batch.h" |
56 | | #include "io/fs/connectivity/storage_connectivity_tester.h" |
57 | | #include "io/fs/local_file_system.h" |
58 | | #include "load/routine_load/routine_load_task_executor.h" |
59 | | #include "load/stream_load/stream_load_context.h" |
60 | | #include "load/stream_load/stream_load_recorder.h" |
61 | | #include "runtime/exec_env.h" |
62 | | #include "runtime/external_scan_context_mgr.h" |
63 | | #include "runtime/fragment_mgr.h" |
64 | | #include "runtime/result_queue_mgr.h" |
65 | | #include "runtime/runtime_profile.h" |
66 | | #include "service/http/http_client.h" |
67 | | #include "storage/olap_common.h" |
68 | | #include "storage/olap_define.h" |
69 | | #include "storage/rowset/beta_rowset.h" |
70 | | #include "storage/rowset/pending_rowset_helper.h" |
71 | | #include "storage/rowset/rowset_factory.h" |
72 | | #include "storage/rowset/rowset_meta.h" |
73 | | #include "storage/snapshot/snapshot_manager.h" |
74 | | #include "storage/storage_engine.h" |
75 | | #include "storage/tablet/tablet_manager.h" |
76 | | #include "storage/tablet/tablet_meta.h" |
77 | | #include "storage/txn/txn_manager.h" |
78 | | #include "udf/python/python_env.h" |
79 | | #include "util/defer_op.h" |
80 | | #include "util/threadpool.h" |
81 | | #include "util/thrift_server.h" |
82 | | #include "util/uid_util.h" |
83 | | #include "util/url_coding.h" |
84 | | |
85 | | namespace apache { |
86 | | namespace thrift { |
87 | | class TException; |
88 | | class TMultiplexedProcessor; |
89 | | class TProcessor; |
90 | | namespace transport { |
91 | | class TTransportException; |
92 | | } // namespace transport |
93 | | } // namespace thrift |
94 | | } // namespace apache |
95 | | |
96 | | namespace doris { |
97 | | #include "common/compile_check_begin.h" |
98 | | namespace { |
99 | | |
100 | | bvar::LatencyRecorder g_ingest_binlog_latency("doris_backend_service", "ingest_binlog"); |
101 | | |
102 | | struct IngestBinlogArg { |
103 | | int64_t txn_id; |
104 | | int64_t partition_id; |
105 | | int64_t local_tablet_id; |
106 | | TabletSharedPtr local_tablet; |
107 | | TIngestBinlogRequest request; |
108 | | TStatus* tstatus; |
109 | | }; |
110 | | |
111 | | Status _exec_http_req(std::optional<HttpClient>& client, int retry_times, int sleep_time, |
112 | 0 | const std::function<Status(HttpClient*)>& callback) { |
113 | 0 | if (client.has_value()) { |
114 | 0 | return client->execute(retry_times, sleep_time, callback); |
115 | 0 | } else { |
116 | 0 | return HttpClient::execute_with_retry(retry_times, sleep_time, callback); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | Status _download_binlog_segment_file(HttpClient* client, const std::string& get_segment_file_url, |
121 | | const std::string& segment_path, uint64_t segment_file_size, |
122 | | uint64_t estimate_timeout, |
123 | 0 | std::vector<std::string>& download_success_files) { |
124 | 0 | RETURN_IF_ERROR(client->init(get_segment_file_url)); |
125 | 0 | client->set_timeout_ms(estimate_timeout * 1000); |
126 | 0 | RETURN_IF_ERROR(client->download(segment_path)); |
127 | 0 | download_success_files.push_back(segment_path); |
128 | |
|
129 | 0 | std::string remote_file_md5; |
130 | 0 | RETURN_IF_ERROR(client->get_content_md5(&remote_file_md5)); |
131 | 0 | LOG(INFO) << "download segment file to " << segment_path << ", remote md5: " << remote_file_md5 |
132 | 0 | << ", remote size: " << segment_file_size; |
133 | |
|
134 | 0 | std::error_code ec; |
135 | | // Check file length |
136 | 0 | uint64_t local_file_size = std::filesystem::file_size(segment_path, ec); |
137 | 0 | if (ec) { |
138 | 0 | LOG(WARNING) << "download file error" << ec.message(); |
139 | 0 | return Status::IOError("can't retrive file_size of {}, due to {}", segment_path, |
140 | 0 | ec.message()); |
141 | 0 | } |
142 | | |
143 | 0 | if (local_file_size != segment_file_size) { |
144 | 0 | LOG(WARNING) << "download file length error" |
145 | 0 | << ", get_segment_file_url=" << get_segment_file_url |
146 | 0 | << ", file_size=" << segment_file_size |
147 | 0 | << ", local_file_size=" << local_file_size; |
148 | 0 | return Status::RuntimeError("downloaded file size is not equal, local={}, remote={}", |
149 | 0 | local_file_size, segment_file_size); |
150 | 0 | } |
151 | | |
152 | 0 | if (!remote_file_md5.empty()) { // keep compatibility |
153 | 0 | std::string local_file_md5; |
154 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->md5sum(segment_path, &local_file_md5)); |
155 | 0 | if (local_file_md5 != remote_file_md5) { |
156 | 0 | LOG(WARNING) << "download file md5 error" |
157 | 0 | << ", get_segment_file_url=" << get_segment_file_url |
158 | 0 | << ", remote_file_md5=" << remote_file_md5 |
159 | 0 | << ", local_file_md5=" << local_file_md5; |
160 | 0 | return Status::RuntimeError("download file md5 is not equal, local={}, remote={}", |
161 | 0 | local_file_md5, remote_file_md5); |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | 0 | return io::global_local_filesystem()->permission(segment_path, |
166 | 0 | io::LocalFileSystem::PERMS_OWNER_RW); |
167 | 0 | } |
168 | | |
169 | | Status _download_binlog_index_file(HttpClient* client, |
170 | | const std::string& get_segment_index_file_url, |
171 | | const std::string& local_segment_index_path, |
172 | | uint64_t segment_index_file_size, uint64_t estimate_timeout, |
173 | 0 | std::vector<std::string>& download_success_files) { |
174 | 0 | RETURN_IF_ERROR(client->init(get_segment_index_file_url)); |
175 | 0 | client->set_timeout_ms(estimate_timeout * 1000); |
176 | 0 | RETURN_IF_ERROR(client->download(local_segment_index_path)); |
177 | 0 | download_success_files.push_back(local_segment_index_path); |
178 | |
|
179 | 0 | std::string remote_file_md5; |
180 | 0 | RETURN_IF_ERROR(client->get_content_md5(&remote_file_md5)); |
181 | | |
182 | 0 | LOG(INFO) << "download segment index file to " << local_segment_index_path |
183 | 0 | << ", remote md5: " << remote_file_md5 |
184 | 0 | << ", remote size: " << segment_index_file_size; |
185 | |
|
186 | 0 | std::error_code ec; |
187 | | // Check file length |
188 | 0 | uint64_t local_index_file_size = std::filesystem::file_size(local_segment_index_path, ec); |
189 | 0 | if (ec) { |
190 | 0 | LOG(WARNING) << "download index file error" << ec.message(); |
191 | 0 | return Status::IOError("can't retrive file_size of {}, due to {}", local_segment_index_path, |
192 | 0 | ec.message()); |
193 | 0 | } |
194 | 0 | if (local_index_file_size != segment_index_file_size) { |
195 | 0 | LOG(WARNING) << "download index file length error" |
196 | 0 | << ", get_segment_index_file_url=" << get_segment_index_file_url |
197 | 0 | << ", index_file_size=" << segment_index_file_size |
198 | 0 | << ", local_index_file_size=" << local_index_file_size; |
199 | 0 | return Status::RuntimeError("downloaded index file size is not equal, local={}, remote={}", |
200 | 0 | local_index_file_size, segment_index_file_size); |
201 | 0 | } |
202 | | |
203 | 0 | if (!remote_file_md5.empty()) { // keep compatibility |
204 | 0 | std::string local_file_md5; |
205 | 0 | RETURN_IF_ERROR( |
206 | 0 | io::global_local_filesystem()->md5sum(local_segment_index_path, &local_file_md5)); |
207 | 0 | if (local_file_md5 != remote_file_md5) { |
208 | 0 | LOG(WARNING) << "download file md5 error" |
209 | 0 | << ", get_segment_index_file_url=" << get_segment_index_file_url |
210 | 0 | << ", remote_file_md5=" << remote_file_md5 |
211 | 0 | << ", local_file_md5=" << local_file_md5; |
212 | 0 | return Status::RuntimeError("download file md5 is not equal, local={}, remote={}", |
213 | 0 | local_file_md5, remote_file_md5); |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | 0 | return io::global_local_filesystem()->permission(local_segment_index_path, |
218 | 0 | io::LocalFileSystem::PERMS_OWNER_RW); |
219 | 0 | } |
220 | | |
221 | 0 | void _ingest_binlog(StorageEngine& engine, IngestBinlogArg* arg) { |
222 | 0 | std::optional<HttpClient> client; |
223 | 0 | if (config::enable_ingest_binlog_with_persistent_connection) { |
224 | | // Save the http client instance for persistent connection |
225 | 0 | client = std::make_optional<HttpClient>(); |
226 | 0 | } |
227 | |
|
228 | 0 | auto txn_id = arg->txn_id; |
229 | 0 | auto partition_id = arg->partition_id; |
230 | 0 | auto local_tablet_id = arg->local_tablet_id; |
231 | 0 | const auto& local_tablet = arg->local_tablet; |
232 | 0 | const auto& local_tablet_uid = local_tablet->tablet_uid(); |
233 | |
|
234 | 0 | std::shared_ptr<MemTrackerLimiter> mem_tracker = MemTrackerLimiter::create_shared( |
235 | 0 | MemTrackerLimiter::Type::OTHER, fmt::format("IngestBinlog#TxnId={}", txn_id)); |
236 | 0 | SCOPED_ATTACH_TASK(mem_tracker); |
237 | |
|
238 | 0 | auto& request = arg->request; |
239 | |
|
240 | 0 | MonotonicStopWatch watch(true); |
241 | 0 | int64_t total_download_bytes = 0; |
242 | 0 | int64_t total_download_files = 0; |
243 | 0 | TStatus tstatus; |
244 | 0 | std::vector<std::string> download_success_files; |
245 | 0 | std::unordered_map<std::string_view, uint64_t> elapsed_time_map; |
246 | 0 | Defer defer {[=, &engine, &tstatus, ingest_binlog_tstatus = arg->tstatus, &watch, |
247 | 0 | &total_download_bytes, &total_download_files, &elapsed_time_map]() { |
248 | 0 | g_ingest_binlog_latency << watch.elapsed_time_microseconds(); |
249 | 0 | auto elapsed_time_ms = static_cast<int64_t>(watch.elapsed_time_milliseconds()); |
250 | 0 | double copy_rate = 0.0; |
251 | 0 | if (elapsed_time_ms > 0) { |
252 | 0 | copy_rate = (double)total_download_bytes / ((double)elapsed_time_ms) / 1000; |
253 | 0 | } |
254 | 0 | LOG(INFO) << "ingest binlog elapsed " << elapsed_time_ms << " ms, download " |
255 | 0 | << total_download_files << " files, total " << total_download_bytes |
256 | 0 | << " bytes, avg rate " << copy_rate |
257 | 0 | << " MB/s. result: " << apache::thrift::ThriftDebugString(tstatus); |
258 | 0 | if (config::ingest_binlog_elapsed_threshold_ms >= 0 && |
259 | 0 | elapsed_time_ms > config::ingest_binlog_elapsed_threshold_ms) { |
260 | 0 | auto elapsed_details_view = |
261 | 0 | elapsed_time_map | std::views::transform([](const auto& pair) { |
262 | 0 | return fmt::format("{}:{}", pair.first, pair.second); |
263 | 0 | }); |
264 | 0 | std::string elapsed_details = fmt::format("{}", fmt::join(elapsed_details_view, ", ")); |
265 | 0 | LOG(WARNING) << "ingest binlog elapsed " << elapsed_time_ms << " ms, " |
266 | 0 | << elapsed_details; |
267 | 0 | } |
268 | 0 | if (tstatus.status_code != TStatusCode::OK) { |
269 | | // abort txn |
270 | 0 | engine.txn_manager()->abort_txn(partition_id, txn_id, local_tablet_id, |
271 | 0 | local_tablet_uid); |
272 | | // delete all successfully downloaded files |
273 | 0 | LOG(WARNING) << "will delete downloaded success files due to error " << tstatus; |
274 | 0 | std::vector<io::Path> paths; |
275 | 0 | for (const auto& file : download_success_files) { |
276 | 0 | paths.emplace_back(file); |
277 | 0 | LOG(WARNING) << "will delete downloaded success file " << file << " due to error"; |
278 | 0 | } |
279 | 0 | static_cast<void>(io::global_local_filesystem()->batch_delete(paths)); |
280 | 0 | LOG(WARNING) << "done delete downloaded success files due to error " << tstatus; |
281 | 0 | } |
282 | |
|
283 | 0 | if (ingest_binlog_tstatus) { |
284 | 0 | *ingest_binlog_tstatus = std::move(tstatus); |
285 | 0 | } |
286 | 0 | }}; |
287 | |
|
288 | 0 | auto set_tstatus = [&tstatus](TStatusCode::type code, std::string error_msg) { |
289 | 0 | tstatus.__set_status_code(code); |
290 | 0 | tstatus.__isset.error_msgs = true; |
291 | 0 | tstatus.error_msgs.push_back(std::move(error_msg)); |
292 | 0 | }; |
293 | |
|
294 | 0 | auto estimate_download_timeout = [](int64_t file_size) { |
295 | 0 | uint64_t estimate_timeout = file_size / config::download_low_speed_limit_kbps / 1024; |
296 | 0 | if (estimate_timeout < config::download_low_speed_time) { |
297 | 0 | estimate_timeout = config::download_low_speed_time; |
298 | 0 | } |
299 | 0 | return estimate_timeout; |
300 | 0 | }; |
301 | | |
302 | | // Step 3: get binlog info |
303 | 0 | auto binlog_api_url = fmt::format("http://{}:{}/api/_binlog/_download", request.remote_host, |
304 | 0 | request.remote_port); |
305 | 0 | constexpr int max_retry = 3; |
306 | |
|
307 | 0 | auto get_binlog_info_url = |
308 | 0 | fmt::format("{}?method={}&tablet_id={}&binlog_version={}", binlog_api_url, |
309 | 0 | "get_binlog_info", request.remote_tablet_id, request.binlog_version); |
310 | 0 | std::string binlog_info; |
311 | 0 | auto get_binlog_info_cb = [&get_binlog_info_url, &binlog_info](HttpClient* client) { |
312 | 0 | RETURN_IF_ERROR(client->init(get_binlog_info_url)); |
313 | 0 | client->set_timeout_ms(config::download_binlog_meta_timeout_ms); |
314 | 0 | return client->execute(&binlog_info); |
315 | 0 | }; |
316 | 0 | auto status = _exec_http_req(client, max_retry, 1, get_binlog_info_cb); |
317 | 0 | if (!status.ok()) { |
318 | 0 | LOG(WARNING) << "failed to get binlog info from " << get_binlog_info_url |
319 | 0 | << ", status=" << status.to_string(); |
320 | 0 | status.to_thrift(&tstatus); |
321 | 0 | return; |
322 | 0 | } |
323 | 0 | elapsed_time_map.emplace("get_binlog_info", watch.elapsed_time_microseconds()); |
324 | |
|
325 | 0 | std::vector<std::string> binlog_info_parts = absl::StrSplit(binlog_info, ":"); |
326 | 0 | if (binlog_info_parts.size() != 2) { |
327 | 0 | status = Status::RuntimeError("failed to parse binlog info into 2 parts: {}", binlog_info); |
328 | 0 | LOG(WARNING) << "failed to get binlog info from " << get_binlog_info_url |
329 | 0 | << ", status=" << status.to_string(); |
330 | 0 | status.to_thrift(&tstatus); |
331 | 0 | return; |
332 | 0 | } |
333 | 0 | std::string remote_rowset_id = std::move(binlog_info_parts[0]); |
334 | 0 | int64_t num_segments = -1; |
335 | 0 | try { |
336 | 0 | num_segments = std::stoll(binlog_info_parts[1]); |
337 | 0 | } catch (std::exception& e) { |
338 | 0 | status = Status::RuntimeError("failed to parse num segments from binlog info {}: {}", |
339 | 0 | binlog_info, e.what()); |
340 | 0 | LOG(WARNING) << "failed to get binlog info from " << get_binlog_info_url |
341 | 0 | << ", status=" << status; |
342 | 0 | status.to_thrift(&tstatus); |
343 | 0 | return; |
344 | 0 | } |
345 | | |
346 | | // Step 4: get rowset meta |
347 | 0 | auto get_rowset_meta_url = fmt::format( |
348 | 0 | "{}?method={}&tablet_id={}&rowset_id={}&binlog_version={}", binlog_api_url, |
349 | 0 | "get_rowset_meta", request.remote_tablet_id, remote_rowset_id, request.binlog_version); |
350 | 0 | std::string rowset_meta_str; |
351 | 0 | auto get_rowset_meta_cb = [&get_rowset_meta_url, &rowset_meta_str](HttpClient* client) { |
352 | 0 | RETURN_IF_ERROR(client->init(get_rowset_meta_url)); |
353 | 0 | client->set_timeout_ms(config::download_binlog_meta_timeout_ms); |
354 | 0 | return client->execute(&rowset_meta_str); |
355 | 0 | }; |
356 | 0 | status = _exec_http_req(client, max_retry, 1, get_rowset_meta_cb); |
357 | 0 | if (!status.ok()) { |
358 | 0 | LOG(WARNING) << "failed to get rowset meta from " << get_rowset_meta_url |
359 | 0 | << ", status=" << status.to_string(); |
360 | 0 | status.to_thrift(&tstatus); |
361 | 0 | return; |
362 | 0 | } |
363 | 0 | elapsed_time_map.emplace("get_rowset_meta", watch.elapsed_time_microseconds()); |
364 | |
|
365 | 0 | RowsetMetaPB rowset_meta_pb; |
366 | 0 | if (!rowset_meta_pb.ParseFromString(rowset_meta_str)) { |
367 | 0 | LOG(WARNING) << "failed to parse rowset meta from " << get_rowset_meta_url; |
368 | 0 | status = Status::InternalError("failed to parse rowset meta"); |
369 | 0 | status.to_thrift(&tstatus); |
370 | 0 | return; |
371 | 0 | } |
372 | | // save source rowset id and tablet id |
373 | 0 | rowset_meta_pb.set_source_rowset_id(remote_rowset_id); |
374 | 0 | rowset_meta_pb.set_source_tablet_id(request.remote_tablet_id); |
375 | | // rewrite rowset meta |
376 | 0 | rowset_meta_pb.set_tablet_id(local_tablet_id); |
377 | 0 | rowset_meta_pb.set_partition_id(partition_id); |
378 | 0 | rowset_meta_pb.set_tablet_schema_hash(local_tablet->tablet_meta()->schema_hash()); |
379 | 0 | rowset_meta_pb.set_txn_id(txn_id); |
380 | 0 | rowset_meta_pb.set_rowset_state(RowsetStatePB::COMMITTED); |
381 | 0 | auto rowset_meta = std::make_shared<RowsetMeta>(); |
382 | 0 | if (!rowset_meta->init_from_pb(rowset_meta_pb)) { |
383 | 0 | LOG(WARNING) << "failed to init rowset meta from " << get_rowset_meta_url; |
384 | 0 | status = Status::InternalError("failed to init rowset meta"); |
385 | 0 | status.to_thrift(&tstatus); |
386 | 0 | return; |
387 | 0 | } |
388 | 0 | RowsetId new_rowset_id = engine.next_rowset_id(); |
389 | 0 | auto pending_rs_guard = engine.pending_local_rowsets().add(new_rowset_id); |
390 | 0 | rowset_meta->set_rowset_id(new_rowset_id); |
391 | 0 | rowset_meta->set_tablet_uid(local_tablet->tablet_uid()); |
392 | | |
393 | | // Step 5: get all segment files |
394 | | // Step 5.1: get all segment files size |
395 | 0 | std::vector<std::string> segment_file_urls; |
396 | 0 | segment_file_urls.reserve(num_segments); |
397 | 0 | std::vector<uint64_t> segment_file_sizes; |
398 | 0 | segment_file_sizes.reserve(num_segments); |
399 | 0 | for (int64_t segment_index = 0; segment_index < num_segments; ++segment_index) { |
400 | 0 | auto get_segment_file_size_url = fmt::format( |
401 | 0 | "{}?method={}&tablet_id={}&rowset_id={}&segment_index={}", binlog_api_url, |
402 | 0 | "get_segment_file", request.remote_tablet_id, remote_rowset_id, segment_index); |
403 | 0 | uint64_t segment_file_size; |
404 | 0 | auto get_segment_file_size_cb = [&get_segment_file_size_url, |
405 | 0 | &segment_file_size](HttpClient* client) { |
406 | 0 | RETURN_IF_ERROR(client->init(get_segment_file_size_url)); |
407 | 0 | client->set_timeout_ms(config::download_binlog_meta_timeout_ms); |
408 | 0 | RETURN_IF_ERROR(client->head()); |
409 | 0 | return client->get_content_length(&segment_file_size); |
410 | 0 | }; |
411 | |
|
412 | 0 | status = _exec_http_req(client, max_retry, 1, get_segment_file_size_cb); |
413 | 0 | if (!status.ok()) { |
414 | 0 | LOG(WARNING) << "failed to get segment file size from " << get_segment_file_size_url |
415 | 0 | << ", status=" << status.to_string(); |
416 | 0 | status.to_thrift(&tstatus); |
417 | 0 | return; |
418 | 0 | } |
419 | | |
420 | 0 | segment_file_sizes.push_back(segment_file_size); |
421 | 0 | segment_file_urls.push_back(std::move(get_segment_file_size_url)); |
422 | 0 | } |
423 | 0 | elapsed_time_map.emplace("get_segment_file_size", watch.elapsed_time_microseconds()); |
424 | | |
425 | | // Step 5.2: check data capacity |
426 | 0 | uint64_t total_size = std::accumulate(segment_file_sizes.begin(), segment_file_sizes.end(), |
427 | 0 | 0ULL); // NOLINT(bugprone-fold-init-type) |
428 | 0 | if (!local_tablet->can_add_binlog(total_size)) { |
429 | 0 | LOG(WARNING) << "failed to add binlog, no enough space, total_size=" << total_size |
430 | 0 | << ", tablet=" << local_tablet->tablet_id(); |
431 | 0 | status = Status::InternalError("no enough space"); |
432 | 0 | status.to_thrift(&tstatus); |
433 | 0 | return; |
434 | 0 | } |
435 | 0 | total_download_bytes = total_size; |
436 | 0 | total_download_files = num_segments; |
437 | | |
438 | | // Step 5.3: get all segment files |
439 | 0 | for (int64_t segment_index = 0; segment_index < num_segments; ++segment_index) { |
440 | 0 | auto segment_file_size = segment_file_sizes[segment_index]; |
441 | 0 | auto get_segment_file_url = segment_file_urls[segment_index]; |
442 | 0 | if (config::enable_download_md5sum_check) { |
443 | 0 | get_segment_file_url = fmt::format("{}&acquire_md5=true", get_segment_file_url); |
444 | 0 | } |
445 | |
|
446 | 0 | auto segment_path = local_segment_path(local_tablet->tablet_path(), |
447 | 0 | rowset_meta->rowset_id().to_string(), segment_index); |
448 | 0 | LOG(INFO) << "download segment file from " << get_segment_file_url << " to " |
449 | 0 | << segment_path; |
450 | 0 | uint64_t estimate_timeout = estimate_download_timeout(segment_file_size); |
451 | 0 | auto get_segment_file_cb = [&get_segment_file_url, &segment_path, segment_file_size, |
452 | 0 | estimate_timeout, &download_success_files](HttpClient* client) { |
453 | 0 | return _download_binlog_segment_file(client, get_segment_file_url, segment_path, |
454 | 0 | segment_file_size, estimate_timeout, |
455 | 0 | download_success_files); |
456 | 0 | }; |
457 | |
|
458 | 0 | status = _exec_http_req(client, max_retry, 1, get_segment_file_cb); |
459 | 0 | if (!status.ok()) { |
460 | 0 | LOG(WARNING) << "failed to get segment file from " << get_segment_file_url |
461 | 0 | << ", status=" << status.to_string(); |
462 | 0 | status.to_thrift(&tstatus); |
463 | 0 | return; |
464 | 0 | } |
465 | 0 | } |
466 | 0 | elapsed_time_map.emplace("get_segment_files", watch.elapsed_time_microseconds()); |
467 | | |
468 | | // Step 6: get all segment index files |
469 | | // Step 6.1: get all segment index files size |
470 | 0 | std::vector<std::string> segment_index_file_urls; |
471 | 0 | std::vector<uint64_t> segment_index_file_sizes; |
472 | 0 | std::vector<std::string> segment_index_file_names; |
473 | 0 | auto tablet_schema = rowset_meta->tablet_schema(); |
474 | 0 | if (tablet_schema->get_inverted_index_storage_format() == InvertedIndexStorageFormatPB::V1) { |
475 | 0 | for (const auto& index : tablet_schema->inverted_indexes()) { |
476 | 0 | auto index_id = index->index_id(); |
477 | 0 | for (int64_t segment_index = 0; segment_index < num_segments; ++segment_index) { |
478 | 0 | auto get_segment_index_file_size_url = fmt::format( |
479 | 0 | "{}?method={}&tablet_id={}&rowset_id={}&segment_index={}&segment_index_id={" |
480 | 0 | "}", |
481 | 0 | binlog_api_url, "get_segment_index_file", request.remote_tablet_id, |
482 | 0 | remote_rowset_id, segment_index, index_id); |
483 | 0 | uint64_t segment_index_file_size; |
484 | 0 | auto get_segment_index_file_size_cb = |
485 | 0 | [&get_segment_index_file_size_url, |
486 | 0 | &segment_index_file_size](HttpClient* client) { |
487 | 0 | RETURN_IF_ERROR(client->init(get_segment_index_file_size_url)); |
488 | 0 | client->set_timeout_ms(config::download_binlog_meta_timeout_ms); |
489 | 0 | RETURN_IF_ERROR(client->head()); |
490 | 0 | return client->get_content_length(&segment_index_file_size); |
491 | 0 | }; |
492 | |
|
493 | 0 | auto segment_path = |
494 | 0 | local_segment_path(local_tablet->tablet_path(), |
495 | 0 | rowset_meta->rowset_id().to_string(), segment_index); |
496 | 0 | segment_index_file_names.push_back(InvertedIndexDescriptor::get_index_file_path_v1( |
497 | 0 | InvertedIndexDescriptor::get_index_file_path_prefix(segment_path), index_id, |
498 | 0 | index->get_index_suffix())); |
499 | |
|
500 | 0 | status = _exec_http_req(client, max_retry, 1, get_segment_index_file_size_cb); |
501 | 0 | if (!status.ok()) { |
502 | 0 | LOG(WARNING) << "failed to get segment file size from " |
503 | 0 | << get_segment_index_file_size_url |
504 | 0 | << ", status=" << status.to_string(); |
505 | 0 | status.to_thrift(&tstatus); |
506 | 0 | return; |
507 | 0 | } |
508 | | |
509 | 0 | segment_index_file_sizes.push_back(segment_index_file_size); |
510 | 0 | segment_index_file_urls.push_back(std::move(get_segment_index_file_size_url)); |
511 | 0 | } |
512 | 0 | } |
513 | 0 | } else { |
514 | 0 | for (int64_t segment_index = 0; segment_index < num_segments; ++segment_index) { |
515 | 0 | if (tablet_schema->has_inverted_index() || tablet_schema->has_ann_index()) { |
516 | 0 | auto get_segment_index_file_size_url = fmt::format( |
517 | 0 | "{}?method={}&tablet_id={}&rowset_id={}&segment_index={}&segment_index_id={" |
518 | 0 | "}", |
519 | 0 | binlog_api_url, "get_segment_index_file", request.remote_tablet_id, |
520 | 0 | remote_rowset_id, segment_index, -1); |
521 | 0 | uint64_t segment_index_file_size; |
522 | 0 | auto get_segment_index_file_size_cb = |
523 | 0 | [&get_segment_index_file_size_url, |
524 | 0 | &segment_index_file_size](HttpClient* client) { |
525 | 0 | RETURN_IF_ERROR(client->init(get_segment_index_file_size_url)); |
526 | 0 | client->set_timeout_ms(config::download_binlog_meta_timeout_ms); |
527 | 0 | RETURN_IF_ERROR(client->head()); |
528 | 0 | return client->get_content_length(&segment_index_file_size); |
529 | 0 | }; |
530 | 0 | auto segment_path = |
531 | 0 | local_segment_path(local_tablet->tablet_path(), |
532 | 0 | rowset_meta->rowset_id().to_string(), segment_index); |
533 | 0 | segment_index_file_names.push_back(InvertedIndexDescriptor::get_index_file_path_v2( |
534 | 0 | InvertedIndexDescriptor::get_index_file_path_prefix(segment_path))); |
535 | |
|
536 | 0 | status = _exec_http_req(client, max_retry, 1, get_segment_index_file_size_cb); |
537 | 0 | if (!status.ok()) { |
538 | 0 | LOG(WARNING) << "failed to get segment file size from " |
539 | 0 | << get_segment_index_file_size_url |
540 | 0 | << ", status=" << status.to_string(); |
541 | 0 | status.to_thrift(&tstatus); |
542 | 0 | return; |
543 | 0 | } |
544 | | |
545 | 0 | segment_index_file_sizes.push_back(segment_index_file_size); |
546 | 0 | segment_index_file_urls.push_back(std::move(get_segment_index_file_size_url)); |
547 | 0 | } |
548 | 0 | } |
549 | 0 | } |
550 | 0 | elapsed_time_map.emplace("get_segment_index_file_size", watch.elapsed_time_microseconds()); |
551 | | |
552 | | // Step 6.2: check data capacity |
553 | 0 | uint64_t total_index_size = |
554 | 0 | std::accumulate(segment_index_file_sizes.begin(), segment_index_file_sizes.end(), |
555 | 0 | 0ULL); // NOLINT(bugprone-fold-init-type) |
556 | 0 | if (!local_tablet->can_add_binlog(total_index_size)) { |
557 | 0 | LOG(WARNING) << "failed to add binlog, no enough space, total_index_size=" |
558 | 0 | << total_index_size << ", tablet=" << local_tablet->tablet_id(); |
559 | 0 | status = Status::InternalError("no enough space"); |
560 | 0 | status.to_thrift(&tstatus); |
561 | 0 | return; |
562 | 0 | } |
563 | 0 | total_download_bytes += total_index_size; |
564 | 0 | total_download_files += segment_index_file_urls.size(); |
565 | | |
566 | | // Step 6.3: get all segment index files |
567 | 0 | DCHECK(segment_index_file_sizes.size() == segment_index_file_names.size()); |
568 | 0 | DCHECK(segment_index_file_names.size() == segment_index_file_urls.size()); |
569 | 0 | for (int64_t i = 0; i < segment_index_file_urls.size(); ++i) { |
570 | 0 | auto segment_index_file_size = segment_index_file_sizes[i]; |
571 | 0 | auto get_segment_index_file_url = segment_index_file_urls[i]; |
572 | 0 | if (config::enable_download_md5sum_check) { |
573 | 0 | get_segment_index_file_url = |
574 | 0 | fmt::format("{}&acquire_md5=true", get_segment_index_file_url); |
575 | 0 | } |
576 | |
|
577 | 0 | uint64_t estimate_timeout = estimate_download_timeout(segment_index_file_size); |
578 | 0 | auto local_segment_index_path = segment_index_file_names[i]; |
579 | 0 | LOG(INFO) << fmt::format("download segment index file from {} to {}", |
580 | 0 | get_segment_index_file_url, local_segment_index_path); |
581 | 0 | auto get_segment_index_file_cb = [&get_segment_index_file_url, &local_segment_index_path, |
582 | 0 | segment_index_file_size, estimate_timeout, |
583 | 0 | &download_success_files](HttpClient* client) { |
584 | 0 | return _download_binlog_index_file(client, get_segment_index_file_url, |
585 | 0 | local_segment_index_path, segment_index_file_size, |
586 | 0 | estimate_timeout, download_success_files); |
587 | 0 | }; |
588 | |
|
589 | 0 | status = _exec_http_req(client, max_retry, 1, get_segment_index_file_cb); |
590 | 0 | if (!status.ok()) { |
591 | 0 | LOG(WARNING) << "failed to get segment index file from " << get_segment_index_file_url |
592 | 0 | << ", status=" << status.to_string(); |
593 | 0 | status.to_thrift(&tstatus); |
594 | 0 | return; |
595 | 0 | } |
596 | 0 | } |
597 | 0 | elapsed_time_map.emplace("get_segment_index_files", watch.elapsed_time_microseconds()); |
598 | | |
599 | | // Step 7: create rowset && calculate delete bitmap && commit |
600 | | // Step 7.1: create rowset |
601 | 0 | RowsetSharedPtr rowset; |
602 | 0 | status = RowsetFactory::create_rowset(local_tablet->tablet_schema(), |
603 | 0 | local_tablet->tablet_path(), rowset_meta, &rowset); |
604 | 0 | if (!status) { |
605 | 0 | LOG(WARNING) << "failed to create rowset from rowset meta for remote tablet" |
606 | 0 | << ". rowset_id: " << rowset_meta_pb.rowset_id() |
607 | 0 | << ", rowset_type: " << rowset_meta_pb.rowset_type() |
608 | 0 | << ", remote_tablet_id=" << rowset_meta_pb.tablet_id() << ", txn_id=" << txn_id |
609 | 0 | << ", status=" << status.to_string(); |
610 | 0 | status.to_thrift(&tstatus); |
611 | 0 | return; |
612 | 0 | } |
613 | | |
614 | | // Step 7.2 calculate delete bitmap before commit |
615 | 0 | auto calc_delete_bitmap_token = engine.calc_delete_bitmap_executor()->create_token(); |
616 | 0 | DeleteBitmapPtr delete_bitmap = std::make_shared<DeleteBitmap>(local_tablet_id); |
617 | 0 | RowsetIdUnorderedSet pre_rowset_ids; |
618 | 0 | if (local_tablet->enable_unique_key_merge_on_write()) { |
619 | 0 | auto beta_rowset = reinterpret_cast<BetaRowset*>(rowset.get()); |
620 | 0 | std::vector<segment_v2::SegmentSharedPtr> segments; |
621 | 0 | status = beta_rowset->load_segments(&segments); |
622 | 0 | if (!status) { |
623 | 0 | LOG(WARNING) << "failed to load segments from rowset" |
624 | 0 | << ". rowset_id: " << beta_rowset->rowset_id() << ", txn_id=" << txn_id |
625 | 0 | << ", status=" << status.to_string(); |
626 | 0 | status.to_thrift(&tstatus); |
627 | 0 | return; |
628 | 0 | } |
629 | 0 | elapsed_time_map.emplace("load_segments", watch.elapsed_time_microseconds()); |
630 | 0 | if (segments.size() > 1) { |
631 | | // calculate delete bitmap between segments |
632 | 0 | status = local_tablet->calc_delete_bitmap_between_segments( |
633 | 0 | rowset->tablet_schema(), rowset->rowset_id(), segments, delete_bitmap); |
634 | 0 | if (!status) { |
635 | 0 | LOG(WARNING) << "failed to calculate delete bitmap" |
636 | 0 | << ". tablet_id: " << local_tablet->tablet_id() |
637 | 0 | << ". rowset_id: " << rowset->rowset_id() << ", txn_id=" << txn_id |
638 | 0 | << ", status=" << status.to_string(); |
639 | 0 | status.to_thrift(&tstatus); |
640 | 0 | return; |
641 | 0 | } |
642 | 0 | elapsed_time_map.emplace("calc_delete_bitmap", watch.elapsed_time_microseconds()); |
643 | 0 | } |
644 | | |
645 | 0 | static_cast<void>(BaseTablet::commit_phase_update_delete_bitmap( |
646 | 0 | local_tablet, rowset, pre_rowset_ids, delete_bitmap, segments, txn_id, |
647 | 0 | calc_delete_bitmap_token.get(), nullptr)); |
648 | 0 | elapsed_time_map.emplace("commit_phase_update_delete_bitmap", |
649 | 0 | watch.elapsed_time_microseconds()); |
650 | 0 | static_cast<void>(calc_delete_bitmap_token->wait()); |
651 | 0 | elapsed_time_map.emplace("wait_delete_bitmap", watch.elapsed_time_microseconds()); |
652 | 0 | } |
653 | | |
654 | | // Step 7.3: commit txn |
655 | 0 | Status commit_txn_status = engine.txn_manager()->commit_txn( |
656 | 0 | local_tablet->data_dir()->get_meta(), rowset_meta->partition_id(), |
657 | 0 | rowset_meta->txn_id(), rowset_meta->tablet_id(), local_tablet->tablet_uid(), |
658 | 0 | rowset_meta->load_id(), rowset, std::move(pending_rs_guard), false); |
659 | 0 | if (!commit_txn_status && !commit_txn_status.is<ErrorCode::PUSH_TRANSACTION_ALREADY_EXIST>()) { |
660 | 0 | auto err_msg = fmt::format( |
661 | 0 | "failed to commit txn for remote tablet. rowset_id: {}, remote_tablet_id={}, " |
662 | 0 | "txn_id={}, status={}", |
663 | 0 | rowset_meta->rowset_id().to_string(), rowset_meta->tablet_id(), |
664 | 0 | rowset_meta->txn_id(), commit_txn_status.to_string()); |
665 | 0 | LOG(WARNING) << err_msg; |
666 | 0 | set_tstatus(TStatusCode::RUNTIME_ERROR, std::move(err_msg)); |
667 | 0 | return; |
668 | 0 | } |
669 | 0 | elapsed_time_map.emplace("commit_txn", watch.elapsed_time_microseconds()); |
670 | |
|
671 | 0 | if (local_tablet->enable_unique_key_merge_on_write()) { |
672 | 0 | engine.txn_manager()->set_txn_related_delete_bitmap(partition_id, txn_id, local_tablet_id, |
673 | 0 | local_tablet->tablet_uid(), true, |
674 | 0 | delete_bitmap, pre_rowset_ids, nullptr); |
675 | 0 | elapsed_time_map.emplace("set_txn_related_delete_bitmap", |
676 | 0 | watch.elapsed_time_microseconds()); |
677 | 0 | } |
678 | |
|
679 | 0 | tstatus.__set_status_code(TStatusCode::OK); |
680 | 0 | } |
681 | | } // namespace |
682 | | |
683 | | BaseBackendService::BaseBackendService(ExecEnv* exec_env) |
684 | 6 | : _exec_env(exec_env), _agent_server(new AgentServer(exec_env, exec_env->cluster_info())) {} |
685 | | |
686 | 2 | BaseBackendService::~BaseBackendService() = default; |
687 | | |
688 | | BackendService::BackendService(StorageEngine& engine, ExecEnv* exec_env) |
689 | 5 | : BaseBackendService(exec_env), _engine(engine) {} |
690 | | |
691 | | BackendService::~BackendService() = default; |
692 | | |
693 | | Status BackendService::create_service(StorageEngine& engine, ExecEnv* exec_env, int port, |
694 | | std::unique_ptr<ThriftServer>* server, |
695 | 5 | std::shared_ptr<doris::BackendService> service) { |
696 | 5 | service->_agent_server->start_workers(engine, exec_env); |
697 | | // TODO: do we want a BoostThreadFactory? |
698 | | // TODO: we want separate thread factories here, so that fe requests can't starve |
699 | | // be requests |
700 | | // std::shared_ptr<TProcessor> be_processor = std::make_shared<BackendServiceProcessor>(service); |
701 | 5 | auto be_processor = std::make_shared<BackendServiceProcessor>(service); |
702 | | |
703 | 5 | *server = std::make_unique<ThriftServer>("backend", be_processor, port, |
704 | 5 | config::be_service_threads); |
705 | | |
706 | 5 | LOG(INFO) << "Doris BackendService listening on " << port; |
707 | | |
708 | 5 | auto thread_num = config::ingest_binlog_work_pool_size; |
709 | 5 | if (thread_num < 0) { |
710 | 5 | LOG(INFO) << fmt::format("ingest binlog thread pool size is {}, so we will in sync mode", |
711 | 5 | thread_num); |
712 | 5 | return Status::OK(); |
713 | 5 | } |
714 | | |
715 | 0 | if (thread_num == 0) { |
716 | 0 | thread_num = std::thread::hardware_concurrency(); |
717 | 0 | } |
718 | 0 | static_cast<void>(doris::ThreadPoolBuilder("IngestBinlog") |
719 | 0 | .set_min_threads(thread_num) |
720 | 0 | .set_max_threads(thread_num * 2) |
721 | 0 | .build(&(service->_ingest_binlog_workers))); |
722 | 0 | LOG(INFO) << fmt::format("ingest binlog thread pool size is {}, in async mode", thread_num); |
723 | 0 | return Status::OK(); |
724 | 5 | } |
725 | | |
726 | 22 | void BackendService::get_tablet_stat(TTabletStatResult& result) { |
727 | 22 | _engine.tablet_manager()->get_tablet_stat(&result); |
728 | 22 | } |
729 | | |
730 | 0 | int64_t BackendService::get_trash_used_capacity() { |
731 | 0 | int64_t result = 0; |
732 | |
|
733 | 0 | std::vector<DataDirInfo> data_dir_infos; |
734 | 0 | static_cast<void>(_engine.get_all_data_dir_info(&data_dir_infos, false /*do not update */)); |
735 | | |
736 | | // uses excute sql `show trash`, then update backend trash capacity too. |
737 | 0 | _engine.notify_listener("REPORT_DISK_STATE"); |
738 | |
|
739 | 0 | for (const auto& root_path_info : data_dir_infos) { |
740 | 0 | result += root_path_info.trash_used_capacity; |
741 | 0 | } |
742 | |
|
743 | 0 | return result; |
744 | 0 | } |
745 | | |
746 | 0 | void BackendService::get_disk_trash_used_capacity(std::vector<TDiskTrashInfo>& diskTrashInfos) { |
747 | 0 | std::vector<DataDirInfo> data_dir_infos; |
748 | 0 | static_cast<void>(_engine.get_all_data_dir_info(&data_dir_infos, false /*do not update */)); |
749 | | |
750 | | // uses excute sql `show trash on <be>`, then update backend trash capacity too. |
751 | 0 | _engine.notify_listener("REPORT_DISK_STATE"); |
752 | |
|
753 | 0 | for (const auto& root_path_info : data_dir_infos) { |
754 | 0 | TDiskTrashInfo diskTrashInfo; |
755 | 0 | diskTrashInfo.__set_root_path(root_path_info.path); |
756 | 0 | diskTrashInfo.__set_state(root_path_info.is_used ? "ONLINE" : "OFFLINE"); |
757 | 0 | diskTrashInfo.__set_trash_used_capacity(root_path_info.trash_used_capacity); |
758 | 0 | diskTrashInfos.push_back(diskTrashInfo); |
759 | 0 | } |
760 | 0 | } |
761 | | |
762 | | void BaseBackendService::submit_routine_load_task(TStatus& t_status, |
763 | 70 | const std::vector<TRoutineLoadTask>& tasks) { |
764 | 70 | for (auto& task : tasks) { |
765 | 70 | Status st = _exec_env->routine_load_task_executor()->submit_task(task); |
766 | 70 | if (!st.ok()) { |
767 | 0 | LOG(WARNING) << "failed to submit routine load task. job id: " << task.job_id |
768 | 0 | << " task id: " << task.id; |
769 | 0 | return st.to_thrift(&t_status); |
770 | 0 | } |
771 | 70 | } |
772 | | |
773 | 70 | return Status::OK().to_thrift(&t_status); |
774 | 70 | } |
775 | | |
776 | | /* |
777 | | * 1. validate user privilege (todo) |
778 | | * 2. FragmentMgr#exec_plan_fragment |
779 | | */ |
780 | 3 | void BaseBackendService::open_scanner(TScanOpenResult& result_, const TScanOpenParams& params) { |
781 | 3 | TStatus t_status; |
782 | 3 | TUniqueId fragment_instance_id = generate_uuid(); |
783 | | // A query_id is randomly generated to replace t_query_plan_info.query_id. |
784 | | // external query does not need to report anything to FE, so the query_id can be changed. |
785 | | // Otherwise, multiple independent concurrent open tablet scanners have the same query_id. |
786 | | // when one of the scanners ends, the other scanners will be canceled through FragmentMgr.cancel(query_id). |
787 | 3 | TUniqueId query_id = generate_uuid(); |
788 | 3 | std::shared_ptr<ScanContext> p_context; |
789 | 3 | static_cast<void>(_exec_env->external_scan_context_mgr()->create_scan_context(&p_context)); |
790 | 3 | p_context->fragment_instance_id = fragment_instance_id; |
791 | 3 | p_context->offset = 0; |
792 | 3 | p_context->last_access_time = time(nullptr); |
793 | 3 | if (params.__isset.keep_alive_min) { |
794 | 0 | p_context->keep_alive_min = params.keep_alive_min; |
795 | 3 | } else { |
796 | 3 | p_context->keep_alive_min = 5; |
797 | 3 | } |
798 | | |
799 | 3 | Status exec_st; |
800 | 3 | TQueryPlanInfo t_query_plan_info; |
801 | 3 | { |
802 | 3 | const std::string& opaqued_query_plan = params.opaqued_query_plan; |
803 | 3 | std::string query_plan_info; |
804 | | // base64 decode query plan |
805 | 3 | if (!base64_decode(opaqued_query_plan, &query_plan_info)) { |
806 | 0 | LOG(WARNING) << "open context error: base64_decode decode opaqued_query_plan failure"; |
807 | 0 | std::stringstream msg; |
808 | 0 | msg << "query_plan_info: " << query_plan_info |
809 | 0 | << " validate error, should not be modified after returned Doris FE processed"; |
810 | 0 | exec_st = Status::InvalidArgument(msg.str()); |
811 | 0 | } |
812 | | |
813 | 3 | const uint8_t* buf = (const uint8_t*)query_plan_info.data(); |
814 | 3 | uint32_t len = (uint32_t)query_plan_info.size(); |
815 | | // deserialize TQueryPlanInfo |
816 | 3 | auto st = deserialize_thrift_msg(buf, &len, false, &t_query_plan_info); |
817 | 3 | if (!st.ok()) { |
818 | 0 | LOG(WARNING) << "open context error: deserialize TQueryPlanInfo failure"; |
819 | 0 | std::stringstream msg; |
820 | 0 | msg << "query_plan_info: " << query_plan_info |
821 | 0 | << " deserialize error, should not be modified after returned Doris FE processed"; |
822 | 0 | exec_st = Status::InvalidArgument(msg.str()); |
823 | 0 | } |
824 | 3 | p_context->query_id = query_id; |
825 | 3 | } |
826 | 3 | std::vector<TScanColumnDesc> selected_columns; |
827 | 3 | if (exec_st.ok()) { |
828 | | // start the scan procedure |
829 | 3 | LOG(INFO) << fmt::format( |
830 | 3 | "exec external scanner, old_query_id = {}, new_query_id = {}, fragment_instance_id " |
831 | 3 | "= {}", |
832 | 3 | print_id(t_query_plan_info.query_id), print_id(query_id), |
833 | 3 | print_id(fragment_instance_id)); |
834 | 3 | exec_st = _exec_env->fragment_mgr()->exec_external_plan_fragment( |
835 | 3 | params, t_query_plan_info, query_id, fragment_instance_id, &selected_columns); |
836 | 3 | } |
837 | 3 | exec_st.to_thrift(&t_status); |
838 | | //return status |
839 | | // t_status.status_code = TStatusCode::OK; |
840 | 3 | result_.status = t_status; |
841 | 3 | result_.__set_context_id(p_context->context_id); |
842 | 3 | result_.__set_selected_columns(selected_columns); |
843 | 3 | } |
844 | | |
845 | | // fetch result from polling the queue, should always maintain the context offset, otherwise inconsistent result |
846 | 5 | void BaseBackendService::get_next(TScanBatchResult& result_, const TScanNextBatchParams& params) { |
847 | 5 | std::string context_id = params.context_id; |
848 | 5 | u_int64_t offset = params.offset; |
849 | 5 | TStatus t_status; |
850 | 5 | std::shared_ptr<ScanContext> context; |
851 | 5 | Status st = _exec_env->external_scan_context_mgr()->get_scan_context(context_id, &context); |
852 | 5 | if (!st.ok()) { |
853 | 0 | st.to_thrift(&t_status); |
854 | 0 | result_.status = t_status; |
855 | 0 | return; |
856 | 0 | } |
857 | 5 | if (offset != context->offset) { |
858 | 0 | LOG(ERROR) << "getNext error: context offset [" << context->offset << " ]" |
859 | 0 | << " ,client offset [ " << offset << " ]"; |
860 | | // invalid offset |
861 | 0 | t_status.status_code = TStatusCode::NOT_FOUND; |
862 | 0 | t_status.error_msgs.push_back( |
863 | 0 | absl::Substitute("context_id=$0, send_offset=$1, context_offset=$2", context_id, |
864 | 0 | offset, context->offset)); |
865 | 0 | result_.status = t_status; |
866 | 5 | } else { |
867 | | // during accessing, should disabled last_access_time |
868 | 5 | context->last_access_time = -1; |
869 | 5 | TUniqueId fragment_instance_id = context->fragment_instance_id; |
870 | 5 | std::shared_ptr<arrow::RecordBatch> record_batch; |
871 | 5 | bool eos; |
872 | | |
873 | 5 | st = _exec_env->result_queue_mgr()->fetch_result(fragment_instance_id, &record_batch, &eos); |
874 | 5 | if (st.ok()) { |
875 | 5 | result_.__set_eos(eos); |
876 | 5 | if (!eos) { |
877 | 3 | std::string record_batch_str; |
878 | 3 | st = serialize_record_batch(*record_batch, &record_batch_str); |
879 | 3 | st.to_thrift(&t_status); |
880 | 3 | if (st.ok()) { |
881 | | // avoid copy large string |
882 | 3 | result_.rows = std::move(record_batch_str); |
883 | | // set __isset |
884 | 3 | result_.__isset.rows = true; |
885 | 3 | context->offset += record_batch->num_rows(); |
886 | 3 | } |
887 | 3 | } |
888 | 5 | } else { |
889 | 0 | LOG(WARNING) << "fragment_instance_id [" << print_id(fragment_instance_id) |
890 | 0 | << "] fetch result status [" << st.to_string() + "]"; |
891 | 0 | st.to_thrift(&t_status); |
892 | 0 | result_.status = t_status; |
893 | 0 | } |
894 | 5 | } |
895 | 5 | context->last_access_time = time(nullptr); |
896 | 5 | } |
897 | | |
898 | 2 | void BaseBackendService::close_scanner(TScanCloseResult& result_, const TScanCloseParams& params) { |
899 | 2 | std::string context_id = params.context_id; |
900 | 2 | TStatus t_status; |
901 | 2 | Status st = _exec_env->external_scan_context_mgr()->clear_scan_context(context_id); |
902 | 2 | st.to_thrift(&t_status); |
903 | 2 | result_.status = t_status; |
904 | 2 | } |
905 | | |
906 | | void BackendService::get_stream_load_record(TStreamLoadRecordResult& result, |
907 | 10 | int64_t last_stream_record_time) { |
908 | 10 | BaseBackendService::get_stream_load_record(result, last_stream_record_time, |
909 | 10 | _engine.get_stream_load_recorder()); |
910 | 10 | } |
911 | | |
912 | 0 | void BackendService::check_storage_format(TCheckStorageFormatResult& result) { |
913 | 0 | _engine.tablet_manager()->get_all_tablets_storage_format(&result); |
914 | 0 | } |
915 | | |
916 | | void BackendService::make_snapshot(TAgentResult& return_value, |
917 | 0 | const TSnapshotRequest& snapshot_request) { |
918 | 0 | std::string snapshot_path; |
919 | 0 | bool allow_incremental_clone = false; |
920 | 0 | Status status = _engine.snapshot_mgr()->make_snapshot(snapshot_request, &snapshot_path, |
921 | 0 | &allow_incremental_clone); |
922 | 0 | if (!status) { |
923 | 0 | LOG_WARNING("failed to make snapshot") |
924 | 0 | .tag("tablet_id", snapshot_request.tablet_id) |
925 | 0 | .tag("schema_hash", snapshot_request.schema_hash) |
926 | 0 | .error(status); |
927 | 0 | } else { |
928 | 0 | LOG_INFO("successfully make snapshot") |
929 | 0 | .tag("tablet_id", snapshot_request.tablet_id) |
930 | 0 | .tag("schema_hash", snapshot_request.schema_hash) |
931 | 0 | .tag("snapshot_path", snapshot_path); |
932 | 0 | return_value.__set_snapshot_path(snapshot_path); |
933 | 0 | return_value.__set_allow_incremental_clone(allow_incremental_clone); |
934 | 0 | } |
935 | |
|
936 | 0 | status.to_thrift(&return_value.status); |
937 | 0 | return_value.__set_snapshot_version(snapshot_request.preferred_snapshot_version); |
938 | 0 | } |
939 | | |
940 | | void BackendService::release_snapshot(TAgentResult& return_value, |
941 | 0 | const std::string& snapshot_path) { |
942 | 0 | Status status = _engine.snapshot_mgr()->release_snapshot(snapshot_path); |
943 | 0 | if (!status) { |
944 | 0 | LOG_WARNING("failed to release snapshot").tag("snapshot_path", snapshot_path).error(status); |
945 | 0 | } else { |
946 | 0 | LOG_INFO("successfully release snapshot").tag("snapshot_path", snapshot_path); |
947 | 0 | } |
948 | 0 | status.to_thrift(&return_value.status); |
949 | 0 | } |
950 | | |
951 | | void BackendService::ingest_binlog(TIngestBinlogResult& result, |
952 | 0 | const TIngestBinlogRequest& request) { |
953 | 0 | LOG(INFO) << "ingest binlog. request: " << apache::thrift::ThriftDebugString(request); |
954 | |
|
955 | 0 | TStatus tstatus; |
956 | 0 | Defer defer {[&result, &tstatus]() { |
957 | 0 | result.__set_status(tstatus); |
958 | 0 | LOG(INFO) << "ingest binlog. result: " << apache::thrift::ThriftDebugString(result); |
959 | 0 | }}; |
960 | |
|
961 | 0 | auto set_tstatus = [&tstatus](TStatusCode::type code, std::string error_msg) { |
962 | 0 | tstatus.__set_status_code(code); |
963 | 0 | tstatus.__isset.error_msgs = true; |
964 | 0 | tstatus.error_msgs.push_back(std::move(error_msg)); |
965 | 0 | }; |
966 | |
|
967 | 0 | if (!config::enable_feature_binlog) { |
968 | 0 | set_tstatus(TStatusCode::RUNTIME_ERROR, "enable feature binlog is false"); |
969 | 0 | return; |
970 | 0 | } |
971 | | |
972 | | /// Check args: txn_id, remote_tablet_id, binlog_version, remote_host, remote_port, partition_id, load_id |
973 | 0 | if (!request.__isset.txn_id) { |
974 | 0 | auto error_msg = "txn_id is empty"; |
975 | 0 | LOG(WARNING) << error_msg; |
976 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
977 | 0 | return; |
978 | 0 | } |
979 | 0 | if (!request.__isset.remote_tablet_id) { |
980 | 0 | auto error_msg = "remote_tablet_id is empty"; |
981 | 0 | LOG(WARNING) << error_msg; |
982 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
983 | 0 | return; |
984 | 0 | } |
985 | 0 | if (!request.__isset.binlog_version) { |
986 | 0 | auto error_msg = "binlog_version is empty"; |
987 | 0 | LOG(WARNING) << error_msg; |
988 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
989 | 0 | return; |
990 | 0 | } |
991 | 0 | if (!request.__isset.remote_host) { |
992 | 0 | auto error_msg = "remote_host is empty"; |
993 | 0 | LOG(WARNING) << error_msg; |
994 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
995 | 0 | return; |
996 | 0 | } |
997 | 0 | if (!request.__isset.remote_port) { |
998 | 0 | auto error_msg = "remote_port is empty"; |
999 | 0 | LOG(WARNING) << error_msg; |
1000 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
1001 | 0 | return; |
1002 | 0 | } |
1003 | 0 | if (!request.__isset.partition_id) { |
1004 | 0 | auto error_msg = "partition_id is empty"; |
1005 | 0 | LOG(WARNING) << error_msg; |
1006 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
1007 | 0 | return; |
1008 | 0 | } |
1009 | 0 | if (!request.__isset.local_tablet_id) { |
1010 | 0 | auto error_msg = "local_tablet_id is empty"; |
1011 | 0 | LOG(WARNING) << error_msg; |
1012 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
1013 | 0 | return; |
1014 | 0 | } |
1015 | 0 | if (!request.__isset.load_id) { |
1016 | 0 | auto error_msg = "load_id is empty"; |
1017 | 0 | LOG(WARNING) << error_msg; |
1018 | 0 | set_tstatus(TStatusCode::ANALYSIS_ERROR, error_msg); |
1019 | 0 | return; |
1020 | 0 | } |
1021 | | |
1022 | 0 | auto txn_id = request.txn_id; |
1023 | | // Step 1: get local tablet |
1024 | 0 | auto const& local_tablet_id = request.local_tablet_id; |
1025 | 0 | auto local_tablet = _engine.tablet_manager()->get_tablet(local_tablet_id); |
1026 | 0 | if (local_tablet == nullptr) { |
1027 | 0 | auto error_msg = fmt::format("tablet {} not found", local_tablet_id); |
1028 | 0 | LOG(WARNING) << error_msg; |
1029 | 0 | set_tstatus(TStatusCode::TABLET_MISSING, std::move(error_msg)); |
1030 | 0 | return; |
1031 | 0 | } |
1032 | | |
1033 | | // Step 2: check txn, create txn, prepare_txn will check it |
1034 | 0 | auto partition_id = request.partition_id; |
1035 | 0 | auto& load_id = request.load_id; |
1036 | 0 | auto is_ingrest = true; |
1037 | 0 | PUniqueId p_load_id; |
1038 | 0 | p_load_id.set_hi(load_id.hi); |
1039 | 0 | p_load_id.set_lo(load_id.lo); |
1040 | |
|
1041 | 0 | { |
1042 | | // TODO: Before push_lock is not held, but I think it should hold. |
1043 | 0 | auto status = local_tablet->prepare_txn(partition_id, txn_id, p_load_id, is_ingrest); |
1044 | 0 | if (!status.ok()) { |
1045 | 0 | LOG(WARNING) << "prepare txn failed. txn_id=" << txn_id |
1046 | 0 | << ", status=" << status.to_string(); |
1047 | 0 | status.to_thrift(&tstatus); |
1048 | 0 | return; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | 0 | bool is_async = (_ingest_binlog_workers != nullptr); |
1053 | 0 | result.__set_is_async(is_async); |
1054 | |
|
1055 | 0 | auto ingest_binlog_func = [=, this, tstatus = &tstatus]() { |
1056 | 0 | IngestBinlogArg ingest_binlog_arg = { |
1057 | 0 | .txn_id = txn_id, |
1058 | 0 | .partition_id = partition_id, |
1059 | 0 | .local_tablet_id = local_tablet_id, |
1060 | 0 | .local_tablet = local_tablet, |
1061 | |
|
1062 | 0 | .request = request, |
1063 | 0 | .tstatus = is_async ? nullptr : tstatus, |
1064 | 0 | }; |
1065 | |
|
1066 | 0 | _ingest_binlog(_engine, &ingest_binlog_arg); |
1067 | 0 | }; |
1068 | |
|
1069 | 0 | if (is_async) { |
1070 | 0 | auto status = _ingest_binlog_workers->submit_func(std::move(ingest_binlog_func)); |
1071 | 0 | if (!status.ok()) { |
1072 | 0 | status.to_thrift(&tstatus); |
1073 | 0 | return; |
1074 | 0 | } |
1075 | 0 | } else { |
1076 | 0 | ingest_binlog_func(); |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | | void BackendService::query_ingest_binlog(TQueryIngestBinlogResult& result, |
1081 | 0 | const TQueryIngestBinlogRequest& request) { |
1082 | 0 | LOG(INFO) << "query ingest binlog. request: " << apache::thrift::ThriftDebugString(request); |
1083 | |
|
1084 | 0 | auto set_result = [&](TIngestBinlogStatus::type status, std::string error_msg) { |
1085 | 0 | result.__set_status(status); |
1086 | 0 | result.__set_err_msg(std::move(error_msg)); |
1087 | 0 | }; |
1088 | | |
1089 | | /// Check args: txn_id, partition_id, tablet_id, load_id |
1090 | 0 | if (!request.__isset.txn_id) { |
1091 | 0 | auto error_msg = "txn_id is empty"; |
1092 | 0 | LOG(WARNING) << error_msg; |
1093 | 0 | set_result(TIngestBinlogStatus::ANALYSIS_ERROR, error_msg); |
1094 | 0 | return; |
1095 | 0 | } |
1096 | 0 | if (!request.__isset.partition_id) { |
1097 | 0 | auto error_msg = "partition_id is empty"; |
1098 | 0 | LOG(WARNING) << error_msg; |
1099 | 0 | set_result(TIngestBinlogStatus::ANALYSIS_ERROR, error_msg); |
1100 | 0 | return; |
1101 | 0 | } |
1102 | 0 | if (!request.__isset.tablet_id) { |
1103 | 0 | auto error_msg = "tablet_id is empty"; |
1104 | 0 | LOG(WARNING) << error_msg; |
1105 | 0 | set_result(TIngestBinlogStatus::ANALYSIS_ERROR, error_msg); |
1106 | 0 | return; |
1107 | 0 | } |
1108 | 0 | if (!request.__isset.load_id) { |
1109 | 0 | auto error_msg = "load_id is empty"; |
1110 | 0 | LOG(WARNING) << error_msg; |
1111 | 0 | set_result(TIngestBinlogStatus::ANALYSIS_ERROR, error_msg); |
1112 | 0 | return; |
1113 | 0 | } |
1114 | | |
1115 | 0 | auto partition_id = request.partition_id; |
1116 | 0 | auto txn_id = request.txn_id; |
1117 | 0 | auto tablet_id = request.tablet_id; |
1118 | | |
1119 | | // Step 1: get local tablet |
1120 | 0 | auto local_tablet = _engine.tablet_manager()->get_tablet(tablet_id); |
1121 | 0 | if (local_tablet == nullptr) { |
1122 | 0 | auto error_msg = fmt::format("tablet {} not found", tablet_id); |
1123 | 0 | LOG(WARNING) << error_msg; |
1124 | 0 | set_result(TIngestBinlogStatus::NOT_FOUND, std::move(error_msg)); |
1125 | 0 | return; |
1126 | 0 | } |
1127 | | |
1128 | | // Step 2: get txn state |
1129 | 0 | auto tablet_uid = local_tablet->tablet_uid(); |
1130 | 0 | auto txn_state = |
1131 | 0 | _engine.txn_manager()->get_txn_state(partition_id, txn_id, tablet_id, tablet_uid); |
1132 | 0 | switch (txn_state) { |
1133 | 0 | case TxnState::NOT_FOUND: |
1134 | 0 | result.__set_status(TIngestBinlogStatus::NOT_FOUND); |
1135 | 0 | break; |
1136 | 0 | case TxnState::PREPARED: |
1137 | 0 | result.__set_status(TIngestBinlogStatus::DOING); |
1138 | 0 | break; |
1139 | 0 | case TxnState::COMMITTED: |
1140 | 0 | result.__set_status(TIngestBinlogStatus::OK); |
1141 | 0 | break; |
1142 | 0 | case TxnState::ROLLEDBACK: |
1143 | 0 | result.__set_status(TIngestBinlogStatus::FAILED); |
1144 | 0 | break; |
1145 | 0 | case TxnState::ABORTED: |
1146 | 0 | result.__set_status(TIngestBinlogStatus::FAILED); |
1147 | 0 | break; |
1148 | 0 | case TxnState::DELETED: |
1149 | 0 | result.__set_status(TIngestBinlogStatus::FAILED); |
1150 | 0 | break; |
1151 | 0 | } |
1152 | 0 | } |
1153 | | |
1154 | 0 | void BaseBackendService::get_tablet_stat(TTabletStatResult& result) { |
1155 | 0 | LOG(ERROR) << "get_tablet_stat is not implemented"; |
1156 | 0 | } |
1157 | | |
1158 | 3 | int64_t BaseBackendService::get_trash_used_capacity() { |
1159 | 3 | LOG(ERROR) << "get_trash_used_capacity is not implemented"; |
1160 | 3 | return 0; |
1161 | 3 | } |
1162 | | |
1163 | | void BaseBackendService::get_stream_load_record(TStreamLoadRecordResult& result, |
1164 | 0 | int64_t last_stream_record_time) { |
1165 | 0 | LOG(ERROR) << "get_stream_load_record is not implemented"; |
1166 | 0 | } |
1167 | | |
1168 | | void BaseBackendService::get_stream_load_record( |
1169 | | TStreamLoadRecordResult& result, int64_t last_stream_record_time, |
1170 | 38 | std::shared_ptr<StreamLoadRecorder> stream_load_recorder) { |
1171 | 38 | if (stream_load_recorder != nullptr) { |
1172 | 38 | std::map<std::string, std::string> records; |
1173 | 38 | auto st = stream_load_recorder->get_batch(std::to_string(last_stream_record_time), |
1174 | 38 | config::stream_load_record_batch_size, &records); |
1175 | 38 | if (st.ok()) { |
1176 | 38 | LOG(INFO) << "get_batch stream_load_record rocksdb successfully. records size: " |
1177 | 38 | << records.size() |
1178 | 38 | << ", last_stream_load_timestamp: " << last_stream_record_time; |
1179 | 38 | std::map<std::string, TStreamLoadRecord> stream_load_record_batch; |
1180 | 38 | auto it = records.begin(); |
1181 | 2.13k | for (; it != records.end(); ++it) { |
1182 | 2.09k | TStreamLoadRecord stream_load_item; |
1183 | 2.09k | StreamLoadContext::parse_stream_load_record(it->second, stream_load_item); |
1184 | 2.09k | stream_load_record_batch.emplace(it->first.c_str(), stream_load_item); |
1185 | 2.09k | } |
1186 | 38 | result.__set_stream_load_record(stream_load_record_batch); |
1187 | 38 | } |
1188 | 38 | } else { |
1189 | 0 | LOG(WARNING) << "stream_load_recorder is null."; |
1190 | 0 | } |
1191 | 38 | } |
1192 | | |
1193 | 0 | void BaseBackendService::get_disk_trash_used_capacity(std::vector<TDiskTrashInfo>& diskTrashInfos) { |
1194 | 0 | LOG(ERROR) << "get_disk_trash_used_capacity is not implemented"; |
1195 | 0 | } |
1196 | | |
1197 | | void BaseBackendService::make_snapshot(TAgentResult& return_value, |
1198 | 0 | const TSnapshotRequest& snapshot_request) { |
1199 | 0 | LOG(ERROR) << "make_snapshot is not implemented"; |
1200 | 0 | return_value.__set_status(Status::NotSupported("make_snapshot is not implemented").to_thrift()); |
1201 | 0 | } |
1202 | | |
1203 | | void BaseBackendService::release_snapshot(TAgentResult& return_value, |
1204 | 0 | const std::string& snapshot_path) { |
1205 | 0 | LOG(ERROR) << "release_snapshot is not implemented"; |
1206 | 0 | return_value.__set_status( |
1207 | 0 | Status::NotSupported("release_snapshot is not implemented").to_thrift()); |
1208 | 0 | } |
1209 | | |
1210 | 0 | void BaseBackendService::check_storage_format(TCheckStorageFormatResult& result) { |
1211 | 0 | LOG(ERROR) << "check_storage_format is not implemented"; |
1212 | 0 | } |
1213 | | |
1214 | | void BaseBackendService::ingest_binlog(TIngestBinlogResult& result, |
1215 | 0 | const TIngestBinlogRequest& request) { |
1216 | 0 | LOG(ERROR) << "ingest_binlog is not implemented"; |
1217 | 0 | result.__set_status(Status::NotSupported("ingest_binlog is not implemented").to_thrift()); |
1218 | 0 | } |
1219 | | |
1220 | | void BaseBackendService::query_ingest_binlog(TQueryIngestBinlogResult& result, |
1221 | 0 | const TQueryIngestBinlogRequest& request) { |
1222 | 0 | LOG(ERROR) << "query_ingest_binlog is not implemented"; |
1223 | 0 | result.__set_status(TIngestBinlogStatus::UNKNOWN); |
1224 | 0 | result.__set_err_msg("query_ingest_binlog is not implemented"); |
1225 | 0 | } |
1226 | | |
1227 | | void BaseBackendService::warm_up_cache_async(TWarmUpCacheAsyncResponse& response, |
1228 | 0 | const TWarmUpCacheAsyncRequest& request) { |
1229 | 0 | LOG(ERROR) << "warm_up_cache_async is not implemented"; |
1230 | 0 | response.__set_status( |
1231 | 0 | Status::NotSupported("warm_up_cache_async is not implemented").to_thrift()); |
1232 | 0 | } |
1233 | | |
1234 | | void BaseBackendService::check_warm_up_cache_async(TCheckWarmUpCacheAsyncResponse& response, |
1235 | 0 | const TCheckWarmUpCacheAsyncRequest& request) { |
1236 | 0 | LOG(ERROR) << "check_warm_up_cache_async is not implemented"; |
1237 | 0 | response.__set_status( |
1238 | 0 | Status::NotSupported("check_warm_up_cache_async is not implemented").to_thrift()); |
1239 | 0 | } |
1240 | | |
1241 | | void BaseBackendService::sync_load_for_tablets(TSyncLoadForTabletsResponse& response, |
1242 | 0 | const TSyncLoadForTabletsRequest& request) { |
1243 | 0 | LOG(ERROR) << "sync_load_for_tablets is not implemented"; |
1244 | 0 | } |
1245 | | |
1246 | | void BaseBackendService::get_top_n_hot_partitions(TGetTopNHotPartitionsResponse& response, |
1247 | 0 | const TGetTopNHotPartitionsRequest& request) { |
1248 | 0 | LOG(ERROR) << "get_top_n_hot_partitions is not implemented"; |
1249 | 0 | } |
1250 | | |
1251 | | void BaseBackendService::warm_up_tablets(TWarmUpTabletsResponse& response, |
1252 | 0 | const TWarmUpTabletsRequest& request) { |
1253 | 0 | LOG(ERROR) << "warm_up_tablets is not implemented"; |
1254 | 0 | response.__set_status(Status::NotSupported("warm_up_tablets is not implemented").to_thrift()); |
1255 | 0 | } |
1256 | | |
1257 | | void BaseBackendService::get_realtime_exec_status(TGetRealtimeExecStatusResponse& response, |
1258 | 1 | const TGetRealtimeExecStatusRequest& request) { |
1259 | 1 | if (!request.__isset.id) { |
1260 | 0 | LOG_WARNING("Invalidate argument, id is empty"); |
1261 | 0 | response.__set_status(Status::InvalidArgument("id is empty").to_thrift()); |
1262 | 0 | return; |
1263 | 0 | } |
1264 | | |
1265 | 1 | RuntimeProfile::Counter get_realtime_timer {TUnit::TIME_NS}; |
1266 | | |
1267 | 1 | Defer _print_log([&]() { |
1268 | 1 | LOG_INFO("Getting realtime exec status of query {} , cost time {}", print_id(request.id), |
1269 | 1 | PrettyPrinter::print(get_realtime_timer.value(), get_realtime_timer.type())); |
1270 | 1 | }); |
1271 | | |
1272 | 1 | SCOPED_TIMER(&get_realtime_timer); |
1273 | | |
1274 | 1 | std::unique_ptr<TReportExecStatusParams> report_exec_status_params = |
1275 | 1 | std::make_unique<TReportExecStatusParams>(); |
1276 | 1 | std::unique_ptr<TQueryStatistics> query_stats = std::make_unique<TQueryStatistics>(); |
1277 | | |
1278 | 1 | std::string req_type = request.__isset.req_type ? request.req_type : "profile"; |
1279 | 1 | Status st; |
1280 | 1 | if (req_type == "stats") { |
1281 | 1 | st = ExecEnv::GetInstance()->fragment_mgr()->get_query_statistics(request.id, |
1282 | 1 | query_stats.get()); |
1283 | 1 | if (st.ok()) { |
1284 | 1 | response.__set_query_stats(*query_stats); |
1285 | 1 | } |
1286 | 1 | } else { |
1287 | | // default is "profile" |
1288 | 0 | st = ExecEnv::GetInstance()->fragment_mgr()->get_realtime_exec_status( |
1289 | 0 | request.id, report_exec_status_params.get()); |
1290 | 0 | if (st.ok()) { |
1291 | 0 | response.__set_report_exec_status_params(*report_exec_status_params); |
1292 | 0 | } |
1293 | 0 | } |
1294 | | |
1295 | 1 | report_exec_status_params->__set_query_id(TUniqueId()); |
1296 | 1 | report_exec_status_params->__set_done(false); |
1297 | 1 | response.__set_status(st.to_thrift()); |
1298 | 1 | } |
1299 | | |
1300 | | void BaseBackendService::get_dictionary_status(TDictionaryStatusList& result, |
1301 | 995 | const std::vector<int64_t>& dictionary_ids) { |
1302 | 995 | std::vector<TDictionaryStatus> dictionary_status; |
1303 | 995 | ExecEnv::GetInstance()->dict_factory()->get_dictionary_status(dictionary_status, |
1304 | 995 | dictionary_ids); |
1305 | 995 | result.__set_dictionary_status_list(dictionary_status); |
1306 | 995 | LOG(INFO) << "query for dictionary status, return " << result.dictionary_status_list.size() |
1307 | 995 | << " rows"; |
1308 | 995 | } |
1309 | | |
1310 | | void BaseBackendService::test_storage_connectivity(TTestStorageConnectivityResponse& response, |
1311 | 0 | const TTestStorageConnectivityRequest& request) { |
1312 | 0 | Status status = io::StorageConnectivityTester::test(request.type, request.properties); |
1313 | 0 | response.__set_status(status.to_thrift()); |
1314 | 0 | } |
1315 | | |
1316 | 2 | void BaseBackendService::get_python_envs(std::vector<TPythonEnvInfo>& result) { |
1317 | 2 | result = PythonVersionManager::instance().env_infos_to_thrift(); |
1318 | 2 | } |
1319 | | |
1320 | | void BaseBackendService::get_python_packages(std::vector<TPythonPackageInfo>& result, |
1321 | 1 | const std::string& python_version) { |
1322 | 1 | PythonVersion version; |
1323 | 1 | auto& manager = PythonVersionManager::instance(); |
1324 | 1 | THROW_IF_ERROR(manager.get_version(python_version, &version)); |
1325 | | |
1326 | 1 | std::vector<std::pair<std::string, std::string>> packages; |
1327 | 1 | THROW_IF_ERROR(list_installed_packages(version, &packages)); |
1328 | 1 | result = manager.package_infos_to_thrift(packages); |
1329 | 1 | } |
1330 | | |
1331 | | #include "common/compile_check_end.h" |
1332 | | } // namespace doris |