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


##########
server/src/main/java/org/apache/druid/server/coordinator/loading/StrategicSegmentAssigner.java:
##########
@@ -645,20 +653,74 @@ private int updateReplicasInTier(
     }
 
     // Cancel loads and queue drops if the projected count exceeds the 
requirement
+    int dropsQueuedOnTier = 0;
     if (projectedReplicas > requiredReplicas) {
       int replicaSurplus = projectedReplicas - requiredReplicas;
       int canceledLoads =
           cancelOperations(SegmentAction.LOAD, replicaSurplus, segment, 
segmentStatus);
 
       int numReplicasToDrop = Math.min(replicaSurplus - canceledLoads, 
maxReplicasToDrop);
       if (numReplicasToDrop > 0) {
-        int dropsQueuedOnTier = dropReplicas(numReplicasToDrop, segment, tier, 
segmentStatus);
+        dropsQueuedOnTier = dropReplicas(numReplicasToDrop, segment, tier, 
segmentStatus);
         incrementStat(Stats.Segments.DROPPED, segment, tier, 
dropsQueuedOnTier);
-        return dropsQueuedOnTier;
       }
     }
 
-    return 0;
+    // Release partial-load rules that no longer apply. Done last so the 
load/drop decisions above claim their
+    // servers first: a replica that just picked up an action is no longer 
`isServingSegment`, so it is skipped here
+    // and reverted on a later run if it is still around.
+    if (replicasToRevert > 0) {
+      final int reverted = revertPartialProfileReplicas(segment, tier);
+      if (reverted > 0) {
+        incrementStat(Stats.Segments.PARTIAL_RULE_REVERTED, segment, tier, 
reverted);
+      }
+    }
+
+    return dropsQueuedOnTier;
+  }
+
+  /**
+   * Queues an in-place reload on every server in {@code tier} that serves 
{@code segment} under a
+   * {@link PartialLoadProfile}. The request carries the plain unwrapped 
{@code segment}, which is what tells the
+   * historical to release its rule holds rather than apply or swap one.
+   * <p>
+   * The replica count is deliberately left alone: these servers <em>are</em> 
serving, so they still satisfy the
+   * rule's replication requirement and must not be double-counted as a 
deficit. This only refreshes what they hold.
+   * <p>
+   * Servers are skipped when:
+   * <ul>
+   *   <li>they have any queued action, via {@link 
ServerHolder#isServingSegment}, which covers both the load/drop
+   *       decisions made earlier in this run and operations left over from a 
previous one;</li>
+   *   <li>their load queue is already at the configured {@code 
maxSegmentsInNodeLoadingQueue} budget for this run.</li>
+   *   <li>they are decommissioning, since their replicas are on the way out 
and reloading them is wasted work.</li>
+   * </ul>
+   * These are the same two eligibility conditions {@code 
PartialSegmentStatusInTier.canReloadAdditively} applies to
+   * the partial-load reconciler's in-place reload. {@link 
ServerHolder#canLoadSegment} is not usable here because it
+   * requires the server to <em>not</em> already have the segment, which is 
precisely the case being handled.
+   */
+  private int revertPartialProfileReplicas(DataSegment segment, String tier)
+  {
+    int numReverted = 0;
+    for (ServerHolder server : cluster.getManagedHistoricalsByTier(tier)) {
+      if (revertPartialProfileReplica(segment, server)) {
+        ++numReverted;
+      }
+    }
+    return numReverted;
+  }
+
+  /**
+   * Queues the in-place reload described by {@link 
#revertPartialProfileReplicas} on a single server, if that server
+   * is holding {@code segment} under a partial-load rule and has room in its 
load queue. Returns whether a reload was
+   * queued.
+   */
+  private boolean revertPartialProfileReplica(DataSegment segment, 
ServerHolder server)
+  {
+    return server.isServingSegment(segment)
+           && !server.isDecommissioning()
+           && !server.isLoadQueueFull()
+           && server.getServer().getPartialLoadProfile(segment.getId()) != null
+           && loadQueueManager.loadSegment(segment, server, 
SegmentAction.LOAD, null);

Review Comment:
   [P2] Keep in-place reverts out of replica surplus accounting
   
   If this in-place reload remains queued into the next coordinator run, 
`SegmentReplicaCountMap` counts both the already-announced partial replica and 
this `LOAD`. For a one-replica full-load rule, `projectedReplicas` therefore 
becomes 2 and `updateReplicasInTier` treats the reload as surplus, canceling it 
through `cancelOperations(LOAD)`. `HttpLoadQueuePeon` permits cancellation 
until dispatch; under queue backlog the coordinator can repeatedly cancel and 
requeue the revert, or with a finite queue budget cancel it and wait another 
run, preventing the partial-to-full transition from completing. Represent this 
as a non-replica refresh or exclude same-server reloads from replica counting 
and surplus cancellation, and add a two-run test with a pending revert.



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