FrankChen021 commented on code in PR #19772:
URL: https://github.com/apache/druid/pull/19772#discussion_r3674249984


##########
server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java:
##########
@@ -1706,6 +1775,26 @@ private Interval mapToInterval(ResultSet resultSet, 
String dataSource)
     }
   }
 
+  /**
+   * Reads the fields {@code dataSource}, {@code start}, {@code end} and
+   * {@code totalCount} from the given result set.
+   */
+  @Nullable
+  private Pair<DatasourceInterval, Integer> 
mapToUnusedSegmentInterval(ResultSet resultSet)
+  {
+    try {
+      final String dataSource = resultSet.getString("dataSource");
+      final Interval interval = mapToInterval(resultSet, "");
+      final int totalCount = resultSet.getInt("totalCount");
+
+      return Pair.of(new DatasourceInterval(dataSource, interval), totalCount);

Review Comment:
   Thanks. I rechecked the current head across all 10 of 10 changed files, and 
this remains present: `mapToInterval` can return null, but 
`mapToUnusedSegmentInterval` still constructs `DatasourceInterval(dataSource, 
null)`. The outer non-null filter therefore retains the row, and queue 
rebuilding later dereferences the null interval. Please check `interval` before 
constructing the pair.



##########
indexing-service/src/main/java/org/apache/druid/indexing/overlord/duty/UnusedSegmentsKiller.java:
##########
@@ -462,7 +490,7 @@ protected List<DataSegmentPlus> 
fetchNextBatchOfUnusedSegments(TaskToolbox toolb
           getDataSource(),
           getInterval(),
           getMaxUsedStatusLastUpdatedTime(),
-          MAX_SEGMENTS_TO_KILL_IN_INTERVAL
+          MAX_SEGMENTS_TO_KILL_IN_BATCH

Review Comment:
   Thanks—the override now forwards `nextBatchSize`, but the candidate limit is 
still not enforced when some unused segments share a load spec. 
`KillUnusedSegmentsTask` increments `numSegmentsKilled` by the number deleted 
from deep storage, although `SegmentNukeAction` removes the full fetched batch 
from metadata. For example, after a 1,001-row candidate fetches 1,000 rows 
whose shared load specs prevent deep-store deletion, the next batch size 
remains 1,000 and another 1,000 metadata rows can be removed. Please track 
metadata rows processed or otherwise cap this override independently. I 
rechecked all 10 of 10 changed files.



##########
server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java:
##########
@@ -1116,6 +1118,70 @@ public List<Interval> 
retrieveSomeUnusedSegmentIntervals(String dataSource, int
     return 
intervals.stream().filter(Objects::nonNull).collect(Collectors.toList());
   }
 
+  /**
+   * Scans upto {@code maxSegmentsToScan} unused segments which are eligible 
for
+   * kill and returns the unique datasource-interval for the segments scanned.
+   * <p>
+   * This method ensures that if there is any unused segment in any datasource
+   * which was updated earlier than {@code maxUpdatedTime}, then the returned
+   * map is not empty. However, it does NOT guarantee that:
+   * <ul>
+   * <li>the candidates in the returned map would be ordered by datasource or 
interval</li>
+   * <li>the result would contain {@code limit} entries when there are more 
distinct
+   * intervals with eligible unused segments in the metadata store.</li>
+   * </ul>
+   *
+   * @param maxUpdatedTime    Unused segments are considered eligible for kill
+   *                          if they were last updated before this time.
+   * @param maxResultSize     Maximum number of candidate intervals to return
+   *                          across all datasources.
+   * @param maxSegmentsToScan Maximum number of eligible unused segments to 
scan
+   *                          in the metadata store.
+   * @return Map from {@link DatasourceInterval} to the number of unused 
segments
+   * eligible for kill.
+   */
+  public Map<DatasourceInterval, Integer> retrieveSomeUnusedSegmentIntervals(
+      DateTime maxUpdatedTime,
+      int maxResultSize,
+      int maxSegmentsToScan
+  )
+  {
+    final String sql = StringUtils.format(
+        // Disable checkstyle to avoid argumentLineBreaking rule from getting 
triggered
+        //CHECKSTYLE.OFF: Regexp
+        """
+            SELECT dataSource, start, %2$send%2$s, COUNT(*) AS totalCount
+            FROM (
+              SELECT dataSource, start, %2$send%2$s
+              FROM %1$s
+              WHERE used = false
+              AND (used_status_last_updated IS NULL OR 
used_status_last_updated <= :maxUpdatedTime)
+              %3$s
+            ) AS unused
+            GROUP BY dataSource, %2$send%2$s, start
+            %4$s
+            """,
+        //CHECKSTYLE.ON: Regexp
+        dbTables.getSegmentsTable(),
+        connector.getQuoteString(),
+        connector.limitClause(maxSegmentsToScan),

Review Comment:
   Thanks for the context. After rechecking all 10 of 10 changed files, I would 
retain this finding. With no ordering, cursor, or rotation state, a stable 
query plan can return the same physical prefix on each rebuild. The locks need 
not be literally permanent: ongoing late-data activity can keep a prefix of 
streaming intervals locked while every row outside the global cap, including 
unrelated datasources, remains undiscovered. Since concurrent locking is 
deferred, this PR still needs deterministic progress or fairness.



-- 
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