yujun777 commented on code in PR #68390:
URL: https://github.com/apache/doris/pull/68390#discussion_r4090988743


##########
fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java:
##########
@@ -510,9 +565,46 @@ && hasUnusableIvmStream()) {
                     mtmv.getName(), getTaskId());
             return Lists.newArrayList(RefreshAttemptType.COMPLETE);
         }
+        // Every partition either needs a rebuild or was never filled, and at 
least one needs a rebuild:
+        // COMPLETE then does nothing the per-partition routing would not, in 
one read of the MV.
+        if (!request.explicitPartitions && 
attempts.contains(RefreshAttemptType.IVM)
+                && shouldEscalateToComplete()) {
+            LOG.info("Every MV partition needs a rebuild or has no data yet, 
mv={}, taskId={}. "
+                    + "Continuing with COMPLETE refresh.", mtmv.getName(), 
getTaskId());
+            return Lists.newArrayList(RefreshAttemptType.COMPLETE);
+        }
         return attempts;
     }
 
+    /**
+     * Notes that this refresh rebuilds partitions the request did not ask to 
rebuild, which is what a
+     * strict INCREMENTAL request cannot tell from its result otherwise: it 
reports the count, and a request
+     * that asked for a complete refresh reports nothing because rebuilding 
everything is what it asked for.
+     */
+    private void recordRebuiltPartitions(RefreshRequest request, int 
rebuiltPartitions) {
+        // Only an IVM MV has a baseline to rebuild: a plain MV's COMPLETE is 
the only way it refreshes at
+        // all, so reporting it there would put a rebuild count on every 
ordinary refresh.
+        if (!mtmv.isIvm() || request.refreshMode == RefreshMode.COMPLETE) {
+            return;
+        }
+        ivmRebuiltPartitions = rebuiltPartitions;
+    }
+
+    /**
+     * Whether every MV partition needs a rebuild, which is when COMPLETE does 
nothing the per-partition
+     * routing would not.
+     *
+     * <p>A partition that holds data and does not need one makes this false: 
COMPLETE would recompute it
+     * for nothing, which is the waste the per-partition routing exists to 
avoid. A partition that was
+     * never refreshed does not count against it -- COMPLETE fills it, which 
its routing branch would do as
+     * well -- and it needs no clause of its own: an aligned entry is {@code 
{0, 1}}, so it is behind its
+     * requirement already. An MV with no partitions is not an escalation 
either.
+     */
+    private boolean shouldEscalateToComplete() {
+        Map<String, MTMVPartitionState> states = mtmv.getPartitionStates();

Review Comment:
   Fixed, and the allocation is gone rather than reduced: the verdict is 
answered in place, under the MV read lock, by the MV that owns the states 
(`MTMV#allPartitionsNeedRebuild`), so the ordinary not-all-dirty case allocates 
nothing at all. `MTMVTask#shouldEscalateToComplete` is deleted and the call 
site asks the MV directly. The detached snapshot `executeIvmAttempt` takes for 
routing is now the only full copy, as you suggested.



##########
fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java:
##########
@@ -342,7 +367,16 @@ public void run() throws JobException {
                         }
                         break;
                     case COMPLETE:
-                        executeCompleteAttempt(refreshContext, ctx);
+                        try {
+                            executeCompleteAttempt(refreshContext, ctx);
+                        } finally {
+                            // Counted from the groups that committed, like 
the rebuild phase above: a
+                            // whole-MV rebuild that failed in a later group 
has still replaced the groups
+                            // before it, and those keep the epochs and 
snapshots they published (see
+                            // MTMV#addTaskResult), so reporting none of them 
would hide the partial work
+                            // this count exists to expose.
+                            recordRebuiltPartitions(request, 
completedPartitions.size());

Review Comment:
   Fixed, and taken further than "initialise it before the try": the count is 
read from the same accumulator the task reports its progress with, null-safely, 
so there is one source for both call sites rather than two shapes of the same 
number, and an attempt that committed nothing reads as zero instead of 
throwing. Both pre-executor exits you named are covered by unit tests now -- 
the empty whole-MV refresh, and the one the stream reconciliation throws out of 
-- and removing the null-safe read makes the first of them fail with exactly 
the NPE you described.



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