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

linxinyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 9322ae62db feat(gui): Display the number of workers for each operator 
during execution (#3911)
9322ae62db is described below

commit 9322ae62db5c6e5407c97654419c959ed694be3f
Author: yunyad <[email protected]>
AuthorDate: Wed Oct 15 16:42:26 2025 -0700

    feat(gui): Display the number of workers for each operator during execution 
(#3911)
    
    ### Description
    This PR adds a feature to display the number of workers (degree of
    parallelism) assigned to each operator during workflow execution.
    A small label appears on top of each operator node while the workflow is
    running, indicating the current number of workers for that operator.
    
    To improve user experience and avoid clutter, a checkbox is provided to
    show or hide the worker count indicators as needed.
    
    ### Motivation
    Previously, only UDF operators exposed their worker count in the
    property panel, while native operators (e.g., Java) did not surface this
    information. This enhancement improves runtime observability and helps
    users better understand system behavior and resource allocation during
    execution.
    
    ### Demo
    ![Screen Recording 2025-10-15 at 11 09
    31 
AM](https://github.com/user-attachments/assets/e21eab7a-61cd-40c2-a048-e2f58668d5d0)
    
    Fix #3841
    
    ---------
    
    Co-authored-by: Xinyuan Lin <[email protected]>
---
 .../src/app/workspace/component/menu/menu.component.html   | 14 ++++++++++++--
 .../src/app/workspace/component/menu/menu.component.ts     | 13 ++++++++++---
 .../workflow-editor/workflow-editor.component.scss         |  4 ++++
 .../component/workflow-editor/workflow-editor.component.ts |  1 +
 .../src/app/workspace/service/joint-ui/joint-ui.service.ts | 10 ++++++++++
 .../src/app/workspace/types/execute-workflow.interface.ts  |  1 +
 6 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/frontend/src/app/workspace/component/menu/menu.component.html 
b/frontend/src/app/workspace/component/menu/menu.component.html
index e88eeaca25..dde9a8aff9 100644
--- a/frontend/src/app/workspace/component/menu/menu.component.html
+++ b/frontend/src/app/workspace/component/menu/menu.component.html
@@ -159,12 +159,22 @@
               <li nz-menu-item>
                 <label
                   nz-checkbox
-                  [(ngModel)]="grid"
-                  (ngModelChange)="onGridChange()"
+                  [(ngModel)]="showGrid"
+                  (ngModelChange)="toggleGrid()"
                   >Grid</label
                 >
               </li>
             </ul>
+            <ul nz-menu>
+              <li nz-menu-item>
+                <label
+                  nz-checkbox
+                  [(ngModel)]="showNumWorkers"
+                  (ngModelChange)="toggleNumWorkers()"
+                  >#Workers</label
+                >
+              </li>
+            </ul>
           </nz-dropdown-menu>
           <button
             (click)="onClickClosePanels()"
diff --git a/frontend/src/app/workspace/component/menu/menu.component.ts 
b/frontend/src/app/workspace/component/menu/menu.component.ts
index 994608fc36..ae804b0e1d 100644
--- a/frontend/src/app/workspace/component/menu/menu.component.ts
+++ b/frontend/src/app/workspace/component/menu/menu.component.ts
@@ -88,7 +88,8 @@ export class MenuComponent implements OnInit, OnDestroy {
   public isWorkflowModifiable: boolean = false;
   public workflowId?: number;
   public isExportDeactivate: boolean = false;
-  public grid: boolean = false;
+  public showGrid: boolean = false;
+  public showNumWorkers: boolean = false;
   protected readonly DASHBOARD_USER_WORKFLOW = DASHBOARD_USER_WORKFLOW;
 
   @Input() public writeAccess: boolean = false;
@@ -253,6 +254,12 @@ export class MenuComponent implements OnInit, OnDestroy {
     document.body.removeChild(tempSpan);
   }
 
+  toggleNumWorkers() {
+    this.workflowActionService
+      .getJointGraphWrapper()
+      .mainPaper.el.classList.toggle("hide-worker-count", 
!this.showNumWorkers);
+  }
+
   public async onClickOpenShareAccess(): Promise<void> {
     this.modalService.create({
       nzContent: ShareAccessComponent,
@@ -458,8 +465,8 @@ export class MenuComponent implements OnInit, OnDestroy {
       });
   }
 
-  public onGridChange(): void {
-    
this.workflowActionService.getJointGraphWrapper().mainPaper.setGridSize(this.grid
 ? 2 : 1);
+  public toggleGrid(): void {
+    
this.workflowActionService.getJointGraphWrapper().mainPaper.setGridSize(this.showGrid
 ? 2 : 1);
   }
 
   /**
diff --git 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.scss
 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.scss
index 872e9fc287..a51b289dab 100644
--- 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.scss
+++ 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.scss
@@ -37,3 +37,7 @@
 ::ng-deep #workflow-editor .link-tools .tool-remove circle {
   fill-opacity: 0;
 }
+
+::ng-deep .hide-worker-count .operator-worker-count {
+  display: none;
+}
diff --git 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
index a334b48603..b18f4baa86 100644
--- 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
+++ 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
@@ -244,6 +244,7 @@ export class WorkflowEditorComponent implements OnInit, 
AfterViewInit, OnDestroy
       width: this.editor.offsetWidth,
       height: this.editor.offsetHeight,
     });
+    this.editor.classList.add("hide-worker-count");
   }
 
   private handleDisableJointPaperInteractiveness(): void {
diff --git a/frontend/src/app/workspace/service/joint-ui/joint-ui.service.ts 
b/frontend/src/app/workspace/service/joint-ui/joint-ui.service.ts
index 84a88ea29e..ba3f611cbe 100644
--- a/frontend/src/app/workspace/service/joint-ui/joint-ui.service.ts
+++ b/frontend/src/app/workspace/service/joint-ui/joint-ui.service.ts
@@ -111,6 +111,7 @@ export const operatorIconClass = "texera-operator-icon";
 export const operatorNameClass = "texera-operator-name";
 export const operatorFriendlyNameClass = "texera-operator-friendly-name";
 export const operatorPortMetricsClass = "texera-operator-port-metrics";
+const operatorWorkerCountClass = "operator-worker-count";
 
 export const linkPathStrokeColor = "#919191";
 
@@ -128,6 +129,7 @@ class TexeraCustomJointElement extends 
joint.shapes.devs.Model {
       <text class="${operatorFriendlyNameClass}"></text>
       <text class="${operatorNameClass}"></text>
       <text class="${operatorPortMetricsClass}"></text>
+      <text class="${operatorWorkerCountClass}"></text>
       <text class="${operatorStateClass}"></text>
       <text class="${operatorReuseCacheTextClass}"></text>
       <text class="${operatorCoeditorEditingClass}"></text>
@@ -308,6 +310,9 @@ export class JointUIService {
     const inputMetrics = statistics.inputPortMetrics;
     const outputMetrics = statistics.outputPortMetrics;
 
+    const workerCount = statistics.numWorkers ?? 1;
+    element.attr(`.${operatorWorkerCountClass}/text`, "#workers: " + 
String(workerCount));
+
     inPorts.forEach(portDef => {
       const portId = portDef.id;
       if (portId != null) {
@@ -404,6 +409,7 @@ export class JointUIService {
       [`.${operatorStateClass}`]: { fill: fillColor },
       "rect.body": { stroke: fillColor },
       [`.${operatorPortMetricsClass}`]: { fill: fillColor },
+      [`.${operatorWorkerCountClass}`]: { fill: fillColor },
     });
     const element = jointPaper.getModelById(operatorID) as 
joint.shapes.devs.Model;
     const allPorts = element.getPorts();
@@ -791,6 +797,10 @@ export class JointUIService {
         "y-alignment": "middle",
         "x-alignment": "middle",
       },
+      [`.${operatorWorkerCountClass}`]: {
+        "ref-x": -5,
+        "ref-y": -35,
+      },
       ".delete-button": {
         x: 60,
         y: -20,
diff --git a/frontend/src/app/workspace/types/execute-workflow.interface.ts 
b/frontend/src/app/workspace/types/execute-workflow.interface.ts
index bc1ab9f7d9..23ade23199 100644
--- a/frontend/src/app/workspace/types/execute-workflow.interface.ts
+++ b/frontend/src/app/workspace/types/execute-workflow.interface.ts
@@ -85,6 +85,7 @@ export interface OperatorStatistics
     inputPortMetrics: Record<string, number>;
     aggregatedOutputRowCount: number;
     outputPortMetrics: Record<string, number>;
+    numWorkers?: number;
   }> {}
 
 export interface OperatorStatsUpdate

Reply via email to