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

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

commit 5ba9166f729d88cacdcdeca638094ce9dea8e655
Author: och5351 <[email protected]>
AuthorDate: Mon Apr 13 22:40:14 2026 +0900

    [FLINK-38898][runtime-web] Introduce the Rescales/Overview sub-page for 
streaming jobs with the adaptive scheduler enabled
    
    Co-authored-by: Yuepeng Pan <[email protected]>
    Co-authored-by: Matthias Pohl <[email protected]>
---
 .../src/app/interfaces/job-rescales.ts             |  18 +
 .../pages/job/rescales/job-rescales.component.html | 402 +++++++++++++++++++++
 .../pages/job/rescales/job-rescales.component.less |  78 +++-
 .../pages/job/rescales/job-rescales.component.ts   |  49 ++-
 .../web-dashboard/src/app/services/job.service.ts  |   5 +
 5 files changed, 537 insertions(+), 15 deletions(-)

diff --git a/flink-runtime-web/web-dashboard/src/app/interfaces/job-rescales.ts 
b/flink-runtime-web/web-dashboard/src/app/interfaces/job-rescales.ts
index 85e90c98428..0cf7f521c36 100644
--- a/flink-runtime-web/web-dashboard/src/app/interfaces/job-rescales.ts
+++ b/flink-runtime-web/web-dashboard/src/app/interfaces/job-rescales.ts
@@ -16,6 +16,24 @@
  * limitations under the License.
  */
 
+export interface RescalesOverview {
+  rescalesCounts: RescalesCounts;
+  latest: LatestRescales;
+}
+
+export interface RescalesCounts {
+  ignored: number;
+  inProgress: number;
+  completed: number;
+  failed: number;
+}
+
+export interface LatestRescales {
+  completed: BriefJobRescaleDetails | null;
+  failed: BriefJobRescaleDetails | null;
+  ignored: BriefJobRescaleDetails | null;
+}
+
 export type RescalesHistory = BriefJobRescaleDetails[];
 
 export interface BriefJobRescaleDetails {
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.html
 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.html
index 08eb8f69a69..1b4342fac02 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.html
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.html
@@ -23,6 +23,408 @@
   [nzTabBarExtraContent]="extraTemplate"
   (nzSelectedIndexChange)="refresh()"
 >
+  <nz-tab nzTitle="Overview">
+    <!-- Reusable template for rescale details -->
+    <ng-template #rescaleDetailsTemplate let-rescale="rescale">
+      <div class="overview-rescale-section">
+        <h3><strong>Rescale Details</strong></h3>
+        <div>
+          <span>
+            <strong>Rescale UUID:</strong>
+            <span nz-tooltip [nzTooltipTitle]="rescale.rescaleUuid">
+              {{ truncateUuid(rescale.rescaleUuid) }}
+            </span>
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Requirements ID:</strong>
+            <span nz-tooltip 
[nzTooltipTitle]="rescale.resourceRequirementsUuid">
+              {{ truncateUuid(rescale.resourceRequirementsUuid) }}
+            </span>
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Attempt ID:</strong>
+            {{ rescale.rescaleAttemptId }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Trigger Cause:</strong>
+            {{ rescale.triggerCause }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Terminal State:</strong>
+            {{ rescale?.terminalState || '-' }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Terminal Reason:</strong>
+            {{ rescale?.terminatedReason || '-' }}
+          </span>
+        </div>
+        <ng-container *ngIf="getDetail(rescale.rescaleUuid) as detail">
+          <nz-table
+            class="small"
+            [nzData]="detail.vertices | keyvalue"
+            [nzSize]="'small'"
+            [nzFrontPagination]="false"
+            [nzShowPagination]="false"
+            [nzTitle]="verticesTitle"
+            [nzBordered]="true"
+          >
+            <thead>
+              <tr>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The unique ID of target JobVertex consists 
of 32 hexadecimal characters"
+                >
+                  <strong>ID</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The short name of target 
vertex">
+                  <strong>Name</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The previous parallelism of target vertex 
before the current rescale"
+                >
+                  <strong>Previous Parallelism</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The acquired parallelism of target vertex 
after the current rescale"
+                >
+                  <strong>Acquired Parallelism</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The desired parallelism of the 
target vertex">
+                  <strong>Desired Parallelism</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The minimal parallelism of 
target vertex to run">
+                  <strong>Sufficient Parallelism</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The unique ID of the slot sharing group 
consists of 32 hexadecimal characters"
+                >
+                  <strong>Slot Sharing Group ID</strong>
+                </th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr *ngFor="let vertex of detail.vertices | keyvalue">
+                <td nz-tooltip [nzTooltipTitle]="vertex.value.jobVertexId">
+                  {{ truncateUuid(vertex.value.jobVertexId) }}
+                </td>
+                <td nz-tooltip [nzTooltipTitle]="vertex.value.jobVertexName">
+                  {{ truncateName(vertex.value.jobVertexName) }}
+                </td>
+                <td>{{ vertex.value.preRescaleParallelism }}</td>
+                <td>{{ vertex.value.postRescaleParallelism || '-' }}</td>
+                <td>{{ vertex.value.desiredParallelism }}</td>
+                <td>{{ vertex.value.sufficientParallelism }}</td>
+                <td nz-tooltip 
[nzTooltipTitle]="vertex.value.slotSharingGroupId">
+                  {{ truncateUuid(vertex.value.slotSharingGroupId) }}
+                </td>
+              </tr>
+            </tbody>
+          </nz-table>
+
+          <nz-table
+            class="small"
+            [nzData]="detail.slots | keyvalue"
+            [nzSize]="'small'"
+            [nzFrontPagination]="false"
+            [nzShowPagination]="false"
+            [nzTitle]="slotsTitle"
+            [nzBordered]="true"
+          >
+            <thead>
+              <tr>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The ID of the slot sharing group to which 
the slot belongs consists of 32 hexadecimal characters"
+                >
+                  <strong>Slot Sharing Group ID</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The name of the slot sharing group to which 
the slot belongs"
+                >
+                  <strong>Slot Sharing Group Name</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The previous number of slots 
before the rescale">
+                  <strong>Previous Slots</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The acquired number of slots 
after the rescale">
+                  <strong>Acquired Slots</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The desired number of slots of 
the rescale">
+                  <strong>Desired Slots</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The minimal number of slots to deploy tasks 
in the rescale"
+                >
+                  <strong>Sufficient Slots</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The required resource profile of the slot 
sharing group in the rescale"
+                >
+                  <strong>Required Profile</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The acquired resource profile of the slot 
sharing group in the rescale"
+                >
+                  <strong>Acquired Profile</strong>
+                </th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr *ngFor="let slot of detail.slots | keyvalue">
+                <td nz-tooltip 
[nzTooltipTitle]="slot.value.slotSharingGroupId">
+                  {{ truncateUuid(slot.value.slotSharingGroupId) }}
+                </td>
+                <td>{{ slot.value.slotSharingGroupName }}</td>
+                <td>{{ slot.value.preRescaleSlots }}</td>
+                <td>{{ slot.value.postRescaleSlots || '-' }}</td>
+                <td>{{ slot.value.desiredSlots }}</td>
+                <td>{{ slot.value.minimalRequiredSlots }}</td>
+                <td>
+                  <pre style="margin: 0">{{ slot.value.requestResourceProfile 
| json }}</pre>
+                </td>
+                <td>
+                  <pre style="margin: 0">{{ slot.value.acquiredResourceProfile 
| json }}</pre>
+                </td>
+              </tr>
+            </tbody>
+          </nz-table>
+
+          <nz-table
+            class="small"
+            [nzData]="detail.schedulerStates"
+            [nzSize]="'small'"
+            [nzFrontPagination]="false"
+            [nzShowPagination]="false"
+            [nzTitle]="schedulerStatesTitle"
+            [nzBordered]="true"
+          >
+            <thead>
+              <tr>
+                <th nz-tooltip nzTooltipTitle="The scheduler state name">
+                  <strong>State</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The time to enter the state">
+                  <strong>Enter Time</strong>
+                </th>
+                <th nz-tooltip nzTooltipTitle="The time to leave the state">
+                  <strong>Leave Time</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The duration time from enter time to leave 
time of the state"
+                >
+                  <strong>Duration</strong>
+                </th>
+                <th
+                  nz-tooltip
+                  nzTooltipTitle="The exception information about current 
rescale during the state"
+                >
+                  <strong>Exception</strong>
+                </th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr *ngFor="let state of detail.schedulerStates">
+                <td>{{ state.state }}</td>
+                <td>{{ state.enterTimestampInMillis | date: 'yyyy-MM-dd 
HH:mm:ss.SSS' }}</td>
+                <td>{{ state.leaveTimestampInMillis | date: 'yyyy-MM-dd 
HH:mm:ss.SSS' }}</td>
+                <td>{{ state.durationInMillis | humanizeDuration }}</td>
+                <td>{{ state.stringifiedException }}</td>
+              </tr>
+            </tbody>
+          </nz-table>
+        </ng-container>
+
+        <div *ngIf="!getDetail(rescale.rescaleUuid)">Loading...</div>
+      </div>
+    </ng-template>
+
+    <ng-container *ngIf="rescalesOverview">
+      <div class="rescale-header rescale-counts-header">
+        <h3><strong>Rescale Counts</strong></h3>
+        <span class="rescale-time-info">
+          <span>
+            <strong>Total:</strong>
+            {{ getTotalRescaleCount() }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>In Progress:</strong>
+            {{ rescalesOverview.rescalesCounts.inProgress }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Completed:</strong>
+            {{ rescalesOverview.rescalesCounts.completed }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Failed:</strong>
+            {{ rescalesOverview.rescalesCounts.failed }}
+          </span>
+          <nz-divider nzType="vertical"></nz-divider>
+          <span>
+            <strong>Ignored:</strong>
+            {{ rescalesOverview.rescalesCounts.ignored }}
+          </span>
+        </span>
+      </div>
+      <nz-divider></nz-divider>
+
+      <ng-container *ngIf="rescalesOverview.latest.completed">
+        <div class="rescale-header">
+          <h3><strong>Latest Completed Rescale</strong></h3>
+          <span class="rescale-time-info">
+            <span>
+              <strong>Start Time:</strong>
+              {{
+                rescalesOverview.latest.completed.startTimestampInMillis
+                  | date: 'yyyy-MM-dd HH:mm:ss.SSS'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>End Time:</strong>
+              {{
+                rescalesOverview.latest.completed.endTimestampInMillis
+                  ? (rescalesOverview.latest.completed.endTimestampInMillis
+                    | date: 'yyyy-MM-dd HH:mm:ss.SSS')
+                  : '-'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>Duration:</strong>
+              {{
+                (rescalesOverview.latest.completed.endTimestampInMillis || 
Date.now()) -
+                  rescalesOverview.latest.completed.startTimestampInMillis | 
humanizeDuration
+              }}
+            </span>
+          </span>
+        </div>
+        <nz-divider></nz-divider>
+        <ng-container
+          *ngTemplateOutlet="
+            rescaleDetailsTemplate;
+            context: { rescale: rescalesOverview.latest.completed }
+          "
+        ></ng-container>
+      </ng-container>
+      <ng-container *ngIf="!rescalesOverview.latest.completed">
+        <div class="rescale-header">
+          <h3><strong>Latest Completed Rescale</strong></h3>
+          <span class="rescale-time-info">None</span>
+        </div>
+        <nz-divider></nz-divider>
+      </ng-container>
+
+      <ng-container *ngIf="rescalesOverview.latest.ignored">
+        <div class="rescale-header">
+          <h3><strong>Latest Ignored Rescale</strong></h3>
+          <span class="rescale-time-info">
+            <span>
+              <strong>Start Time:</strong>
+              {{
+                rescalesOverview.latest.ignored.startTimestampInMillis
+                  | date: 'yyyy-MM-dd HH:mm:ss.SSS'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>End Time:</strong>
+              {{
+                rescalesOverview.latest.ignored.endTimestampInMillis
+                  ? (rescalesOverview.latest.ignored.endTimestampInMillis
+                    | date: 'yyyy-MM-dd HH:mm:ss.SSS')
+                  : '-'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>Duration:</strong>
+              {{
+                (rescalesOverview.latest.ignored.endTimestampInMillis || 
Date.now()) -
+                  rescalesOverview.latest.ignored.startTimestampInMillis | 
humanizeDuration
+              }}
+            </span>
+          </span>
+        </div>
+        <nz-divider></nz-divider>
+        <ng-container
+          *ngTemplateOutlet="
+            rescaleDetailsTemplate;
+            context: { rescale: rescalesOverview.latest.ignored }
+          "
+        ></ng-container>
+      </ng-container>
+      <ng-container *ngIf="!rescalesOverview.latest.ignored">
+        <div class="rescale-header">
+          <h3><strong>Latest Ignored Rescale</strong></h3>
+          <span class="rescale-time-info">None</span>
+        </div>
+        <nz-divider></nz-divider>
+      </ng-container>
+
+      <ng-container *ngIf="rescalesOverview.latest.failed">
+        <div class="rescale-header">
+          <h3><strong>Latest Failed Rescale</strong></h3>
+          <span class="rescale-time-info">
+            <span>
+              <strong>Start Time:</strong>
+              {{
+                rescalesOverview.latest.failed.startTimestampInMillis
+                  | date: 'yyyy-MM-dd HH:mm:ss.SSS'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>End Time:</strong>
+              {{
+                rescalesOverview.latest.failed.endTimestampInMillis
+                  ? (rescalesOverview.latest.failed.endTimestampInMillis
+                    | date: 'yyyy-MM-dd HH:mm:ss.SSS')
+                  : '-'
+              }}
+            </span>
+            <nz-divider nzType="vertical"></nz-divider>
+            <span>
+              <strong>Duration:</strong>
+              {{
+                (rescalesOverview.latest.failed.endTimestampInMillis || 
Date.now()) -
+                  rescalesOverview.latest.failed.startTimestampInMillis | 
humanizeDuration
+              }}
+            </span>
+          </span>
+        </div>
+        <nz-divider></nz-divider>
+        <ng-container
+          *ngTemplateOutlet="
+            rescaleDetailsTemplate;
+            context: { rescale: rescalesOverview.latest.failed }
+          "
+        ></ng-container>
+      </ng-container>
+      <ng-container *ngIf="!rescalesOverview.latest.failed">
+        <div class="rescale-header">
+          <h3><strong>Latest Failed Rescale</strong></h3>
+          <span class="rescale-time-info">None</span>
+        </div>
+        <nz-divider></nz-divider>
+      </ng-container>
+    </ng-container>
+  </nz-tab>
   <nz-tab nzTitle="History">
     <nz-table
       class="no-border small"
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.less
 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.less
index bbca1186be4..a5ad73dde77 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.less
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.less
@@ -22,6 +22,37 @@
       position: relative;
       top: -16px;
       padding: 24px;
+
+      > h3 {
+        margin-top: 8px;
+        margin-bottom: 8px;
+      }
+
+      > nz-divider {
+        margin: 8px 0;
+      }
+
+      .rescale-header {
+        display: flex;
+        align-items: center;
+        margin-top: 8px;
+        margin-bottom: 8px;
+
+        h3 {
+          flex-shrink: 0;
+          width: 280px;
+          margin: 0;
+        }
+
+        .rescale-time-info {
+          color: rgba(0, 0, 0, 0.85);
+          font-size: 14px;
+        }
+
+        &.rescale-counts-header {
+          margin-top: -2px;
+        }
+      }
     }
 
     .ant-tabs-nav-list {
@@ -35,6 +66,24 @@
         }
       }
     }
+
+    .nz-disable-td {
+      width: 100% !important;
+      padding: 0 !important;
+    }
+
+    .collapse-td {
+      width: 100% !important;
+      padding: 0 !important;
+    }
+
+    tr.ant-table-expanded-row {
+      width: 100%;
+
+      > td {
+        width: 100% !important;
+      }
+    }
   }
 }
 
@@ -46,22 +95,22 @@ nz-empty {
   padding: 24px;
 }
 
-::ng-deep {
-  .nz-disable-td {
-    width: 100% !important;
-    padding: 0 !important;
-  }
+.overview-rescale-section {
+  margin: 16px 0;
+  padding: 16px;
+  border: 1px solid #f0f0f0;
+  background-color: #fff;
 
-  .collapse-td {
-    width: 100% !important;
-    padding: 0 !important;
+  h3 {
+    margin-top: 0;
+    margin-bottom: 8px;
   }
 
-  tr.ant-table-expanded-row {
-    width: 100%;
+  nz-table {
+    margin-bottom: 16px;
 
-    > td {
-      width: 100% !important;
+    &:last-child {
+      margin-bottom: 0;
     }
   }
 }
@@ -79,6 +128,11 @@ nz-empty {
   border: 1px solid #f0f0f0;
   background-color: #fff;
 
+  h3 {
+    margin-top: 0;
+    margin-bottom: 8px;
+  }
+
   ::ng-deep {
     nz-table.ant-table-wrapper {
       display: block !important;
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.ts
 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.ts
index 6f1a1ecc1c5..e25d8747d44 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.ts
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/job/rescales/job-rescales.component.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import { NgFor, NgIf, JsonPipe, DatePipe, KeyValuePipe } from 
'@angular/common';
+import { NgFor, NgIf, JsonPipe, DatePipe, KeyValuePipe, NgTemplateOutlet } 
from '@angular/common';
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, 
OnInit } from '@angular/core';
 import { forkJoin, of, Subject } from 'rxjs';
 import { catchError, distinctUntilChanged, switchMap, takeUntil } from 
'rxjs/operators';
@@ -28,7 +28,8 @@ import {
   JobRescaleDetails,
   JobRescaleConfigInfo,
   JobDetail,
-  RescalesHistory
+  RescalesHistory,
+  RescalesOverview
 } from '@flink-runtime-web/interfaces';
 import { JobService } from '@flink-runtime-web/services';
 import { NzButtonModule } from 'ng-zorro-antd/button';
@@ -53,6 +54,7 @@ import { JobLocalService } from '../job-local.service';
     JsonPipe,
     DatePipe,
     KeyValuePipe,
+    NgTemplateOutlet,
     NzTabsModule,
     NzDividerModule,
     JobBadgeComponent,
@@ -66,6 +68,7 @@ import { JobLocalService } from '../job-local.service';
   ]
 })
 export class JobRescalesComponent implements OnInit, OnDestroy {
+  public rescalesOverview?: RescalesOverview;
   public rescalesHistory?: RescalesHistory;
   public rescaleDetailsMap = new Map<string, JobRescaleDetails>();
   public rescalesConfig?: JobRescaleConfigInfo;
@@ -87,6 +90,11 @@ export class JobRescalesComponent implements OnInit, 
OnDestroy {
       .pipe(
         switchMap(() =>
           forkJoin([
+            this.jobService.loadRescalesOverview(this.jobDetail.jid).pipe(
+              catchError(() => {
+                return of(undefined);
+              })
+            ),
             this.jobService.loadRescalesHistory(this.jobDetail.jid).pipe(
               catchError(() => {
                 return of(undefined);
@@ -101,9 +109,24 @@ export class JobRescalesComponent implements OnInit, 
OnDestroy {
         ),
         takeUntil(this.destroy$)
       )
-      .subscribe(([history, config]) => {
+      .subscribe(([overview, history, config]) => {
+        this.rescalesOverview = overview;
         this.rescalesHistory = history;
         this.rescalesConfig = config;
+
+        // Load details for latest rescales in overview
+        if (overview?.latest) {
+          if (overview.latest.completed?.rescaleUuid) {
+            
this.loadRescaleDetailIfNeeded(overview.latest.completed.rescaleUuid);
+          }
+          if (overview.latest.failed?.rescaleUuid) {
+            this.loadRescaleDetailIfNeeded(overview.latest.failed.rescaleUuid);
+          }
+          if (overview.latest.ignored?.rescaleUuid) {
+            
this.loadRescaleDetailIfNeeded(overview.latest.ignored.rescaleUuid);
+          }
+        }
+
         this.cdr.markForCheck();
       });
 
@@ -178,4 +201,24 @@ export class JobRescalesComponent implements OnInit, 
OnDestroy {
   public truncateName(name: string, maxLength: number = 32): string {
     return name && name.length > maxLength ? `${name.substring(0, 
maxLength)}...` : name;
   }
+
+  public getTotalRescaleCount(): number {
+    if (!this.rescalesOverview?.rescalesCounts) {
+      return 0;
+    }
+    const counts = this.rescalesOverview.rescalesCounts;
+    return counts.inProgress + counts.completed + counts.failed + 
counts.ignored;
+  }
+
+  private loadRescaleDetailIfNeeded(rescaleUuid: string): void {
+    if (!this.rescaleDetailsMap.has(rescaleUuid)) {
+      this.jobService
+        .loadRescaleDetail(this.jobDetail.jid, rescaleUuid)
+        .pipe(takeUntil(this.destroy$))
+        .subscribe(detail => {
+          this.rescaleDetailsMap.set(rescaleUuid, detail);
+          this.cdr.markForCheck();
+        });
+    }
+  }
 }
diff --git a/flink-runtime-web/web-dashboard/src/app/services/job.service.ts 
b/flink-runtime-web/web-dashboard/src/app/services/job.service.ts
index aff1e903b6d..a5bb8252e0b 100644
--- a/flink-runtime-web/web-dashboard/src/app/services/job.service.ts
+++ b/flink-runtime-web/web-dashboard/src/app/services/job.service.ts
@@ -26,6 +26,7 @@ import {
   CheckpointConfig,
   CheckpointDetail,
   CheckpointSubTask,
+  RescalesOverview,
   RescalesHistory,
   JobRescaleDetails,
   JobRescaleConfigInfo,
@@ -181,6 +182,10 @@ export class JobService {
     );
   }
 
+  public loadRescalesOverview(jobId: string): Observable<RescalesOverview> {
+    return 
this.httpClient.get<RescalesOverview>(`${this.configService.BASE_URL}/jobs/${jobId}/rescales/overview`);
+  }
+
   public loadRescalesHistory(jobId: string): Observable<RescalesHistory> {
     return 
this.httpClient.get<RescalesHistory>(`${this.configService.BASE_URL}/jobs/${jobId}/rescales/history`);
   }

Reply via email to