You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by pa...@apache.org on 2026/04/11 11:39:28 UTC
(flink) branch master updated (1719209491d -> 451372aba24)
This is an automated email from the ASF dual-hosted git repository.
panyuepeng pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
from 1719209491d [FLINK-38727][test] Fix flaky test EventTimeWindowCheckpointingITCase#testPreAggregatedSlidingTimeWindow by increasing ResourceManagerOptions.REQUIREMENTS_CHECK_DELAY (#27659)
new bdef5af63b6 [FLINK-38899][runtime-web] Introduce the Rescales/History sub-page for streaming jobs with the adaptive scheduler enabled
new 451372aba24 [hotfix][runtime-web] Handle hyphen in the rescales/configuration tab when the schedulerExecutionMode variable value is null
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../src/app/interfaces/job-rescales.ts | 59 +++-
.../pages/job/rescales/job-rescales.component.html | 366 ++++++++++++++++++++-
.../pages/job/rescales/job-rescales.component.less | 78 +++++
.../pages/job/rescales/job-rescales.component.ts | 79 ++++-
.../web-dashboard/src/app/services/job.service.ts | 18 +-
5 files changed, 590 insertions(+), 10 deletions(-)
(flink) 02/02: [hotfix][runtime-web] Handle hyphen in the rescales/configuration tab when the schedulerExecutionMode variable value is null
Posted by pa...@apache.org.
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 451372aba24453aaa96fabbe711f14d1f08b2346
Author: och5351 <dh...@gmail.com>
AuthorDate: Sat Apr 11 15:27:08 2026 +0900
[hotfix][runtime-web] Handle hyphen in the rescales/configuration tab when the schedulerExecutionMode variable value is null
Co-authored-by: Matthias Pohl <mp...@confluent.io>
Co-authored-by: Yuepeng Pan <hi...@gmail.com>
---
.../src/app/pages/job/rescales/job-rescales.component.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
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 c282aa8c945..08eb8f69a69 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
@@ -391,8 +391,7 @@
<ng-container *ngIf="rescalesConfig">
<tr>
<td>Scheduler Execution Mode</td>
- <td *ngIf="rescalesConfig['schedulerExecutionMode'] === 'REACTIVE'">REACTIVE</td>
- <td *ngIf="rescalesConfig['schedulerExecutionMode'] !== 'REACTIVE'"></td>
+ <td>{{ rescalesConfig['schedulerExecutionMode'] || '-' }}</td>
</tr>
<tr>
<td>Submission Resource Wait Timeout</td>
(flink) 01/02: [FLINK-38899][runtime-web] Introduce the Rescales/History sub-page for streaming jobs with the adaptive scheduler enabled
Posted by pa...@apache.org.
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 bdef5af63b6c1e64e0a266667f498111611e2b74
Author: och5351 <dh...@gmail.com>
AuthorDate: Sat Apr 11 15:25:24 2026 +0900
[FLINK-38899][runtime-web] Introduce the Rescales/History sub-page for streaming jobs with the adaptive scheduler enabled
Co-authored-by: Matthias Pohl <mp...@confluent.io>
Co-authored-by: Yuepeng Pan <hi...@gmail.com>
---
.../src/app/interfaces/job-rescales.ts | 59 +++-
.../pages/job/rescales/job-rescales.component.html | 363 +++++++++++++++++++++
.../pages/job/rescales/job-rescales.component.less | 78 +++++
.../pages/job/rescales/job-rescales.component.ts | 79 ++++-
.../web-dashboard/src/app/services/job.service.ts | 18 +-
5 files changed, 589 insertions(+), 8 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 b5db266ddb6..85e90c98428 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,7 +16,64 @@
* limitations under the License.
*/
-export interface RescalesConfig {
+export type RescalesHistory = BriefJobRescaleDetails[];
+
+export interface BriefJobRescaleDetails {
+ rescaleUuid: string;
+ resourceRequirementsUuid: string;
+ rescaleAttemptId: number;
+ vertices: { [jobVertexId: string]: VertexParallelismRescaleInfo };
+ slots: { [slotSharingGroupId: string]: SlotSharingGroupRescaleInfo };
+ schedulerStates: SchedulerState[];
+ startTimestampInMillis: number;
+ endTimestampInMillis: number;
+ terminalState: string;
+ triggerCause: string;
+ terminatedReason: string;
+}
+
+export interface JobRescaleDetails extends BriefJobRescaleDetails {}
+
+export interface VertexParallelismRescaleInfo {
+ jobVertexId: string;
+ jobVertexName: string;
+ slotSharingGroupId: string;
+ slotSharingGroupName: string;
+ desiredParallelism: number;
+ sufficientParallelism: number;
+ preRescaleParallelism: number;
+ postRescaleParallelism: number;
+}
+
+export interface SlotSharingGroupRescaleInfo {
+ slotSharingGroupId: string;
+ slotSharingGroupName: string;
+ requestResourceProfile: ResourceProfileInfo;
+ desiredSlots: number;
+ minimalRequiredSlots: number;
+ preRescaleSlots: number;
+ postRescaleSlots: number;
+ acquiredResourceProfile: ResourceProfileInfo;
+}
+
+export interface ResourceProfileInfo {
+ cpuCores: number;
+ taskHeapMemory: number;
+ taskOffHeapMemory: number;
+ managedMemory: number;
+ networkMemory: number;
+ extendedResources: { [key: string]: unknown };
+}
+
+export interface SchedulerState {
+ state: string;
+ enterTimestampInMillis: number;
+ leaveTimestampInMillis: number;
+ durationInMillis: number;
+ stringifiedException: string;
+}
+
+export interface JobRescaleConfigInfo {
rescaleHistoryMax: number;
schedulerExecutionMode: string;
submissionResourceWaitTimeoutInMillis: number;
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 2e90f2fbb7f..c282aa8c945 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
@@ -21,7 +21,358 @@
[nzSize]="'small'"
[nzAnimated]="{ inkBar: true, tabPane: false }"
[nzTabBarExtraContent]="extraTemplate"
+ (nzSelectedIndexChange)="refresh()"
>
+ <nz-tab nzTitle="History">
+ <nz-table
+ class="no-border small"
+ [nzSize]="'small'"
+ [nzData]="jobRescaleDetails || []"
+ [nzFrontPagination]="false"
+ [nzShowPagination]="false"
+ >
+ <thead>
+ <tr>
+ <th nzWidth="70px" nzAlign="center"></th>
+ <th
+ nz-tooltip
+ nzTooltipTitle="The unique ID in Rescale consists of 32 hexadecimal characters"
+ >
+ <strong>Rescale UUID</strong>
+ </th>
+ <th
+ nz-tooltip
+ nzTooltipTitle="The unique ID of resource requirements consists of 32 hexadecimal characters"
+ >
+ <strong>Requirements ID</strong>
+ </th>
+ <th
+ nz-tooltip
+ nzTooltipTitle="The number ID of Rescale attempts that occurred under the same resource requirements"
+ >
+ <strong>Attempt ID</strong>
+ </th>
+ <th nz-tooltip nzTooltipTitle="The reason that triggers the target Rescale">
+ <strong>Trigger Cause</strong>
+ </th>
+ <th nz-tooltip nzTooltipTitle="The end state of the target Rescale">
+ <strong>Terminal State</strong>
+ </th>
+ <th
+ nz-tooltip
+ nzTooltipTitle="The reason for the completion or termination of the target Rescale"
+ >
+ <strong>Terminated Reason</strong>
+ </th>
+ <th nz-tooltip nzTooltipTitle="The start time of the target Rescale">
+ <strong>Start Time</strong>
+ </th>
+ <th
+ nz-tooltip
+ nzTooltipTitle="Duration from the start of the rescale to its completion or until now"
+ >
+ <strong>Duration</strong>
+ </th>
+ <th nz-tooltip nzTooltipTitle="The end time of the target Rescale">
+ <strong>End Time</strong>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <ng-container *ngFor="let jobRescaleDetails of rescalesHistory; trackBy: trackById">
+ <tr>
+ <td
+ nzShowExpand
+ [nzExpand]="isExpanded(jobRescaleDetails.rescaleUuid)"
+ (nzExpandChange)="onExpandChange(jobRescaleDetails, $event)"
+ ></td>
+ <td nz-tooltip [nzTooltipTitle]="jobRescaleDetails.rescaleUuid">
+ {{ truncateUuid(jobRescaleDetails.rescaleUuid) }}
+ </td>
+ <td nz-tooltip [nzTooltipTitle]="jobRescaleDetails.resourceRequirementsUuid">
+ {{ truncateUuid(jobRescaleDetails.resourceRequirementsUuid) }}
+ </td>
+ <td>{{ jobRescaleDetails.rescaleAttemptId }}</td>
+ <td>{{ jobRescaleDetails.triggerCause }}</td>
+ <td>
+ <flink-job-badge
+ *ngIf="jobRescaleDetails.terminalState"
+ [state]="jobRescaleDetails.terminalState"
+ ></flink-job-badge>
+ <span *ngIf="!jobRescaleDetails.terminalState">-</span>
+ </td>
+ <td>{{ jobRescaleDetails.terminatedReason || '-' }}</td>
+ <td>
+ {{ jobRescaleDetails.startTimestampInMillis | date: 'yyyy-MM-dd HH:mm:ss.SSS' }}
+ </td>
+ <td>
+ {{
+ (jobRescaleDetails.endTimestampInMillis || Date.now()) -
+ jobRescaleDetails.startTimestampInMillis | humanizeDuration
+ }}
+ </td>
+ <td>
+ {{
+ jobRescaleDetails.endTimestampInMillis
+ ? (jobRescaleDetails.endTimestampInMillis | date: 'yyyy-MM-dd HH:mm:ss.SSS')
+ : '-'
+ }}
+ </td>
+ </tr>
+ <tr [nzExpand]="isExpanded(jobRescaleDetails.rescaleUuid)">
+ <td colspan="10" *ngIf="isExpanded(jobRescaleDetails.rescaleUuid)" class="collapse-td">
+ <div class="rescale-details-wrapper">
+ <div class="rescale-details-box">
+ <h3><strong>Rescale Details</strong></h3>
+ <tr>
+ <td>
+ <span>
+ <strong>Rescale UUID:</strong>
+ {{ truncateUuid(jobRescaleDetails.rescaleUuid) }}
+ </span>
+ <nz-divider nzType="vertical"></nz-divider>
+ <span>
+ <strong>Requirements ID:</strong>
+ {{ truncateUuid(jobRescaleDetails.resourceRequirementsUuid) }}
+ </span>
+ <nz-divider nzType="vertical"></nz-divider>
+ <span>
+ <strong>Attempt ID:</strong>
+ {{ jobRescaleDetails.rescaleAttemptId }}
+ </span>
+ <nz-divider nzType="vertical"></nz-divider>
+ <span>
+ <strong>Trigger Cause:</strong>
+ {{ jobRescaleDetails.triggerCause }}
+ </span>
+ <nz-divider nzType="vertical"></nz-divider>
+ <span>
+ <strong>Terminal State:</strong>
+ {{ jobRescaleDetails?.terminalState || '-' }}
+ </span>
+ <nz-divider nzType="vertical"></nz-divider>
+ <span>
+ <strong>Terminal Reason:</strong>
+ {{ jobRescaleDetails?.terminatedReason || '-' }}
+ </span>
+ </td>
+ </tr>
+ <ng-container *ngIf="getDetail(jobRescaleDetails.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(jobRescaleDetails.rescaleUuid)">Loading...</div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </ng-container>
+ </tbody>
+ </nz-table>
+ </nz-tab>
<nz-tab nzTitle="Configuration">
<nz-table
class="no-border small"
@@ -100,4 +451,16 @@
</button>
</ng-template>
+<ng-template #verticesTitle>
+ <strong>Vertices</strong>
+</ng-template>
+
+<ng-template #slotsTitle>
+ <strong>Slots</strong>
+</ng-template>
+
+<ng-template #schedulerStatesTitle>
+ <strong>Scheduler State History</strong>
+</ng-template>
+
<nz-empty *ngIf="!rescalesConfig"></nz-empty>
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 6b780729481..bbca1186be4 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
@@ -27,6 +27,14 @@
.ant-tabs-nav-list {
padding: 4px 16px;
}
+
+ > .ant-table-wrapper {
+ .ant-table {
+ > tbody > tr.ant-table-expanded-row > td {
+ padding: 0 !important;
+ }
+ }
+ }
}
}
@@ -37,3 +45,73 @@
nz-empty {
padding: 24px;
}
+
+::ng-deep {
+ .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;
+ }
+ }
+}
+
+.rescale-details-wrapper {
+ display: block;
+ box-sizing: border-box;
+ width: 100%;
+ padding: 0 24px;
+}
+
+.rescale-details-box {
+ margin: 16px 0;
+ padding: 16px;
+ border: 1px solid #f0f0f0;
+ background-color: #fff;
+
+ ::ng-deep {
+ nz-table.ant-table-wrapper {
+ display: block !important;
+ width: 100% !important;
+ margin-bottom: 16px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .ant-spin-nested-loading {
+ display: block;
+ width: 100%;
+ }
+
+ .ant-spin-container {
+ display: block;
+ width: 100%;
+ }
+
+ .ant-table {
+ width: 100% !important;
+ table-layout: fixed !important;
+
+ colgroup {
+ display: none;
+ }
+ }
+
+ table {
+ width: 100% !important;
+ table-layout: fixed !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 4b80254bedf..6f1a1ecc1c5 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,13 +16,20 @@
* limitations under the License.
*/
-import { NgIf } from '@angular/common';
+import { NgFor, NgIf, JsonPipe, DatePipe, KeyValuePipe } 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';
import { HumanizeDurationPipe } from '@flink-runtime-web/components/humanize-duration.pipe';
-import { RescalesConfig, JobDetail } from '@flink-runtime-web/interfaces';
+import { JobBadgeComponent } from '@flink-runtime-web/components/job-badge/job-badge.component';
+import {
+ BriefJobRescaleDetails,
+ JobRescaleDetails,
+ JobRescaleConfigInfo,
+ JobDetail,
+ RescalesHistory
+} from '@flink-runtime-web/interfaces';
import { JobService } from '@flink-runtime-web/services';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzCollapseModule } from 'ng-zorro-antd/collapse';
@@ -42,8 +49,13 @@ import { JobLocalService } from '../job-local.service';
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
NgIf,
+ NgFor,
+ JsonPipe,
+ DatePipe,
+ KeyValuePipe,
NzTabsModule,
NzDividerModule,
+ JobBadgeComponent,
HumanizeDurationPipe,
NzTableModule,
NzIconModule,
@@ -54,8 +66,12 @@ import { JobLocalService } from '../job-local.service';
]
})
export class JobRescalesComponent implements OnInit, OnDestroy {
- public rescalesConfig?: RescalesConfig;
+ public rescalesHistory?: RescalesHistory;
+ public rescaleDetailsMap = new Map<string, JobRescaleDetails>();
+ public rescalesConfig?: JobRescaleConfigInfo;
public jobDetail: JobDetail;
+ public expandedRowsSet = new Set<string>();
+ public readonly Date = Date;
private refresh$ = new Subject<void>();
private destroy$ = new Subject<void>();
@@ -71,6 +87,11 @@ export class JobRescalesComponent implements OnInit, OnDestroy {
.pipe(
switchMap(() =>
forkJoin([
+ this.jobService.loadRescalesHistory(this.jobDetail.jid).pipe(
+ catchError(() => {
+ return of(undefined);
+ })
+ ),
this.jobService.loadRescalesConfig(this.jobDetail.jid).pipe(
catchError(() => {
return of(undefined);
@@ -80,7 +101,8 @@ export class JobRescalesComponent implements OnInit, OnDestroy {
),
takeUntil(this.destroy$)
)
- .subscribe(([config]) => {
+ .subscribe(([history, config]) => {
+ this.rescalesHistory = history;
this.rescalesConfig = config;
this.cdr.markForCheck();
});
@@ -105,6 +127,55 @@ export class JobRescalesComponent implements OnInit, OnDestroy {
}
public refresh(): void {
+ const expandedUuids = Array.from(this.expandedRowsSet);
+
+ expandedUuids.forEach(uuid => {
+ this.jobService
+ .loadRescaleDetail(this.jobDetail.jid, uuid)
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(detail => {
+ this.rescaleDetailsMap.set(uuid, detail);
+ this.cdr.markForCheck();
+ });
+ });
+
this.refresh$.next();
}
+
+ public trackById(item: BriefJobRescaleDetails): string {
+ return item.rescaleUuid;
+ }
+
+ public onExpandChange(jobRescaleDetails: BriefJobRescaleDetails, expanded: boolean): void {
+ if (expanded) {
+ this.expandedRowsSet.add(jobRescaleDetails.rescaleUuid);
+ if (!this.rescaleDetailsMap.has(jobRescaleDetails.rescaleUuid)) {
+ this.jobService
+ .loadRescaleDetail(this.jobDetail.jid, jobRescaleDetails.rescaleUuid)
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(detail => {
+ this.rescaleDetailsMap.set(jobRescaleDetails.rescaleUuid, detail);
+ this.cdr.markForCheck();
+ });
+ }
+ } else {
+ this.expandedRowsSet.delete(jobRescaleDetails.rescaleUuid);
+ }
+ }
+
+ public isExpanded(rescaleUuid: string): boolean {
+ return this.expandedRowsSet.has(rescaleUuid);
+ }
+
+ public getDetail(rescaleUuid: string): JobRescaleDetails | undefined {
+ return this.rescaleDetailsMap.get(rescaleUuid);
+ }
+
+ public truncateUuid(uuid: string): string {
+ return uuid ? uuid.substring(0, 8) : '';
+ }
+
+ public truncateName(name: string, maxLength: number = 32): string {
+ return name && name.length > maxLength ? `${name.substring(0, maxLength)}...` : name;
+ }
}
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 313a3e9308e..aff1e903b6d 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,7 +26,9 @@ import {
CheckpointConfig,
CheckpointDetail,
CheckpointSubTask,
- RescalesConfig,
+ RescalesHistory,
+ JobRescaleDetails,
+ JobRescaleConfigInfo,
JobAccumulators,
JobBackpressure,
JobConfig,
@@ -179,8 +181,18 @@ export class JobService {
);
}
- public loadRescalesConfig(jobId: string): Observable<RescalesConfig> {
- return this.httpClient.get<RescalesConfig>(`${this.configService.BASE_URL}/jobs/${jobId}/rescales/config`);
+ public loadRescalesHistory(jobId: string): Observable<RescalesHistory> {
+ return this.httpClient.get<RescalesHistory>(`${this.configService.BASE_URL}/jobs/${jobId}/rescales/history`);
+ }
+
+ public loadRescaleDetail(jobId: string, rescaleUuid: string): Observable<JobRescaleDetails> {
+ return this.httpClient.get<JobRescaleDetails>(
+ `${this.configService.BASE_URL}/jobs/${jobId}/rescales/details/${rescaleUuid}`
+ );
+ }
+
+ public loadRescalesConfig(jobId: string): Observable<JobRescaleConfigInfo> {
+ return this.httpClient.get<JobRescaleConfigInfo>(`${this.configService.BASE_URL}/jobs/${jobId}/rescales/config`);
}
public loadJobResourceRequirements(jobId: string): Observable<JobResourceRequirements> {