This is an automated email from the ASF dual-hosted git repository.

kfaraz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 12116521c88 fix: Limit number of unused segment rows scanned (#19690)
12116521c88 is described below

commit 12116521c8881f6ac5f5f793afbcdeeae5691876
Author: Kashif Faraz <[email protected]>
AuthorDate: Wed Jul 15 22:48:28 2026 +0530

    fix: Limit number of unused segment rows scanned (#19690)
    
    * Limit number of unused segment rows scanned in
    SqlSegmentsMetadatQuery.retrieveSomeUnusedSegmentIntervals
    * Disable checkstyle for text block
    * Fix javadocs and method name
---
 .../overlord/duty/UnusedSegmentsKiller.java        |  2 +-
 .../TestIndexerMetadataStorageCoordinator.java     |  2 +-
 .../IndexerMetadataStorageCoordinator.java         | 18 +++++++--
 .../IndexerSQLMetadataStorageCoordinator.java      |  4 +-
 .../druid/metadata/SqlSegmentsMetadataQuery.java   | 43 +++++++++++++++++-----
 .../IndexerSQLMetadataStorageCoordinatorTest.java  | 10 ++---
 6 files changed, 56 insertions(+), 23 deletions(-)

diff --git 
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/duty/UnusedSegmentsKiller.java
 
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/duty/UnusedSegmentsKiller.java
index b582ae80acd..daaef7d6c03 100644
--- 
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/duty/UnusedSegmentsKiller.java
+++ 
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/duty/UnusedSegmentsKiller.java
@@ -249,7 +249,7 @@ public class UnusedSegmentsKiller implements OverlordDuty
 
       final Map<String, Integer> dataSourceToIntervalCounts = new HashMap<>();
       for (String dataSource : dataSources) {
-        storageCoordinator.retrieveUnusedSegmentIntervals(dataSource, 
MAX_INTERVALS_TO_KILL_IN_DATASOURCE).forEach(
+        storageCoordinator.retrieveSomeUnusedSegmentIntervals(dataSource, 
MAX_INTERVALS_TO_KILL_IN_DATASOURCE).forEach(
             interval -> {
               dataSourceToIntervalCounts.merge(dataSource, 1, Integer::sum);
               killQueue.offer(new KillCandidate(dataSource, interval));
diff --git 
a/indexing-service/src/test/java/org/apache/druid/indexing/test/TestIndexerMetadataStorageCoordinator.java
 
b/indexing-service/src/test/java/org/apache/druid/indexing/test/TestIndexerMetadataStorageCoordinator.java
index 00cddb943c7..dbc5a5def5c 100644
--- 
a/indexing-service/src/test/java/org/apache/druid/indexing/test/TestIndexerMetadataStorageCoordinator.java
+++ 
b/indexing-service/src/test/java/org/apache/druid/indexing/test/TestIndexerMetadataStorageCoordinator.java
@@ -70,7 +70,7 @@ public class TestIndexerMetadataStorageCoordinator implements 
IndexerMetadataSto
   }
 
   @Override
-  public List<Interval> retrieveUnusedSegmentIntervals(String dataSource, int 
limit)
+  public List<Interval> retrieveSomeUnusedSegmentIntervals(String dataSource, 
int limit)
   {
     return List.of();
   }
diff --git 
a/server/src/main/java/org/apache/druid/indexing/overlord/IndexerMetadataStorageCoordinator.java
 
b/server/src/main/java/org/apache/druid/indexing/overlord/IndexerMetadataStorageCoordinator.java
index 5ac25f6ad0b..db9b9835ef5 100644
--- 
a/server/src/main/java/org/apache/druid/indexing/overlord/IndexerMetadataStorageCoordinator.java
+++ 
b/server/src/main/java/org/apache/druid/indexing/overlord/IndexerMetadataStorageCoordinator.java
@@ -600,12 +600,22 @@ public interface IndexerMetadataStorageCoordinator
 
   /**
    * Retrieves intervals of the specified datasource that contain any unused 
segments.
-   * There is no guarantee on the order of intervals in the list or on whether
-   * the limited list contains the earliest or latest intervals of the 
datasource.
+   * <p>
+   * This method ensures that if there is any unused segment for the 
datasource,
+   * the returned list is not empty. However, it does NOT guarantee that:
+   * <ul>
+   * <li>the intervals in the result would be ordered</li>
+   * <li>the result would contain the earliest or latest intervals for this 
datasource</li>
+   * <li>it would scan all unused segments for this datasource. So, the result
+   * may contain less than {@code limit} entries even when there are more 
distinct
+   * unused segment intervals in the metadata store for this datasource.</li>
+   * </ul>
    *
-   * @return Unsorted list of unused segment intervals containing upto {@code 
limit} entries.
+   * @return List of distinct unused segment intervals for the specified 
datasource
+   * containing at least 1 entry if there is any unused segment for the 
datasource,
+   * upto a maximum of {@code limit} entries.
    */
-  List<Interval> retrieveUnusedSegmentIntervals(String dataSource, int limit);
+  List<Interval> retrieveSomeUnusedSegmentIntervals(String dataSource, int 
limit);
 
   /**
    * Returns the number of segment entries in the database whose state was 
changed as the result of this call (that is,
diff --git 
a/server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java
 
b/server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java
index 3aa06b8e371..1317fd339a6 100644
--- 
a/server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java
+++ 
b/server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java
@@ -164,10 +164,10 @@ public class IndexerSQLMetadataStorageCoordinator 
implements IndexerMetadataStor
   }
 
   @Override
-  public List<Interval> retrieveUnusedSegmentIntervals(String dataSource, int 
limit)
+  public List<Interval> retrieveSomeUnusedSegmentIntervals(String dataSource, 
int limit)
   {
     return inReadOnlyTransaction(
-        sql -> sql.retrieveUnusedSegmentIntervals(dataSource, limit)
+        sql -> sql.retrieveSomeUnusedSegmentIntervals(dataSource, limit)
     );
   }
 
diff --git 
a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java 
b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
index 6257a11b58a..559d3009436 100644
--- 
a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
+++ 
b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
@@ -1065,20 +1065,43 @@ public class SqlSegmentsMetadataQuery
   }
 
   /**
-   * Gets unused segment intervals for the specified datasource. There is no
-   * guarantee on the order of intervals in the list or on whether the limited
-   * list contains the earliest or latest intervals present in the datasource.
+   * Retrieves intervals containing unused segments for the specified 
datasource.
+   * <p>
+   * This method ensures that if there is any unused segment for the 
datasource,
+   * the returned list is not empty. However, it does NOT guarantee that:
+   * <ul>
+   * <li>the intervals in the result would be ordered</li>
+   * <li>the result would contain the earliest or latest intervals for this 
datasource</li>
+   * <li>it would scan all unused segments for this datasource. So, the result
+   * may contain less than {@code limit} entries even when there are more 
distinct
+   * unused segment intervals in the metadata store for this datasource.</li>
+   * </ul>
    *
-   * @return List of unused segment intervals containing upto {@code limit} 
interval entries.
+   * @return List of distinct unused segment intervals for the specified 
datasource
+   * containing at least 1 entry if there is any unused segment for the 
datasource,
+   * upto a maximum of {@code limit} entries.
    */
-  public List<Interval> retrieveUnusedSegmentIntervals(String dataSource, int 
limit)
+  public List<Interval> retrieveSomeUnusedSegmentIntervals(String dataSource, 
int limit)
   {
     final String sql = StringUtils.format(
-        "SELECT start, %2$send%2$s FROM %1$s"
-        + " WHERE dataSource = :dataSource AND used = false"
-        + " GROUP BY %2$send%2$s, start"
-        + "  %3$s",
-        dbTables.getSegmentsTable(), connector.getQuoteString(), 
connector.limitClause(limit)
+        // Disable checkstyle to avoid argumentLineBreaking rule from getting 
triggered
+        //CHECKSTYLE.OFF: Regexp
+        """
+            SELECT start, %2$send%2$s
+            FROM (
+              SELECT start, %2$send%2$s
+              FROM %1$s
+              WHERE dataSource = :dataSource AND used = false
+              %3$s
+            ) AS unused
+            GROUP BY %2$send%2$s, start
+            %4$s
+            """,
+        //CHECKSTYLE.ON: Regexp
+        dbTables.getSegmentsTable(),
+        connector.getQuoteString(),
+        connector.limitClause(1_000_000), // limit unused segment rows scanned 
to 1M
+        connector.limitClause(limit)
     );
 
     final List<Interval> intervals = connector.inReadOnlyTransaction(
diff --git 
a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
 
b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
index 000ba2be3b9..70f4d53cc38 100644
--- 
a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
+++ 
b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
@@ -2252,29 +2252,29 @@ public class IndexerSQLMetadataStorageCoordinatorTest 
extends IndexerSqlMetadata
   }
 
   @Test
-  public void testRetrieveUnusedSegmentIntervals()
+  public void testRetrieveSomeUnusedSegmentIntervals()
   {
     final String dataSource = defaultSegment.getDataSource();
     coordinator.commitSegments(Set.of(defaultSegment, defaultSegment3), null);
 
-    Assert.assertTrue(coordinator.retrieveUnusedSegmentIntervals(dataSource, 
100).isEmpty());
+    
Assert.assertTrue(coordinator.retrieveSomeUnusedSegmentIntervals(dataSource, 
100).isEmpty());
 
     markAllSegmentsUnused(Set.of(defaultSegment), 
DateTimes.nowUtc().minusHours(1));
     Assert.assertEquals(
         List.of(defaultSegment.getInterval()),
-        coordinator.retrieveUnusedSegmentIntervals(dataSource, 100)
+        coordinator.retrieveSomeUnusedSegmentIntervals(dataSource, 100)
     );
 
     markAllSegmentsUnused(Set.of(defaultSegment3), 
DateTimes.nowUtc().minusHours(1));
     Assert.assertEquals(
         Set.of(defaultSegment.getInterval(), defaultSegment3.getInterval()),
-        Set.copyOf(coordinator.retrieveUnusedSegmentIntervals(dataSource, 100))
+        Set.copyOf(coordinator.retrieveSomeUnusedSegmentIntervals(dataSource, 
100))
     );
 
     // Verify retrieve with limit 1 returns only 1 interval
     Assert.assertEquals(
         1,
-        coordinator.retrieveUnusedSegmentIntervals(dataSource, 1).size()
+        coordinator.retrieveSomeUnusedSegmentIntervals(dataSource, 1).size()
     );
   }
 


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

Reply via email to