github-advanced-security[bot] commented on code in PR #20805:
URL: https://github.com/apache/druid/pull/20805#discussion_r4448052930


##########
multi-stage-query/src/main/java/org/apache/druid/msq/querykit/ReadableInputQueue.java:
##########
@@ -471,20 +597,137 @@
   }
 
   /**
-   * Calls {@link #nextSegmentInput()} and adds the future to {@link 
#pendingNextInputs}. Returns whether a future
-   * was added.
+   * Acquires the next segment that {@link #inputPruner} does not rule out and 
adds its future to
+   * {@link #pendingNextInputs}. Returns whether a future was added.
    */
   private boolean addLoadaheadFuture()
   {
-    final ListenableFuture<NumberedReadableInput> nextFuture = 
loadNextSegment();
-    if (nextFuture != null) {
-      synchronized (this) {
-        pendingNextInputs.add(nextFuture);
-      }
-      return true;
-    } else {
+    if (canSkipRemaining()) {
+      return false;
+    }
+
+    final NumberedItem<LoadableSegment> nextItem = pollUnprunedSegment();
+    if (nextItem == null) {
       return false;
     }
+
+    final ListenableFuture<NumberedReadableInput> nextFuture = 
acquireSegment(nextItem);
+    synchronized (this) {
+      pendingNextInputs.put(nextFuture, nextItem.item());
+    }
+    return true;
+  }
+
+  /**
+   * Releases pending inputs that will never be handed out for processing. 
Loads still in progress are canceled, and
+   * loaded inputs are released. An input that is mid-delivery (claimed from 
{@link #loadingSegments} by
+   * {@link #onSegmentReady} but not yet completed) is left for {@link 
#close()}; this is a conscious decision to
+   * avoid complexity for what is a sliver of a race.
+   * <p>
+   * Called outside the queue monitor, because canceling a load can run its 
canceler and releasing a segment can take
+   * cache locks.
+   */
+  private void releaseSkippedInputs(final 
List<ListenableFuture<NumberedReadableInput>> skippedFutures)
+  {
+    if (skippedFutures.isEmpty()) {
+      return;
+    }
+
+    final Set<ListenableFuture<NumberedReadableInput>> pendingSkips = 
Sets.newIdentityHashSet();
+    pendingSkips.addAll(skippedFutures);
+    final Map<AcquireSegmentAction, SettableFuture<NumberedReadableInput>> 
loadsToCancel = new LinkedHashMap<>();
+
+    synchronized (this) {
+      final Iterator<Map.Entry<AcquireSegmentAction, 
SettableFuture<NumberedReadableInput>>> iterator =
+          loadingSegments.entrySet().iterator();
+      while (iterator.hasNext()) {
+        final Map.Entry<AcquireSegmentAction, 
SettableFuture<NumberedReadableInput>> entry = iterator.next();
+        if (pendingSkips.remove(entry.getValue())) {
+          loadsToCancel.put(entry.getKey(), entry.getValue());
+          iterator.remove();
+        }
+      }
+    }
+
+    // Cancel all pending segment loads in segments that have been skipped.
+    for (final Map.Entry<AcquireSegmentAction, 
SettableFuture<NumberedReadableInput>> entry : loadsToCancel.entrySet()) {
+      closeLoadingSegment(entry.getKey());
+      entry.getValue().setException(DruidException.defensive("Segment load 
canceled because its input was skipped"));
+    }
+
+    // Lastly, release completed loads that were skipped if their future is 
done.
+    for (final ListenableFuture<NumberedReadableInput> future : pendingSkips) {
+      if (future.isDone()) {
+        final NumberedReadableInput input;
+        try {
+          input = Futures.getDone(future);
+        }
+        catch (ExecutionException | CancellationException e) {
+          // A failed load has already closed its handle in onSegmentReady, so 
there is nothing to release.
+          continue;
+        }
+        releaseLoadedSegment(input.input().getSegment());
+      }
+    }
+  }
+
+  /**
+   * Removes {@code holder} from {@link #loadedSegments} and closes its 
segment, unless {@link #close()} claimed it
+   * first.
+   */
+  private void releaseLoadedSegment(final SegmentReferenceHolder holder)
+  {
+    synchronized (this) {
+      loadedSegments.remove(holder);
+    }
+    closeLoadedSegment(holder);
+  }
+
+  /**
+   * Closes a load that has not been delivered: closing a NEW handle runs its 
canceler (aborting the load), and closing
+   * a ready but unclaimed handle (its ready callback hasn't reached the queue 
monitor yet) closes the delivered result.
+   * The caller must have claimed the handle's entry from {@link 
#loadingSegments}, and is responsible for failing its
+   * future. Called outside the queue monitor.
+   */
+  private static void closeLoadingSegment(final AcquireSegmentAction 
acquireSegmentAction)
+  {
+    CloseableUtils.closeAndSuppressExceptions(
+        acquireSegmentAction,
+        // AcquireSegmentAction currently doesn't have a meaningful toString 
method, so if this message
+        // ever actually gets logged, it won't mention the specific segment 
that had a problem. Perhaps
+        // one day this will change.
+        e -> log.warn(e, "Failed to close loadingSegment[%s]", 
acquireSegmentAction)

Review Comment:
   ## CodeQL / Use of default toString()
   
   Default toString(): AcquireSegmentAction inherits toString() from Object, 
and so is not suitable for printing.
   
   [Show more 
details](https://github.com/apache/druid/security/code-scanning/12141)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to