| 157 | } |
| 158 | |
| 159 | kudu::Status ImpalaServicePool::QueueInboundCall( |
| 160 | std::unique_ptr<kudu::rpc::InboundCall> call) { |
| 161 | kudu::rpc::InboundCall* c = call.release(); |
| 162 | |
| 163 | vector<uint32_t> unsupported_features; |
| 164 | for (uint32_t feature : c->GetRequiredFeatures()) { |
| 165 | if (!service_->SupportsFeature(feature)) { |
| 166 | unsupported_features.push_back(feature); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | if (!unsupported_features.empty()) { |
| 171 | c->RespondUnsupportedFeature(unsupported_features); |
| 172 | return kudu::Status::NotSupported( |
| 173 | "call requires unsupported application feature flags", |
| 174 | JoinMapped(unsupported_features, |
| 175 | [] (uint32_t flag) { return std::to_string(flag); }, ", ")); |
| 176 | } |
| 177 | |
| 178 | TRACE_TO(c->trace(), "Inserting onto call queue"); // NOLINT(*) |
| 179 | |
| 180 | // Queue message on service queue. |
| 181 | const int64_t transfer_size = c->GetTransferSize(); |
| 182 | { |
| 183 | // Drops an incoming request if consumption already exceeded the limit. Note that |
| 184 | // the current inbound call isn't counted towards the limit yet so adding this call |
| 185 | // may cause the MemTracker's limit to be exceeded. This is done to ensure fairness |
| 186 | // among all inbound calls, otherwise calls with larger payloads are more likely to |
| 187 | // fail. The check and the consumption need to be atomic so as to bound the memory |
| 188 | // usage. |
| 189 | unique_lock<SpinLock> mem_tracker_lock(mem_tracker_lock_); |
| 190 | if (UNLIKELY(service_mem_tracker_->AnyLimitExceeded(MemLimit::HARD))) { |
| 191 | if (c->remote_method().method_name() == DataStreamService::END_DATA_STREAM) { |
| 192 | // EndDataStream operations use very little memory and help free up other |
| 193 | // resources, so ignore memory hard limit and always add them to the queue. This |
| 194 | // can help complete queries earlier when under heavy load that would otherwise |
| 195 | // drop the requests and require them to be retried. |
| 196 | LOG(WARNING) << "Admitting " << c->remote_method().method_name() |
| 197 | << " request on " << service_->service_name() |
| 198 | << "from " << c->remote_address().ToString() |
| 199 | << " to complete query despite exceeding memory limit; memory consumption is " |
| 200 | << PrettyPrinter::Print(service_mem_tracker_->consumption(), TUnit::BYTES); |
| 201 | } else { |
| 202 | // Discards the transfer early so the transfer size drops to 0. This is to ensure |
| 203 | // the MemTracker::Release() call in FailAndReleaseRpc() is correct as we haven't |
| 204 | // called MemTracker::Consume() at this point. |
| 205 | mem_tracker_lock.unlock(); |
| 206 | c->DiscardTransfer(); |
| 207 | RejectTooBusy(c); |
| 208 | return kudu::Status::OK(); |
| 209 | } |
| 210 | } |
| 211 | if (UNLIKELY(!FLAGS_debug_actions.empty())) { |
| 212 | Status status = DebugAction(FLAGS_debug_actions, "INBOUND_GETQUERYSTATUS_REJECT"); |
| 213 | if (!status.ok() && c->remote_method().method_name() == "GetQueryStatus") { |
| 214 | mem_tracker_lock.unlock(); |
| 215 | c->DiscardTransfer(); |
| 216 | RejectTooBusy(c); |
nothing calls this directly
no test coverage detected