| // 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. | |
| #include "exec/exchange/vdata_stream_mgr.h" | |
| #include <gen_cpp/Types_types.h> | |
| #include <gen_cpp/data.pb.h> | |
| #include <gen_cpp/internal_service.pb.h> | |
| #include <gen_cpp/types.pb.h> | |
| #include <stddef.h> | |
| #include <memory> | |
| #include <ostream> | |
| #include <string> | |
| #include <vector> | |
| #include "common/logging.h" | |
| #include "exec/exchange/vdata_stream_recvr.h" | |
| #include "util/hash_util.hpp" | |
| namespace doris { | |
| VDataStreamMgr::VDataStreamMgr() { | |
| // TODO: metric | |
| } | |
| VDataStreamMgr::~VDataStreamMgr() { | |
| // Has to call close here, because receiver will check if the receiver is closed. | |
| // It will core during graceful stop. | |
| auto receivers = std::vector<std::shared_ptr<VDataStreamRecvr>>(); | |
| { | |
| SharedLockGuard l(_lock); | |
| auto receiver_iterator = _receiver_map.begin(); | |
| while (receiver_iterator != _receiver_map.end()) { | |
| // Could not call close directly, because during close method, it will remove itself | |
| // from the map, and modify the map, it will core. | |
| receivers.push_back(receiver_iterator->second); | |
| receiver_iterator++; | |
| } | |
| } | |
| for (auto iter = receivers.begin(); iter != receivers.end(); ++iter) { | |
| (*iter)->close(); | |
| } | |
| } | |
| inline uint32_t VDataStreamMgr::get_hash_value(const TUniqueId& fragment_instance_id, | |
| PlanNodeId node_id) { | |
| uint32_t value = HashUtil::hash(&fragment_instance_id.lo, 8, 0); | |
| value = HashUtil::hash(&fragment_instance_id.hi, 8, value); | |
| value = HashUtil::hash(&node_id, 4, value); | |
| return value; | |
| } | |
| std::shared_ptr<VDataStreamRecvr> VDataStreamMgr::create_recvr( | |
| RuntimeState* state, RuntimeProfile::HighWaterMarkCounter* memory_used_counter, | |
| const TUniqueId& fragment_instance_id, PlanNodeId dest_node_id, int num_senders, | |
| RuntimeProfile* profile, bool is_merging, size_t data_queue_capacity) { | |
| DCHECK(profile != nullptr); | |
| VLOG_FILE << "creating receiver for fragment=" << print_id(fragment_instance_id) | |
| << ", node=" << dest_node_id; | |
| std::shared_ptr<VDataStreamRecvr> recvr(new VDataStreamRecvr( | |
| this, memory_used_counter, state, fragment_instance_id, dest_node_id, num_senders, | |
| is_merging, profile, data_queue_capacity)); | |
| uint32_t hash_value = get_hash_value(fragment_instance_id, dest_node_id); | |
| LockGuard l(_lock); | |
| _fragment_stream_set.insert(std::make_pair(fragment_instance_id, dest_node_id)); | |
| _receiver_map.insert(std::make_pair(hash_value, recvr)); | |
| return recvr; | |
| } | |
| Status VDataStreamMgr::_find_recvr(uint32_t hash_value, const TUniqueId& fragment_instance_id, | |
| PlanNodeId node_id, std::shared_ptr<VDataStreamRecvr>* res) { | |
| VLOG_ROW << "looking up fragment_instance_id=" << print_id(fragment_instance_id) | |
| << ", node=" << node_id; | |
| std::pair<StreamMap::iterator, StreamMap::iterator> range = | |
| _receiver_map.equal_range(hash_value); | |
| while (range.first != range.second) { | |
| auto recvr = range.first->second; | |
| if (recvr->fragment_instance_id() == fragment_instance_id && | |
| recvr->dest_node_id() == node_id) { | |
| *res = recvr; | |
| return Status::OK(); | |
| } | |
| ++range.first; | |
| } | |
| return Status::InvalidArgument("Could not find local receiver for node {} with instance {}", | |
| node_id, print_id(fragment_instance_id)); | |
| } | |
| Status VDataStreamMgr::find_recvr(const TUniqueId& fragment_instance_id, PlanNodeId node_id, | |
| std::shared_ptr<VDataStreamRecvr>* res) { | |
| SharedLockGuard recvr_lock(_lock); | |
| uint32_t hash_value = get_hash_value(fragment_instance_id, node_id); | |
| return _find_recvr(hash_value, fragment_instance_id, node_id, res); | |
| } | |
| Status VDataStreamMgr::transmit_block(const PTransmitDataParams* request, | |
| ::google::protobuf::Closure** done, | |
| const int64_t wait_for_worker) { | |
| const PUniqueId& finst_id = request->finst_id(); | |
| TUniqueId t_finst_id; | |
| t_finst_id.hi = finst_id.hi(); | |
| t_finst_id.lo = finst_id.lo(); | |
| std::shared_ptr<VDataStreamRecvr> recvr = nullptr; | |
| ThreadCpuStopWatch cpu_time_stop_watch; | |
| cpu_time_stop_watch.start(); | |
| static_cast<void>(find_recvr(t_finst_id, request->node_id(), &recvr)); | |
| if (recvr == nullptr) { | |
| // The receiver may remove itself from the receiver map via deregister_recvr() | |
| // at any time without considering the remaining number of senders. | |
| // As a consequence, find_recvr() may return an innocuous NULL if a thread | |
| // calling deregister_recvr() beat the thread calling find_recvr() | |
| // in acquiring _lock. | |
| // | |
| // e.g. for broadcast join build side, only one instance will build the hash table, | |
| // all other instances don't need build side data and will close the data stream receiver. | |
| // | |
| // TODO: Rethink the lifecycle of DataStreamRecvr to distinguish | |
| // errors from receiver-initiated teardowns. | |
| return Status::EndOfFile("data stream receiver closed"); | |
| } | |
| // Lock the fragment context to ensure the runtime state and other objects are not | |
| // deconstructed | |
| auto ctx_lock = recvr->task_exec_ctx(); | |
| if (ctx_lock == nullptr) { | |
| // Do not return internal error, because when query finished, the downstream node | |
| // may finish before upstream node. And the object maybe deconstructed. If return error | |
| // then the upstream node may report error status to FE, the query is failed. | |
| return Status::EndOfFile("data stream receiver is deconstructed"); | |
| } | |
| bool eos = request->eos(); | |
| Status exec_status = | |
| request->has_exec_status() ? Status::create(request->exec_status()) : Status::OK(); | |
| auto sender_id = request->sender_id(); | |
| auto be_number = request->be_number(); | |
| if (!request->blocks().empty()) { | |
| RETURN_IF_ERROR(recvr->add_blocks(request, done, wait_for_worker, | |
| cpu_time_stop_watch.elapsed_time())); | |
| } else if (request->has_block()) { | |
| // old logic, for compatibility | |
| std::unique_ptr<PBlock> pblock_ptr = std::make_unique<PBlock>(); | |
| pblock_ptr->CopyFrom(request->block()); | |
| RETURN_IF_ERROR(recvr->add_block(std::move(pblock_ptr), sender_id, be_number, | |
| request->packet_seq(), eos ? nullptr : done, | |
| wait_for_worker, cpu_time_stop_watch.elapsed_time())); | |
| } | |
| if (eos) { | |
| recvr->remove_sender(sender_id, be_number, exec_status); | |
| } | |
| return Status::OK(); | |
| } | |
| Status VDataStreamMgr::deregister_recvr(const TUniqueId& fragment_instance_id, PlanNodeId node_id) { | |
| std::shared_ptr<VDataStreamRecvr> targert_recvr; | |
| VLOG_QUERY << "deregister_recvr(): fragment_instance_id=" << print_id(fragment_instance_id) | |
| << ", node=" << node_id; | |
| uint32_t hash_value = get_hash_value(fragment_instance_id, node_id); | |
| { | |
| LockGuard l(_lock); | |
| auto range = _receiver_map.equal_range(hash_value); | |
| while (range.first != range.second) { | |
| const std::shared_ptr<VDataStreamRecvr>& recvr = range.first->second; | |
| if (recvr->fragment_instance_id() == fragment_instance_id && | |
| recvr->dest_node_id() == node_id) { | |
| targert_recvr = recvr; | |
| _fragment_stream_set.erase( | |
| std::make_pair(recvr->fragment_instance_id(), recvr->dest_node_id())); | |
| _receiver_map.erase(range.first); | |
| break; | |
| } | |
| ++range.first; | |
| } | |
| } | |
| // Notify concurrent add_data() requests that the stream has been terminated. | |
| // cancel_stream maybe take a long time, so we handle it out of lock. | |
| if (targert_recvr) { | |
| targert_recvr->cancel_stream(Status::OK()); | |
| return Status::OK(); | |
| } else { | |
| return Status::InternalError("unknown row receiver id: fragment_instance_id={}, node_id={}", | |
| print_id(fragment_instance_id), node_id); | |
| } | |
| } | |
| void VDataStreamMgr::cancel(const TUniqueId& fragment_instance_id, Status exec_status) { | |
| VLOG_QUERY << "cancelling all streams for fragment=" << print_id(fragment_instance_id); | |
| std::vector<std::shared_ptr<VDataStreamRecvr>> recvrs; | |
| { | |
| SharedLockGuard l(_lock); | |
| FragmentStreamSet::iterator i = | |
| _fragment_stream_set.lower_bound(std::make_pair(fragment_instance_id, 0)); | |
| while (i != _fragment_stream_set.end() && i->first == fragment_instance_id) { | |
| std::shared_ptr<VDataStreamRecvr> recvr; | |
| uint32_t hash_value = get_hash_value(i->first, i->second); | |
| WARN_IF_ERROR(_find_recvr(hash_value, i->first, i->second, &recvr), ""); | |
| if (recvr == nullptr) { | |
| // keep going but at least log it | |
| std::stringstream err; | |
| err << "cancel(): missing in stream_map: fragment=" << print_id(i->first) | |
| << " node=" << i->second; | |
| LOG(ERROR) << err.str(); | |
| } else { | |
| recvrs.push_back(recvr); | |
| } | |
| ++i; | |
| } | |
| } | |
| // cancel_stream maybe take a long time, so we handle it out of lock. | |
| for (auto& it : recvrs) { | |
| it->cancel_stream(exec_status); | |
| } | |
| } | |
| } // namespace doris |