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

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


The following commit(s) were added to refs/heads/master by this push:
     new fb4a6a8d60f [FLINK-40969][runtime] Fix rescale details lookup for 
evicted latest events.
fb4a6a8d60f is described below

commit fb4a6a8d60fe9b8a4a4affc41eea57451de79c0a
Author: danzhewuju <[email protected]>
AuthorDate: Mon Oct 12 18:03:24 2026 +0800

    [FLINK-40969][runtime] Fix rescale details lookup for evicted latest events.
    
    Signed-off-by: danzhewuju <[email protected]>
    Generated-by: GPT-6
---
 .../job/rescales/JobRescaleDetailsHandler.java     |   8 +-
 .../adaptive/timeline/RescalesStatsSnapshot.java   |  21 +--
 .../job/rescales/JobRescaleDetailsHandlerTest.java | 143 +++++++++++++++++++++
 3 files changed, 160 insertions(+), 12 deletions(-)

diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandler.java
 
b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandler.java
index d3f94a32c24..5d39bcf0f0b 100644
--- 
a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandler.java
+++ 
b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandler.java
@@ -101,20 +101,20 @@ public class JobRescaleDetailsHandler
     @Override
     public Collection<ArchivedJson> archiveJsonWithPath(ExecutionGraphInfo 
executionGraphInfo)
             throws IOException {
-        if (executionGraphInfo.getRescalesStatsSnapshot() == null
-                || 
executionGraphInfo.getRescalesStatsSnapshot().getRescaleHistory() == null) {
+        final RescalesStatsSnapshot rescalesStatsSnapshot =
+                executionGraphInfo.getRescalesStatsSnapshot();
+        if (rescalesStatsSnapshot == null) {
             return List.of();
         }
 
         List<ArchivedJson> archives = new ArrayList<>();
-        List<Rescale> rescales = 
executionGraphInfo.getRescalesStatsSnapshot().getRescaleHistory();
         final String commonPathPrefix =
                 JobRescaleDetailsHeaders.getInstance()
                         .getTargetRestEndpointURL()
                         .replace(
                                 ':' + JobIDPathParameter.KEY,
                                 executionGraphInfo.getJobId().toString());
-        for (Rescale rescale : rescales) {
+        for (Rescale rescale : rescalesStatsSnapshot.getRescales()) {
             archives.add(
                     new ArchivedJson(
                             commonPathPrefix.replace(
diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/timeline/RescalesStatsSnapshot.java
 
b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/timeline/RescalesStatsSnapshot.java
index 0dfd3fed380..109bd0f4eda 100644
--- 
a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/timeline/RescalesStatsSnapshot.java
+++ 
b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/timeline/RescalesStatsSnapshot.java
@@ -25,11 +25,10 @@ import javax.annotation.Nullable;
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 public class RescalesStatsSnapshot implements Serializable {
     private static final long serialVersionUID = 1L;
@@ -44,13 +43,15 @@ public class RescalesStatsSnapshot implements Serializable {
             Map<TerminalState, Rescale> latestRescales,
             RescalesSummarySnapshot rescalesSummarySnapshot) {
         this.rescaleHistory = List.copyOf(rescaleHistory);
-        this.idToRescaleMap =
-                rescaleHistory.stream()
-                        .collect(
-                                Collectors.toMap(
-                                        r -> 
r.getRescaleIdInfo().getRescaleUuid(),
-                                        Function.identity()));
         this.latestRescales = Map.copyOf(latestRescales);
+        final Map<AbstractID, Rescale> rescalesById = new HashMap<>();
+        for (Rescale rescale : rescaleHistory) {
+            rescalesById.put(rescale.getRescaleIdInfo().getRescaleUuid(), 
rescale);
+        }
+        for (Rescale rescale : latestRescales.values()) {
+            
rescalesById.putIfAbsent(rescale.getRescaleIdInfo().getRescaleUuid(), rescale);
+        }
+        this.idToRescaleMap = Map.copyOf(rescalesById);
         this.rescalesSummarySnapshot = rescalesSummarySnapshot;
     }
 
@@ -62,6 +63,10 @@ public class RescalesStatsSnapshot implements Serializable {
         return idToRescaleMap.get(rescaleId);
     }
 
+    public Collection<Rescale> getRescales() {
+        return idToRescaleMap.values();
+    }
+
     @Nullable
     public Rescale getLatestRescale(TerminalState terminalState) {
         return latestRescales.get(terminalState);
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandlerTest.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandlerTest.java
index be0275a42b6..4b0e129c247 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandlerTest.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/rescales/JobRescaleDetailsHandlerTest.java
@@ -44,11 +44,16 @@ import 
org.apache.flink.runtime.scheduler.adaptive.timeline.RescalesSummary;
 import 
org.apache.flink.runtime.scheduler.adaptive.timeline.SlotSharingGroupRescale;
 import org.apache.flink.runtime.scheduler.adaptive.timeline.TerminatedReason;
 import org.apache.flink.runtime.scheduler.adaptive.timeline.TriggerCause;
+import org.apache.flink.runtime.webmonitor.history.ArchivedJson;
 import org.apache.flink.testutils.TestingUtils;
 import org.apache.flink.util.concurrent.Executors;
 
+import 
org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpResponseStatus;
+
 import org.junit.jupiter.api.Test;
 
+import java.io.IOException;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -160,6 +165,144 @@ class JobRescaleDetailsHandlerTest {
         assertThat(jobRescaleDetails).isEqualTo(fromRescale(rescale, true));
     }
 
+    @Test
+    void testRequestLatestRescaleEvictedFromHistory()
+            throws HandlerRequestException, RestHandlerException {
+        final Rescale latestIgnoredRescale =
+                
createTerminatedRescale(TerminatedReason.RESOURCE_REQUIREMENTS_UPDATED);
+        final Rescale retainedCompletedRescale =
+                createTerminatedRescale(TerminatedReason.SUCCEEDED);
+        final ExecutionGraphInfo executionGraphInfo =
+                createExecutionGraphInfo(latestIgnoredRescale, 
retainedCompletedRescale);
+        final HandlerRequest<EmptyRequestBody> request =
+                createRequest(
+                        executionGraphInfo.getJobId(),
+                        
latestIgnoredRescale.getRescaleIdInfo().getRescaleUuid());
+
+        assertThat(testInstance.handleRequest(request, executionGraphInfo))
+                .isEqualTo(fromRescale(latestIgnoredRescale, true));
+    }
+
+    @Test
+    void testRequestHistoryOnlyRescale() throws HandlerRequestException, 
RestHandlerException {
+        final Rescale latestIgnoredRescale =
+                
createTerminatedRescale(TerminatedReason.RESOURCE_REQUIREMENTS_UPDATED);
+        final Rescale retainedCompletedRescale =
+                createTerminatedRescale(TerminatedReason.SUCCEEDED);
+        final Rescale historyOnlyFailedRescale =
+                createTerminatedRescale(TerminatedReason.EXCEPTION_OCCURRED);
+        final ExecutionGraphInfo executionGraphInfo =
+                createExecutionGraphInfo(
+                        latestIgnoredRescale,
+                        retainedCompletedRescale,
+                        List.of(historyOnlyFailedRescale, 
retainedCompletedRescale));
+        final HandlerRequest<EmptyRequestBody> request =
+                createRequest(
+                        executionGraphInfo.getJobId(),
+                        
historyOnlyFailedRescale.getRescaleIdInfo().getRescaleUuid());
+
+        assertThat(testInstance.handleRequest(request, executionGraphInfo))
+                .isEqualTo(fromRescale(historyOnlyFailedRescale, true));
+    }
+
+    @Test
+    void testRequestUnknownRescale() throws HandlerRequestException {
+        final Rescale latestIgnoredRescale =
+                
createTerminatedRescale(TerminatedReason.RESOURCE_REQUIREMENTS_UPDATED);
+        final Rescale retainedCompletedRescale =
+                createTerminatedRescale(TerminatedReason.SUCCEEDED);
+        final Rescale historyOnlyFailedRescale =
+                createTerminatedRescale(TerminatedReason.EXCEPTION_OCCURRED);
+        final ExecutionGraphInfo executionGraphInfo =
+                createExecutionGraphInfo(
+                        latestIgnoredRescale,
+                        retainedCompletedRescale,
+                        List.of(historyOnlyFailedRescale, 
retainedCompletedRescale));
+        final RescaleIdInfo.RescaleUUID unknownRescaleUuid = new 
RescaleIdInfo.RescaleUUID();
+        final HandlerRequest<EmptyRequestBody> request =
+                createRequest(executionGraphInfo.getJobId(), 
unknownRescaleUuid);
+
+        assertThatThrownBy(() -> testInstance.handleRequest(request, 
executionGraphInfo))
+                .isInstanceOfSatisfying(
+                        RestHandlerException.class,
+                        exception -> {
+                            assertThat(exception.getHttpResponseStatus())
+                                    .isEqualTo(HttpResponseStatus.NOT_FOUND);
+                            assertThat(exception.getMessage())
+                                    .contains(unknownRescaleUuid.toString());
+                        });
+    }
+
+    @Test
+    void testArchiveIncludesLatestRescaleEvictedFromHistory() throws 
IOException {
+        final Rescale latestIgnoredRescale =
+                
createTerminatedRescale(TerminatedReason.RESOURCE_REQUIREMENTS_UPDATED);
+        final Rescale retainedCompletedRescale =
+                createTerminatedRescale(TerminatedReason.SUCCEEDED);
+        final Rescale historyOnlyFailedRescale =
+                createTerminatedRescale(TerminatedReason.EXCEPTION_OCCURRED);
+        final ExecutionGraphInfo executionGraphInfo =
+                createExecutionGraphInfo(
+                        latestIgnoredRescale,
+                        retainedCompletedRescale,
+                        List.of(historyOnlyFailedRescale, 
retainedCompletedRescale));
+
+        final Collection<ArchivedJson> archives =
+                testInstance.archiveJsonWithPath(executionGraphInfo);
+
+        assertThat(archives)
+                .extracting(ArchivedJson::getPath)
+                .containsExactlyInAnyOrder(
+                        getDetailsPath(executionGraphInfo, 
latestIgnoredRescale),
+                        getDetailsPath(executionGraphInfo, 
retainedCompletedRescale),
+                        getDetailsPath(executionGraphInfo, 
historyOnlyFailedRescale));
+    }
+
+    private static ExecutionGraphInfo createExecutionGraphInfo(
+            Rescale latestIgnoredRescale, Rescale retainedCompletedRescale) {
+        return createExecutionGraphInfo(
+                latestIgnoredRescale, retainedCompletedRescale, 
List.of(retainedCompletedRescale));
+    }
+
+    private static ExecutionGraphInfo createExecutionGraphInfo(
+            Rescale latestIgnoredRescale,
+            Rescale retainedCompletedRescale,
+            List<Rescale> rescaleHistory) {
+        final RescalesSummary rescalesSummary = new RescalesSummary(1);
+        rescalesSummary.addTerminated(latestIgnoredRescale);
+        rescalesSummary.addTerminated(retainedCompletedRescale);
+        return new ExecutionGraphInfo(
+                new ArchivedExecutionGraphBuilder().build(),
+                List.of(),
+                null,
+                null,
+                new RescalesStatsSnapshot(
+                        rescaleHistory,
+                        Map.of(
+                                latestIgnoredRescale.getTerminalState(),
+                                latestIgnoredRescale,
+                                retainedCompletedRescale.getTerminalState(),
+                                retainedCompletedRescale),
+                        rescalesSummary.createSnapshot()));
+    }
+
+    private static Rescale createTerminatedRescale(TerminatedReason 
terminatedReason) {
+        return new Rescale(new RescaleIdInfo(new 
RescaleIdInfo.ResourceRequirementsID(), 1L))
+                .setStartTimestamp(1L)
+                .setEndTimestamp(2L)
+                .setTriggerCause(TriggerCause.UPDATE_REQUIREMENT)
+                .setTerminatedReason(terminatedReason);
+    }
+
+    private static String getDetailsPath(ExecutionGraphInfo 
executionGraphInfo, Rescale rescale) {
+        return JobRescaleDetailsHeaders.getInstance()
+                .getTargetRestEndpointURL()
+                .replace(':' + JobIDPathParameter.KEY, 
executionGraphInfo.getJobId().toString())
+                .replace(
+                        ':' + JobRescaleIDPathParameter.KEY,
+                        
rescale.getRescaleIdInfo().getRescaleUuid().toString());
+    }
+
     private static HandlerRequest<EmptyRequestBody> createRequest(
             JobID jobId, RescaleIdInfo.RescaleUUID rescaleUuid) throws 
HandlerRequestException {
         final Map<String, String> pathParameters = new HashMap<>();

Reply via email to