| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| // This file is copied from | |
| // https://github.com/apache/impala/blob/branch-2.9.0/be/src/runtime/runtime-state.cpp | |
| // and modified by Doris | |
| #include "runtime/runtime_state.h" | |
| #include <fmt/format.h> | |
| #include <gen_cpp/PaloInternalService_types.h> | |
| #include <gen_cpp/Types_types.h> | |
| #include <glog/logging.h> | |
| #include <fstream> | |
| #include <memory> | |
| #include <string> | |
| #include "cloud/cloud_storage_engine.h" | |
| #include "cloud/config.h" | |
| #include "common/config.h" | |
| #include "common/logging.h" | |
| #include "common/object_pool.h" | |
| #include "common/status.h" | |
| #include "core/value/vdatetime_value.h" | |
| #include "exec/operator/operator.h" | |
| #include "exec/pipeline/pipeline_fragment_context.h" | |
| #include "exec/pipeline/pipeline_task.h" | |
| #include "exec/runtime_filter/runtime_filter_consumer.h" | |
| #include "exec/runtime_filter/runtime_filter_mgr.h" | |
| #include "exec/runtime_filter/runtime_filter_producer.h" | |
| #include "exprs/function/cast/cast_to_date_or_datetime_impl.hpp" | |
| #include "io/fs/s3_file_system.h" | |
| #include "load/load_path_mgr.h" | |
| #include "runtime/exec_env.h" | |
| #include "runtime/fragment_mgr.h" | |
| #include "runtime/memory/mem_tracker_limiter.h" | |
| #include "runtime/query_context.h" | |
| #include "runtime/thread_context.h" | |
| #include "storage/id_manager.h" | |
| #include "storage/storage_engine.h" | |
| #include "util/thrift_util.h" | |
| #include "util/timezone_utils.h" | |
| #include "util/uid_util.h" | |
| namespace doris { | |
| ExternalFileReportReservation::~ExternalFileReportReservation() { | |
| if (_state != nullptr) { | |
| _state->release_external_file_report_reservation(_reserved_bytes); | |
| } | |
| } | |
| using namespace ErrorCode; | |
| Status RuntimeState::add_iceberg_commit_datas(TIcebergCommitData iceberg_commit_data) { | |
| ThriftSerializer serializer(false, 256); | |
| uint32_t serialized_size = 0; | |
| uint8_t* buffer = nullptr; | |
| RETURN_IF_ERROR(serializer.serialize(&iceberg_commit_data, &serialized_size, &buffer)); | |
| // This is an early per-vector guard only; the assembled RPC is measured again before send. | |
| constexpr size_t report_envelope_headroom = 1024 * 1024; | |
| const size_t thrift_limit = coordinator_thrift_message_limit(); | |
| const size_t commit_data_limit = | |
| thrift_limit > report_envelope_headroom ? thrift_limit - report_envelope_headroom : 0; | |
| std::lock_guard<std::mutex> budget_lock(_external_file_report_state->mutex); | |
| // Parallel task states share this budget because FE receives their vectors in one fragment report. | |
| if (_external_file_report_state->serialized_commit_bytes + serialized_size + sizeof(uint32_t) > | |
| commit_data_limit) { | |
| return Status::InternalError( | |
| "Iceberg commit metadata exceeds the Thrift report limit; reduce output file " | |
| "count"); | |
| } | |
| std::lock_guard<std::mutex> data_lock(_iceberg_commit_datas_mutex); | |
| _external_file_report_state->serialized_commit_bytes += serialized_size + sizeof(uint32_t); | |
| _iceberg_commit_datas.emplace_back(std::move(iceberg_commit_data)); | |
| return Status::OK(); | |
| } | |
| Status RuntimeState::reserve_paimon_commit_messages( | |
| const std::vector<TPaimonCommitMessage>& commit_messages, | |
| ExternalFileReportReservation* reservation) { | |
| DORIS_CHECK(reservation != nullptr); | |
| DORIS_CHECK(reservation->_state == nullptr); | |
| size_t serialized_bytes = 0; | |
| ThriftSerializer serializer(false, 256); | |
| for (const auto& commit_message : commit_messages) { | |
| uint32_t serialized_size = 0; | |
| uint8_t* buffer = nullptr; | |
| RETURN_IF_ERROR(serializer.serialize(&commit_message, &serialized_size, &buffer)); | |
| serialized_bytes += serialized_size + sizeof(uint32_t); | |
| } | |
| constexpr size_t report_envelope_headroom = 1024 * 1024; | |
| const size_t thrift_limit = coordinator_thrift_message_limit(); | |
| const size_t commit_data_limit = | |
| thrift_limit > report_envelope_headroom ? thrift_limit - report_envelope_headroom : 0; | |
| std::lock_guard<std::mutex> budget_lock(_external_file_report_state->mutex); | |
| if (_external_file_report_state->serialized_commit_bytes + serialized_bytes > | |
| commit_data_limit) { | |
| return Status::InternalError( | |
| "Paimon commit metadata exceeds the Thrift report limit; reduce output file " | |
| "count"); | |
| } | |
| _external_file_report_state->serialized_commit_bytes += serialized_bytes; | |
| reservation->_state = this; | |
| reservation->_reserved_bytes = serialized_bytes; | |
| return Status::OK(); | |
| } | |
| void RuntimeState::publish_reserved_paimon_commit_messages( | |
| std::vector<TPaimonCommitMessage> commit_messages, | |
| ExternalFileReportReservation* reservation) { | |
| DORIS_CHECK(reservation != nullptr); | |
| DORIS_CHECK(reservation->_state == this); | |
| std::lock_guard<std::mutex> data_lock(_paimon_commit_messages_mutex); | |
| for (auto& commit_message : commit_messages) { | |
| _paimon_commit_messages.emplace_back(std::move(commit_message)); | |
| } | |
| reservation->_state = nullptr; | |
| reservation->_reserved_bytes = 0; | |
| } | |
| void RuntimeState::release_external_file_report_reservation(size_t reserved_bytes) { | |
| std::lock_guard<std::mutex> budget_lock(_external_file_report_state->mutex); | |
| DORIS_CHECK_GE(_external_file_report_state->serialized_commit_bytes, reserved_bytes); | |
| _external_file_report_state->serialized_commit_bytes -= reserved_bytes; | |
| } | |
| size_t RuntimeState::coordinator_thrift_message_limit() const { | |
| int32_t effective_thrift_limit = std::max(config::thrift_max_message_size, 0); | |
| if (_query_options.__isset.coordinator_thrift_max_message_size && | |
| _query_options.coordinator_thrift_max_message_size > 0) { | |
| // An older FE omits this field; otherwise the receiver's smaller limit is authoritative. | |
| effective_thrift_limit = std::min(effective_thrift_limit, | |
| _query_options.coordinator_thrift_max_message_size); | |
| } | |
| return static_cast<size_t>(effective_thrift_limit); | |
| } | |
| void RuntimeState::append_external_file_commit_data(TReportExecStatusParams* params, | |
| bool final_report) const { | |
| if (!final_report) { | |
| // Ownership-bearing commit vectors must only appear in the final report that transfers them. | |
| return; | |
| } | |
| if (auto updates = hive_partition_updates(); !updates.empty()) { | |
| params->__isset.hive_partition_updates = true; | |
| params->hive_partition_updates.insert(params->hive_partition_updates.end(), updates.begin(), | |
| updates.end()); | |
| } | |
| append_iceberg_commit_datas(¶ms->iceberg_commit_datas); | |
| if (!params->iceberg_commit_datas.empty()) { | |
| params->__isset.iceberg_commit_datas = true; | |
| } | |
| if (auto commit_datas = mc_commit_datas(); !commit_datas.empty()) { | |
| params->__isset.mc_commit_datas = true; | |
| params->mc_commit_datas.insert(params->mc_commit_datas.end(), commit_datas.begin(), | |
| commit_datas.end()); | |
| } | |
| append_paimon_commit_messages(¶ms->paimon_commit_messages); | |
| if (!params->paimon_commit_messages.empty()) { | |
| params->__isset.paimon_commit_messages = true; | |
| } | |
| } | |
| void RuntimeState::add_rejected_external_file_report_cleanup(std::function<void()> cleanup) { | |
| std::lock_guard lock(_external_file_report_state->mutex); | |
| _external_file_report_state->rejected_report_cleanups.emplace_back(std::move(cleanup)); | |
| } | |
| void RuntimeState::finalize_external_file_report_cleanup(ExternalFileReportOutcome outcome) { | |
| std::vector<std::function<void()>> cleanups; | |
| { | |
| std::lock_guard lock(_external_file_report_state->mutex); | |
| if (outcome == ExternalFileReportOutcome::ACKNOWLEDGED) { | |
| _external_file_report_state->rejected_report_cleanups.clear(); | |
| return; | |
| } | |
| if (outcome == ExternalFileReportOutcome::AMBIGUOUS) { | |
| // Once an ACK can have been lost, a later rejection cannot prove FE never accepted the files. | |
| _external_file_report_state->ownership_may_have_transferred = true; | |
| return; | |
| } | |
| if (_external_file_report_state->ownership_may_have_transferred) { | |
| return; | |
| } | |
| cleanups.swap(_external_file_report_state->rejected_report_cleanups); | |
| } | |
| for (auto& cleanup : cleanups) { | |
| cleanup(); | |
| } | |
| } | |
| RuntimeState::RuntimeState(const TPlanFragmentExecParams& fragment_exec_params, | |
| const TQueryOptions& query_options, const TQueryGlobals& query_globals, | |
| ExecEnv* exec_env, QueryContext* ctx, | |
| const std::shared_ptr<MemTrackerLimiter>& query_mem_tracker) | |
| : _profile("Fragment " + print_id(fragment_exec_params.fragment_instance_id)), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _query_id(fragment_exec_params.query_id), | |
| _per_fragment_instance_idx(0), | |
| _num_rows_load_total(0), | |
| _num_rows_load_filtered(0), | |
| _num_rows_load_unselected(0), | |
| _num_print_error_rows(0), | |
| _num_bytes_load_total(0), | |
| _num_finished_scan_range(0), | |
| _query_ctx(ctx) { | |
| Status status = | |
| init(fragment_exec_params.fragment_instance_id, query_options, query_globals, exec_env); | |
| DCHECK(status.ok()); | |
| _query_mem_tracker = query_mem_tracker; | |
| DCHECK(_query_mem_tracker != nullptr); | |
| } | |
| RuntimeState::RuntimeState(const TUniqueId& instance_id, const TUniqueId& query_id, | |
| int32_t fragment_id, const TQueryOptions& query_options, | |
| const TQueryGlobals& query_globals, ExecEnv* exec_env, QueryContext* ctx) | |
| : _profile("Fragment " + print_id(instance_id)), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _query_id(query_id), | |
| _fragment_id(fragment_id), | |
| _per_fragment_instance_idx(0), | |
| _num_rows_load_total(0), | |
| _num_rows_load_filtered(0), | |
| _num_rows_load_unselected(0), | |
| _num_rows_filtered_in_strict_mode_partial_update(0), | |
| _num_print_error_rows(0), | |
| _num_bytes_load_total(0), | |
| _num_finished_scan_range(0), | |
| _query_ctx(ctx) { | |
| [[maybe_unused]] auto status = init(instance_id, query_options, query_globals, exec_env); | |
| DCHECK(status.ok()); | |
| _query_mem_tracker = ctx->query_mem_tracker(); | |
| } | |
| RuntimeState::RuntimeState(const TUniqueId& query_id, int32_t fragment_id, | |
| const TQueryOptions& query_options, const TQueryGlobals& query_globals, | |
| ExecEnv* exec_env, QueryContext* ctx) | |
| : _profile(fmt::format("PipelineX(fragment_id={})", fragment_id)), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _query_id(query_id), | |
| _fragment_id(fragment_id), | |
| _per_fragment_instance_idx(0), | |
| _num_rows_load_total(0), | |
| _num_rows_load_filtered(0), | |
| _num_rows_load_unselected(0), | |
| _num_rows_filtered_in_strict_mode_partial_update(0), | |
| _num_print_error_rows(0), | |
| _num_bytes_load_total(0), | |
| _num_finished_scan_range(0), | |
| _query_ctx(ctx) { | |
| // TODO: do we really need instance id? | |
| Status status = init(TUniqueId(), query_options, query_globals, exec_env); | |
| DCHECK(status.ok()); | |
| _query_mem_tracker = ctx->query_mem_tracker(); | |
| } | |
| RuntimeState::RuntimeState(const TUniqueId& query_id, int32_t fragment_id, | |
| const TQueryOptions& query_options, const TQueryGlobals& query_globals, | |
| ExecEnv* exec_env, | |
| const std::shared_ptr<MemTrackerLimiter>& query_mem_tracker) | |
| : _profile(fmt::format("PipelineX(fragment_id={})", fragment_id)), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _query_id(query_id), | |
| _fragment_id(fragment_id), | |
| _per_fragment_instance_idx(0), | |
| _num_rows_load_total(0), | |
| _num_rows_load_filtered(0), | |
| _num_rows_load_unselected(0), | |
| _num_rows_filtered_in_strict_mode_partial_update(0), | |
| _num_print_error_rows(0), | |
| _num_bytes_load_total(0), | |
| _num_finished_scan_range(0) { | |
| Status status = init(TUniqueId(), query_options, query_globals, exec_env); | |
| DCHECK(status.ok()); | |
| _query_mem_tracker = query_mem_tracker; | |
| DCHECK(_query_mem_tracker != nullptr); | |
| } | |
| RuntimeState::RuntimeState(const TQueryOptions& query_options, const TQueryGlobals& query_globals) | |
| : _profile("<unnamed>"), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _per_fragment_instance_idx(0) { | |
| Status status = init(TUniqueId(), query_options, query_globals, nullptr); | |
| _exec_env = ExecEnv::GetInstance(); | |
| init_mem_trackers("<unnamed>"); | |
| DCHECK(status.ok()); | |
| } | |
| RuntimeState::RuntimeState() | |
| : _profile("<unnamed>"), | |
| _load_channel_profile("<unnamed>"), | |
| _obj_pool(new ObjectPool()), | |
| _unreported_error_idx(0), | |
| _per_fragment_instance_idx(0) { | |
| _query_options.batch_size = DEFAULT_BATCH_SIZE; | |
| _query_options.be_exec_version = BeExecVersionManager::get_newest_version(); | |
| _timezone = TimezoneUtils::default_time_zone; | |
| _timestamp_ms = 0; | |
| _nano_seconds = 0; | |
| TimezoneUtils::find_cctz_time_zone(_timezone, _timezone_obj); | |
| _exec_env = ExecEnv::GetInstance(); | |
| init_mem_trackers("<unnamed>"); | |
| } | |
| RuntimeState::~RuntimeState() { | |
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(_query_mem_tracker); | |
| // close error log file | |
| if (_error_log_file != nullptr && _error_log_file->is_open()) { | |
| _error_log_file->close(); | |
| } | |
| _obj_pool->clear(); | |
| } | |
| const std::set<int>& RuntimeState::get_deregister_runtime_filter() const { | |
| return _registered_runtime_filter_ids; | |
| } | |
| void RuntimeState::merge_register_runtime_filter(const std::set<int>& runtime_filter_ids) { | |
| _registered_runtime_filter_ids.insert(runtime_filter_ids.begin(), runtime_filter_ids.end()); | |
| } | |
| Status RuntimeState::init(const TUniqueId& fragment_instance_id, const TQueryOptions& query_options, | |
| const TQueryGlobals& query_globals, ExecEnv* exec_env) { | |
| _fragment_instance_id = fragment_instance_id; | |
| _query_options = query_options; | |
| _lc_time_names = query_globals.lc_time_names; | |
| if (query_globals.__isset.time_zone && query_globals.__isset.nano_seconds) { | |
| _timezone = query_globals.time_zone; | |
| _timestamp_ms = query_globals.timestamp_ms; | |
| _nano_seconds = query_globals.nano_seconds; | |
| } else if (query_globals.__isset.time_zone) { | |
| _timezone = query_globals.time_zone; | |
| _timestamp_ms = query_globals.timestamp_ms; | |
| _nano_seconds = 0; | |
| } else if (!query_globals.now_string.empty()) { | |
| _timezone = TimezoneUtils::default_time_zone; | |
| VecDateTimeValue dt; | |
| CastParameters params; | |
| DORIS_CHECK((CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT, | |
| DatelikeTargetType::DATE_TIME>( | |
| {query_globals.now_string.c_str(), query_globals.now_string.size()}, dt, nullptr, | |
| params))); | |
| int64_t timestamp; | |
| dt.unix_timestamp(×tamp, _timezone); | |
| _timestamp_ms = timestamp * 1000; | |
| _nano_seconds = 0; | |
| } else { | |
| //Unit test may set into here | |
| _timezone = TimezoneUtils::default_time_zone; | |
| _timestamp_ms = 0; | |
| _nano_seconds = 0; | |
| } | |
| TimezoneUtils::find_cctz_time_zone(_timezone, _timezone_obj); | |
| if (query_globals.__isset.load_zero_tolerance) { | |
| _load_zero_tolerance = query_globals.load_zero_tolerance; | |
| } | |
| _exec_env = exec_env; | |
| if (_query_options.max_errors <= 0) { | |
| // TODO: fix linker error and uncomment this | |
| //_query_options.max_errors = config::max_errors; | |
| _query_options.max_errors = 100; | |
| } | |
| if (_query_options.batch_size <= 0) { | |
| _query_options.batch_size = DEFAULT_BATCH_SIZE; | |
| } | |
| _db_name = "insert_stmt"; | |
| _import_label = print_id(fragment_instance_id); | |
| _profile_level = query_options.__isset.profile_level ? query_options.profile_level : 2; | |
| return Status::OK(); | |
| } | |
| std::weak_ptr<QueryContext> RuntimeState::get_query_ctx_weak() { | |
| return _exec_env->fragment_mgr()->get_query_ctx(_query_ctx->query_id()); | |
| } | |
| void RuntimeState::init_mem_trackers(const std::string& name, const TUniqueId& id) { | |
| _query_mem_tracker = MemTrackerLimiter::create_shared( | |
| MemTrackerLimiter::Type::OTHER, fmt::format("{}#Id={}", name, print_id(id))); | |
| } | |
| std::shared_ptr<MemTrackerLimiter> RuntimeState::query_mem_tracker() const { | |
| CHECK(_query_mem_tracker != nullptr); | |
| return _query_mem_tracker; | |
| } | |
| WorkloadGroupPtr RuntimeState::workload_group() { | |
| return _query_ctx->workload_group(); | |
| } | |
| bool RuntimeState::log_error(const std::string& error) { | |
| std::lock_guard<std::mutex> l(_error_log_lock); | |
| if (_error_log.size() < _query_options.max_errors) { | |
| _error_log.push_back(error); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void RuntimeState::get_unreported_errors(std::vector<std::string>* new_errors) { | |
| std::lock_guard<std::mutex> l(_error_log_lock); | |
| if (_unreported_error_idx < _error_log.size()) { | |
| new_errors->assign(_error_log.begin() + _unreported_error_idx, _error_log.end()); | |
| _unreported_error_idx = (int)_error_log.size(); | |
| } | |
| } | |
| bool RuntimeState::is_cancelled() const { | |
| // Maybe we should just return _is_cancelled.load() | |
| return !_exec_status.ok() || (_query_ctx && _query_ctx->is_cancelled()); | |
| } | |
| Status RuntimeState::cancel_reason() const { | |
| if (!_exec_status.ok()) { | |
| return _exec_status.status(); | |
| } | |
| if (_query_ctx) { | |
| return _query_ctx->exec_status(); | |
| } | |
| return Status::Cancelled("Query cancelled"); | |
| } | |
| const int64_t MAX_ERROR_NUM = 50; | |
| Status RuntimeState::create_error_log_file() { | |
| if (config::save_load_error_log_to_s3 && config::is_cloud_mode()) { | |
| _s3_error_fs = std::dynamic_pointer_cast<io::S3FileSystem>( | |
| ExecEnv::GetInstance()->storage_engine().to_cloud().latest_fs()); | |
| if (_s3_error_fs) { | |
| std::stringstream ss; | |
| // https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_err_packet.html | |
| // shorten the path as much as possible to prevent the length of the presigned URL from | |
| // exceeding the MySQL error packet size limit | |
| ss << "error_log/" << std::hex << _fragment_instance_id.lo; | |
| _s3_error_log_file_path = ss.str(); | |
| } | |
| } | |
| static_cast<void>(_exec_env->load_path_mgr()->get_load_error_file_name( | |
| _db_name, _import_label, _fragment_instance_id, &_error_log_file_path)); | |
| std::string error_log_absolute_path = | |
| _exec_env->load_path_mgr()->get_load_error_absolute_path(_error_log_file_path); | |
| _error_log_file = std::make_unique<std::ofstream>(error_log_absolute_path, std::ifstream::out); | |
| if (!_error_log_file->is_open()) { | |
| std::stringstream error_msg; | |
| error_msg << "Fail to open error file: [" << _error_log_file_path << "]."; | |
| LOG(WARNING) << error_msg.str(); | |
| return Status::InternalError(error_msg.str()); | |
| } | |
| LOG(INFO) << "create error log file: " << _error_log_file_path | |
| << ", query id: " << print_id(_query_id) | |
| << ", fragment instance id: " << print_id(_fragment_instance_id); | |
| return Status::OK(); | |
| } | |
| Status RuntimeState::append_error_msg_to_file(std::function<std::string()> line, | |
| std::function<std::string()> error_msg) { | |
| if (query_type() != TQueryType::LOAD) { | |
| return Status::OK(); | |
| } | |
| const auto error_limit_status = [this]() -> Status { | |
| if (_load_zero_tolerance) { | |
| return Status::DataQualityError( | |
| "Encountered unqualified data, stop processing. Please check if the source " | |
| "data matches the schema, and consider disabling strict mode or increasing " | |
| "max_filter_ratio."); | |
| } | |
| return Status::OK(); | |
| }; | |
| if (_num_print_error_rows.load(std::memory_order_relaxed) > MAX_ERROR_NUM) { | |
| return error_limit_status(); | |
| } | |
| std::lock_guard<std::mutex> l(_load_error_log_lock); | |
| // If file haven't been opened, open it here | |
| if (_error_log_file == nullptr) { | |
| Status status = create_error_log_file(); | |
| if (!status.ok()) { | |
| LOG(WARNING) << "Create error file log failed. because: " << status; | |
| if (_error_log_file != nullptr) { | |
| _error_log_file->close(); | |
| } | |
| return status; | |
| } | |
| // record the first error message if the file is just created | |
| _first_error_msg = error_msg() + ". Src line: " + line(); | |
| LOG(INFO) << "The first error message: " << _first_error_msg; | |
| } | |
| // If num of printed error row exceeds the limit, don't add error messages to error log file any more | |
| if (_num_print_error_rows.fetch_add(1, std::memory_order_relaxed) > MAX_ERROR_NUM) { | |
| return error_limit_status(); | |
| } | |
| fmt::memory_buffer out; | |
| // Note: export reason first in case src line too long and be truncated. | |
| fmt::format_to(out, "Reason: {}. src line [{}]; ", error_msg(), line()); | |
| size_t error_row_size = out.size(); | |
| if (error_row_size > 0) { | |
| if (error_row_size > config::load_error_log_limit_bytes) { | |
| fmt::memory_buffer limit_byte_out; | |
| limit_byte_out.append(out.data(), out.data() + config::load_error_log_limit_bytes); | |
| (*_error_log_file) << fmt::to_string(limit_byte_out) + "error log is too long" | |
| << std::endl; | |
| } else { | |
| (*_error_log_file) << fmt::to_string(out) << std::endl; | |
| } | |
| } | |
| return Status::OK(); | |
| } | |
| std::string RuntimeState::get_first_error_msg() const { | |
| std::lock_guard<std::mutex> l(_load_error_log_lock); | |
| return _first_error_msg; | |
| } | |
| std::string RuntimeState::get_error_log_file_path() { | |
| std::lock_guard<std::mutex> s3_lock(_s3_error_log_file_lock); | |
| std::shared_ptr<io::S3FileSystem> s3_error_fs; | |
| std::string local_error_log_file_path; | |
| std::string remote_error_log_file_path; | |
| { | |
| std::lock_guard<std::mutex> load_lock(_load_error_log_lock); | |
| DBUG_EXECUTE_IF("RuntimeState::get_error_log_file_path.block", { | |
| if (!_error_log_file_path.empty()) { | |
| std::this_thread::sleep_for(std::chrono::seconds(1)); | |
| } | |
| }); | |
| if (!_s3_error_fs || !_error_log_file || !_error_log_file->is_open()) { | |
| return _error_log_file_path; | |
| } | |
| // close error log file | |
| _error_log_file->close(); | |
| s3_error_fs = _s3_error_fs; | |
| local_error_log_file_path = _error_log_file_path; | |
| remote_error_log_file_path = _s3_error_log_file_path; | |
| } | |
| std::string error_log_absolute_path = | |
| _exec_env->load_path_mgr()->get_load_error_absolute_path(local_error_log_file_path); | |
| // upload error log file to s3 | |
| Status st = s3_error_fs->upload(error_log_absolute_path, remote_error_log_file_path); | |
| if (!st.ok()) { | |
| // upload failed and return local error log file path | |
| LOG(WARNING) << "Fail to upload error file to s3, error_log_file_path=" | |
| << local_error_log_file_path << ", error=" << st; | |
| return local_error_log_file_path; | |
| } | |
| // expiration must be less than a week (in seconds) for presigned url | |
| static const unsigned EXPIRATION_SECONDS = 7 * 24 * 60 * 60 - 1; | |
| // Use public or private endpoint based on configuration | |
| auto presigned_url = | |
| s3_error_fs->generate_presigned_url(remote_error_log_file_path, EXPIRATION_SECONDS, | |
| config::use_public_endpoint_for_error_log); | |
| std::lock_guard<std::mutex> load_lock(_load_error_log_lock); | |
| _error_log_file_path = std::move(presigned_url); | |
| return _error_log_file_path; | |
| } | |
| void RuntimeState::resize_op_id_to_local_state(int operator_size) { | |
| _op_id_to_local_state.resize(-operator_size); | |
| } | |
| void RuntimeState::emplace_local_state(int id, | |
| std::unique_ptr<doris::PipelineXLocalStateBase> state) { | |
| id = -id; | |
| DCHECK_LT(id, _op_id_to_local_state.size()) | |
| << state->parent()->get_name() << " node id = " << state->parent()->node_id(); | |
| DCHECK(!_op_id_to_local_state[id]); | |
| _op_id_to_local_state[id] = std::move(state); | |
| } | |
| doris::PipelineXLocalStateBase* RuntimeState::get_local_state(int id) { | |
| DCHECK_GT(_op_id_to_local_state.size(), -id); | |
| return _op_id_to_local_state[-id].get(); | |
| } | |
| Result<RuntimeState::LocalState*> RuntimeState::get_local_state_result(int id) { | |
| id = -id; | |
| if (id >= _op_id_to_local_state.size()) { | |
| return ResultError(Status::InternalError("get_local_state out of range size:{} , id:{}", | |
| _op_id_to_local_state.size(), id)); | |
| } | |
| if (!_op_id_to_local_state[id]) { | |
| return ResultError(Status::InternalError("get_local_state id:{} is null", id)); | |
| } | |
| return _op_id_to_local_state[id].get(); | |
| }; | |
| void RuntimeState::emplace_sink_local_state( | |
| int id, std::unique_ptr<doris::PipelineXSinkLocalStateBase> state) { | |
| DCHECK(!_sink_local_state) << " id=" << id << " state: " << state->debug_string(0); | |
| _sink_local_state = std::move(state); | |
| } | |
| doris::PipelineXSinkLocalStateBase* RuntimeState::get_sink_local_state() { | |
| return _sink_local_state.get(); | |
| } | |
| Result<RuntimeState::SinkLocalState*> RuntimeState::get_sink_local_state_result() { | |
| if (!_sink_local_state) { | |
| return ResultError(Status::InternalError("_op_id_to_sink_local_state not exist")); | |
| } | |
| return _sink_local_state.get(); | |
| } | |
| bool RuntimeState::enable_page_cache() const { | |
| return !config::disable_storage_page_cache && | |
| (_query_options.__isset.enable_page_cache && _query_options.enable_page_cache); | |
| } | |
| RuntimeFilterMgr* RuntimeState::global_runtime_filter_mgr() { | |
| return _query_ctx->runtime_filter_mgr(); | |
| } | |
| Status RuntimeState::register_producer_runtime_filter( | |
| const TRuntimeFilterDesc& desc, std::shared_ptr<RuntimeFilterProducer>* producer_filter) { | |
| _registered_runtime_filter_ids.insert(desc.filter_id); | |
| // Producers are created by local runtime filter mgr and shared by global runtime filter manager. | |
| // When RF is published, consumers in both global and local RF mgr will be found. | |
| RETURN_IF_ERROR(local_runtime_filter_mgr()->register_producer_filter(_query_ctx, desc, | |
| producer_filter)); | |
| // Stamp the producer with the current recursive CTE stage so that outgoing merge RPCs | |
| // carry the correct round number and stale messages from old rounds are discarded. | |
| // PFC must still be alive: this runs inside a pipeline task, so the execution context | |
| // cannot have expired yet. | |
| // In unit-test scenarios the task execution context is never set (no PipelineFragmentContext | |
| // exists), so skip the stage stamping — the default stage (0) is correct. | |
| if (task_execution_context_inited()) { | |
| auto pfc = std::static_pointer_cast<PipelineFragmentContext>( | |
| get_task_execution_context().lock()); | |
| DORIS_CHECK(pfc); | |
| (*producer_filter)->set_stage(pfc->rec_cte_stage()); | |
| } | |
| RETURN_IF_ERROR(global_runtime_filter_mgr()->register_local_merge_producer_filter( | |
| _query_ctx, desc, *producer_filter)); | |
| return Status::OK(); | |
| } | |
| Status RuntimeState::register_consumer_runtime_filter( | |
| const TRuntimeFilterDesc& desc, bool need_local_merge, int node_id, | |
| std::shared_ptr<RuntimeFilterConsumer>* consumer_filter) { | |
| _registered_runtime_filter_ids.insert(desc.filter_id); | |
| bool need_merge = desc.has_remote_targets || need_local_merge || | |
| (desc.__isset.force_local_merge && desc.force_local_merge); | |
| RuntimeFilterMgr* mgr = need_merge ? global_runtime_filter_mgr() : local_runtime_filter_mgr(); | |
| RETURN_IF_ERROR(mgr->register_consumer_filter(this, desc, node_id, consumer_filter)); | |
| // Stamp the consumer with the current recursive CTE stage so that incoming publish RPCs | |
| // from old rounds are detected and discarded. | |
| // PFC must still be alive: this runs inside a pipeline task, so the execution context | |
| // cannot have expired yet. | |
| // In unit-test scenarios the task execution context is never set (no PipelineFragmentContext | |
| // exists), so skip the stage stamping — the default stage (0) is correct. | |
| if (task_execution_context_inited()) { | |
| auto pfc = std::static_pointer_cast<PipelineFragmentContext>( | |
| get_task_execution_context().lock()); | |
| DORIS_CHECK(pfc); | |
| (*consumer_filter)->set_stage(pfc->rec_cte_stage()); | |
| } | |
| return Status::OK(); | |
| } | |
| bool RuntimeState::is_nereids() const { | |
| return _query_ctx->is_nereids(); | |
| } | |
| std::vector<std::shared_ptr<RuntimeProfile>> RuntimeState::pipeline_id_to_profile() { | |
| std::shared_lock lc(_pipeline_profile_lock); | |
| return _pipeline_id_to_profile; | |
| } | |
| std::vector<std::shared_ptr<RuntimeProfile>> RuntimeState::build_pipeline_profile( | |
| std::size_t pipeline_size) { | |
| std::unique_lock lc(_pipeline_profile_lock); | |
| if (!_pipeline_id_to_profile.empty()) { | |
| return _pipeline_id_to_profile; | |
| } | |
| _pipeline_id_to_profile.resize(pipeline_size); | |
| { | |
| size_t pip_idx = 0; | |
| for (auto& pipeline_profile : _pipeline_id_to_profile) { | |
| pipeline_profile = | |
| std::make_shared<RuntimeProfile>(fmt::format("Pipeline(id={})", pip_idx)); | |
| pip_idx++; | |
| } | |
| } | |
| return _pipeline_id_to_profile; | |
| } | |
| bool RuntimeState::low_memory_mode() const { | |
| #ifdef BE_TEST | |
| if (!_query_ctx) { | |
| return false; | |
| } | |
| #endif | |
| return _query_ctx->low_memory_mode(); | |
| } | |
| void RuntimeState::set_id_file_map() { | |
| _id_file_map = _exec_env->get_id_manager()->add_id_file_map(_query_id, execution_timeout()); | |
| } | |
| } // end namespace doris |