This is an automated email from the ASF dual-hosted git repository.
RocMarshal 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 e1125e34ce1 [FLINK-39490][web] Improve feedback when cluster is
unreachable (#27971)
e1125e34ce1 is described below
commit e1125e34ce13af21a85e88e24dc4ffb4cb9c4d67
Author: Purushottam Sinha <[email protected]>
AuthorDate: Fri Jul 31 21:45:43 2026 +0530
[FLINK-39490][web] Improve feedback when cluster is unreachable (#27971)
---
.../web-dashboard/src/app/app.component.ts | 4 +++-
.../web-dashboard/src/app/app.interceptor.ts | 27 +++++++++++++++++++++-
.../web-dashboard/src/app/services/job.service.ts | 7 +++---
.../src/app/services/status.service.ts | 19 ++++++++++++++-
4 files changed, 51 insertions(+), 6 deletions(-)
diff --git a/flink-runtime-web/web-dashboard/src/app/app.component.ts
b/flink-runtime-web/web-dashboard/src/app/app.component.ts
index 47fb9b66d47..daf4ada8c89 100644
--- a/flink-runtime-web/web-dashboard/src/app/app.component.ts
+++ b/flink-runtime-web/web-dashboard/src/app/app.component.ts
@@ -84,5 +84,7 @@ export class AppComponent {
constructor(
public statusService: StatusService,
private cdr: ChangeDetectorRef
- ) {}
+ ) {
+ this.statusService.registerAppCdr(this.cdr);
+ }
}
diff --git a/flink-runtime-web/web-dashboard/src/app/app.interceptor.ts
b/flink-runtime-web/web-dashboard/src/app/app.interceptor.ts
index cfc85d20091..c77b890da33 100644
--- a/flink-runtime-web/web-dashboard/src/app/app.interceptor.ts
+++ b/flink-runtime-web/web-dashboard/src/app/app.interceptor.ts
@@ -21,12 +21,13 @@ import {
HttpHandler,
HttpInterceptor,
HttpRequest,
+ HttpResponse,
HttpResponseBase,
HttpStatusCode
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
-import { catchError } from 'rxjs/operators';
+import { catchError, tap } from 'rxjs/operators';
import { StatusService } from '@flink-runtime-web/services';
import { NzNotificationService, NzNotificationDataOptions } from
'ng-zorro-antd/notification';
@@ -48,6 +49,16 @@ export class AppInterceptor implements HttpInterceptor {
};
return next.handle(req.clone({ withCredentials: true })).pipe(
+ tap(event => {
+ if (event instanceof HttpResponse) {
+ if (this.statusService.networkFailureCount > 0) {
+ this.statusService.networkFailureCount = 0;
+ }
+ if (this.statusService.networkErrorNotificationId) {
+
this.notificationService.remove(this.statusService.networkErrorNotificationId);
+ }
+ }
+ }),
catchError(res => {
if (
res instanceof HttpResponseBase &&
@@ -67,6 +78,20 @@ export class AppInterceptor implements HttpInterceptor {
) {
this.statusService.listOfErrorMessage.push(errorMessage);
this.notificationService.info('Server Response Message:',
errorMessage.replaceAll(' at ', '\n at '), option);
+ this.statusService.markAppForCheck();
+ } else if (res.status === 0 || res.status >= 500) {
+ this.statusService.networkFailureCount += 1;
+ if (
+ this.statusService.networkFailureCount >=
this.statusService.networkFailureThreshold &&
+ !this.statusService.networkErrorNotificationId
+ ) {
+ const ref = this.notificationService.warning('Network Error:',
'Connection lost or server error.', option);
+ this.statusService.networkErrorNotificationId = ref.messageId;
+ ref.onClose.subscribe(() => {
+ this.statusService.networkErrorNotificationId = null;
+ this.statusService.networkFailureCount = 0;
+ });
+ }
}
return throwError(res);
})
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 0250f051a67..2e2ca5d46ea 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
@@ -94,9 +94,10 @@ export class JobService {
}
public loadJob(jobId: string): Observable<JobDetailCorrect> {
- return this.httpClient
- .get<JobDetail>(`${this.configService.BASE_URL}/jobs/${jobId}`)
- .pipe(map(job => this.convertJob(job)));
+ return
this.httpClient.get<JobDetail>(`${this.configService.BASE_URL}/jobs/${jobId}`).pipe(
+ map(job => this.convertJob(job)),
+ catchError(() => EMPTY)
+ );
}
public loadAccumulators(jobId: string, vertexId: string):
Observable<JobAccumulators> {
diff --git a/flink-runtime-web/web-dashboard/src/app/services/status.service.ts
b/flink-runtime-web/web-dashboard/src/app/services/status.service.ts
index 3f82cfd4586..e932ba5ec4d 100644
--- a/flink-runtime-web/web-dashboard/src/app/services/status.service.ts
+++ b/flink-runtime-web/web-dashboard/src/app/services/status.service.ts
@@ -17,7 +17,7 @@
*/
import { HttpClient } from '@angular/common/http';
-import { inject, Injectable } from '@angular/core';
+import { ChangeDetectorRef, inject, Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { EMPTY, fromEvent, interval, merge, Observable, Subject } from 'rxjs';
import { debounceTime, filter, map, share, startWith, switchMap, tap } from
'rxjs/operators';
@@ -37,6 +37,23 @@ export class StatusService {
/** Error server response message cache list. */
public listOfErrorMessage: string[] = [];
+ /** Threshold of consecutive network failures before surfacing an error to
the user. */
+ public readonly networkFailureThreshold = 5;
+ /** Count of consecutive network failures (status 0 or >=500) across all
requests. */
+ public networkFailureCount = 0;
+ /** messageId of the currently-visible network-error notification, if any. */
+ public networkErrorNotificationId: string | null = null;
+
+ private appCdr?: ChangeDetectorRef;
+
+ public registerAppCdr(cdr: ChangeDetectorRef): void {
+ this.appCdr = cdr;
+ }
+
+ public markAppForCheck(): void {
+ this.appCdr?.markForCheck();
+ }
+
/** Flink configuration from backend. */
public configuration: Configuration;