@@ -293,16 +293,21 @@ void TopNRowNumber::processInputRow(vector_size_t index, TopRows& partition) {
293293}
294294
295295void TopNRowNumber::noMoreInput () {
296- Operator::noMoreInput ();
297-
298296 updateEstimatedOutputRowSize ();
299297
298+ if (spiller_ == nullptr ) {
299+ ensureOutputFits ();
300+ }
301+
302+ Operator::noMoreInput ();
303+
300304 outputBatchSize_ = outputBatchRows (estimatedOutputRowSize_);
301305 if (spiller_ != nullptr ) {
302- // Spill remaining data to avoid running out of memory while sort-merging
303- // spilled data.
304- spill ();
305-
306+ if (data_->numRows () > 0 ) {
307+ // Spill remaining data to avoid running out of memory while sort-merging
308+ // spilled data.
309+ spill ();
310+ }
306311 BOLT_CHECK_NULL (merge_);
307312 auto spillPartition = spiller_->finishSpill ();
308313 merge_ = spillPartition.createOrderedReader (pool ());
@@ -312,6 +317,59 @@ void TopNRowNumber::noMoreInput() {
312317 }
313318}
314319
320+ void TopNRowNumber::ensureOutputFits () {
321+ if (abandonedPartial_ || !spillEnabled () || table_ == nullptr ||
322+ data_->numRows () == 0 ) {
323+ return ;
324+ }
325+
326+ const auto currentBytes = pool ()->currentBytes ();
327+ const auto * task = operatorCtx_->task ().get ();
328+ const auto pressure = task->memoryPressureSnapshot ();
329+ const auto pressureWatermarkBytes = pressure.admissionWatermarkBytes ();
330+ const auto & queryConfig = operatorCtx_->driverCtx ()->queryConfig ();
331+ const uint64_t outputAdmissionLimit =
332+ queryConfig.preferredOutputBatchBytes () *
333+ queryConfig.outputBatchMemoryReservationMultiple ();
334+ const auto admissionReservationBytes =
335+ (pressureWatermarkBytes != 0 &&
336+ currentBytes >
337+ pressureWatermarkBytes * queryConfig.memoryPressureWatermarkRatio ())
338+ ? pressureWatermarkBytes
339+ : std::min<uint64_t >(currentBytes / 2 , outputAdmissionLimit);
340+
341+ if (admissionReservationBytes == 0 ) {
342+ return ;
343+ }
344+
345+ {
346+ Operator::ReclaimableSectionGuard guard (this );
347+ const auto scopedDisableMemoryExpansion =
348+ task->maybeScopedDisableMemoryExpansion ();
349+ LOG (INFO ) << name () << " ensureOutputFits: try to reserve "
350+ << succinctBytes (admissionReservationBytes)
351+ << " for output admission, " << pool ()->name () << " ["
352+ << succinctBytes (pool ()->currentBytes ()) << " , "
353+ << succinctBytes (pool ()->reservedBytes ()) << " , "
354+ << succinctBytes (pool ()->capacity ()) << " ]"
355+ << " , pressureWatermarkBytes="
356+ << succinctBytes (pressureWatermarkBytes)
357+ << " , outputAdmissionLimit="
358+ << succinctBytes (outputAdmissionLimit)
359+ << " , memoryPressure=" << task->memoryPressureDetails ();
360+ if (pool ()->maybeReserve (admissionReservationBytes)) {
361+ pool ()->release ();
362+ return ;
363+ }
364+ }
365+
366+ LOG (WARNING ) << name () << " Failed to reserve "
367+ << succinctBytes (admissionReservationBytes)
368+ << " before producing output for memory pool " << pool ()->name ()
369+ << " , usage: " << succinctBytes (pool ()->currentBytes ())
370+ << " , reservation: " << succinctBytes (pool ()->reservedBytes ());
371+ }
372+
315373void TopNRowNumber::updateEstimatedOutputRowSize () {
316374 const auto optionalRowSize = data_->estimateRowSize ();
317375 if (!optionalRowSize.has_value ()) {
0 commit comments