Zoltán Borók-Nagy created IMPALA-15656:
------------------------------------------

             Summary: CTE producer hangs in LocalExchanger::Release() when a 
consumer fails before opening the exchanger
                 Key: IMPALA-15656
                 URL: https://issues.apache.org/jira/browse/IMPALA-15656
             Project: IMPALA
          Issue Type: Bug
          Components: Backend
            Reporter: Zoltán Borók-Nagy


LocalExchanger::Release(), called from CTEProducerNode::Close(), waits until 
every consumer has called CloseConsumer() (local-exchanger.cc:121 on master 
1a26e1f778):

{code}
all_consumers_done_cv_.wait(l, [&]() {
  return consumers_done_ == consumer_count_;
});
{code}

CTEConsumerNode looks up its exchanger only at the end of Open(), after 
ExecNode::Open(), ClaimBufferReservation() and the expr opens. Close() calls 
CloseConsumer() only if that lookup happened. So a consumer is never counted as 
closed when:
* an earlier step of its Open() fails, e.g. the reservation claim under memory 
pressure, or a cancellation;
* its fragment instance fails or is cancelled before the node is opened, e.g. a 
join whose build side fails before the probe side is opened;
* its fragment instance never starts.

The producer's fragment instance then blocks in Release() forever. The query is 
never torn down, and all row batches in the exchanger stay charged to it.

*Seen with* TPC-DS q14_1 at SF3000 on 8 executors (PLANNER=CALCITE, 
CTE_THRESHOLD=1, MT_DOP=8, mem_limit=40g). The query fails with "Memory limit 
exceeded" (the CTE buffer cannot spill, IMPALA-15313). Afterwards every 
executor keeps the query's memory (about 23 GB on the coordinator). In one run 
all 91 queries that followed failed with "Failed to get minimum memory 
reservation" until the impalads were restarted. A stack dump of the coordinator 
60 s after the failure shows 16 fragment instance threads of the failed query 
in:

{noformat}
std::condition_variable::wait<LocalExchanger::Release()::<lambda()>>
impala::LocalExchanger::Release() local-exchanger.cc:121
impala::CTEProducerNode::Close() cte-producer-node.cc:135
impala::FragmentInstanceState::Close() fragment-instance-state.cc:572
{noformat}

No consumer threads of that query were alive.

*Repro idea (not tried):* run a query whose CTE is materialized 
(PLANNER=CALCITE, CTE_THRESHOLD=1) with a DEBUG_ACTION that fails one 
consumer's fragment before the CTE consumer is opened. The query should fail, 
and /memz should keep listing it.

*Fix (prototyped):* once the query is cancelled, Release() waits only for the 
consumers that called Open(). After the release, LocalExchanger::Open() returns 
-1 and a late consumer runs without an exchanger; that is safe because the 
query is already cancelled. With this change q14_1's memory was released 5-11 s 
after the failure, and the queries after it ran normally.

An alternative is to register consumers in Prepare() and always release the 
consumer slot in Close().

Prototype: https://gerrit.cloudera.org/#/c/25095/




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to