| 234 | } |
| 235 | |
| 236 | Status BlockingJoinNode::ProcessBuildInputAndOpenProbe( |
| 237 | RuntimeState* state, JoinBuilder* build_sink) { |
| 238 | // This function implements various strategies for executing Open() on the left child |
| 239 | // and doing or waiting for the join build. Some allow the two to proceed in parallel, |
| 240 | // while others do them serially. Generally parallelism yields better performance |
| 241 | // except inside a subplan. There we expect Open() to be called a number of times |
| 242 | // proportional to the input data of the SubplanNode, so processing the build input |
| 243 | // in the main thread can be more efficient, assuming that thread creation is expensive |
| 244 | // relative to a single subplan iteration. |
| 245 | // |
| 246 | // In this block, we also compute the 'overlap' time for the left and right child. This |
| 247 | // is the time (i.e. clock reads) when the right child stops overlapping with the left |
| 248 | // child. For the single threaded case, the left and right child never overlap. For the |
| 249 | // build side in a different thread, the overlap stops when the left child Open() |
| 250 | // returns. |
| 251 | if (UseSeparateBuild(state->query_options())) { |
| 252 | // Open the left child in parallel before waiting for the build fragment to maximise |
| 253 | // parallelism. The build execution is done concurrently by the build finstance's |
| 254 | // thread. |
| 255 | RETURN_IF_ERROR(child(0)->Open(state)); |
| 256 | // AcquireResourcesForBuild() opens the buffer pool client, so that probe reservation |
| 257 | // can be transferred. |
| 258 | RETURN_IF_ERROR(AcquireResourcesForBuild(state)); |
| 259 | { |
| 260 | SCOPED_TIMER(runtime_profile_->inactive_timer()); |
| 261 | events_->MarkEvent("Waiting for initial build"); |
| 262 | RETURN_IF_ERROR(build_sink->WaitForInitialBuild(state)); |
| 263 | events_->MarkEvent("Initial build available"); |
| 264 | } |
| 265 | waited_for_build_ = true; |
| 266 | } else if (!IsInSubplan() && state->resource_pool()->TryAcquireThreadToken()) { |
| 267 | // The build is integrated into the join node and we got a thread token. Do the hash |
| 268 | // table build in a separate thread. |
| 269 | Status build_side_status; |
| 270 | runtime_profile()->AppendExecOption("Join Build-Side Prepared Asynchronously"); |
| 271 | string thread_name = Substitute("join-build-thread (finst:$0, plan-node-id:$1)", |
| 272 | PrintId(state->fragment_instance_id()), id()); |
| 273 | unique_ptr<Thread> build_thread; |
| 274 | Status thread_status = Thread::Create(FragmentInstanceState::FINST_THREAD_GROUP_NAME, |
| 275 | thread_name, [this, state, build_sink, status=&build_side_status]() { |
| 276 | ProcessBuildInputAsync(state, build_sink, status); |
| 277 | }, &build_thread, true); |
| 278 | if (!thread_status.ok()) { |
| 279 | state->resource_pool()->ReleaseThreadToken(false); |
| 280 | return thread_status; |
| 281 | } |
| 282 | // Open the left child so that it may perform any initialisation in parallel. |
| 283 | // Don't exit even if we see an error, we still need to wait for the build thread |
| 284 | // to finish. |
| 285 | Status open_status = child(0)->Open(state); |
| 286 | |
| 287 | // The left/right child overlap stops here. |
| 288 | built_probe_overlap_stop_watch_.SetTimeCeiling(); |
| 289 | |
| 290 | // Blocks until ProcessBuildInput has returned, after which the build side structures |
| 291 | // are fully constructed. |
| 292 | build_thread->Join(); |
| 293 | RETURN_IF_ERROR(build_side_status); |
nothing calls this directly
no test coverage detected