eugenegujing opened a new issue, #8850:
URL: https://github.com/apache/texera/issues/8850

   ### What happened?
   
   `ComputingUnitStatusService` keeps the computing-unit list and the selected 
unit's status fresh through two RxJS pipelines. Both are built the same way, 
and both have the same flaw: the inner HTTP observable has no error handling, 
so an HTTP error propagates out of the `switchMap`, reaches a `subscribe` that 
has no error callback, and terminates the subscription. Nothing ever re-creates 
it. 
   
   The list refresh:
   
   ```ts
   // 
frontend/src/app/common/service/computing-unit/computing-unit-status/computing-unit-status.service.ts:141-155
     private startRefreshInterval(): void {
       if (this.refreshSubscription) {
         this.refreshSubscription.unsubscribe();
       }
   
       this.refreshSubscription = this.refreshComputingUnitListSignal
         .pipe(
           switchMap(() => this.computingUnitService.listComputingUnits()),
           untilDestroyed(this)
         )
         .subscribe(units => {
           this.setComputingUnitsState(units);
         });
     }
   ```
   
   The poll of the selected unit:
   
   ```ts
   // 
frontend/src/app/common/service/computing-unit/computing-unit-status/computing-unit-status.service.ts:91-104
     private startPollingSelectedUnit(cuid: number): void {
       // cancel previous poll, if any
       this.selectedUnitPoll?.unsubscribe();
   
       this.selectedUnitPoll = interval(this.REFRESH_INTERVAL_MS)
         .pipe(
           // each tick → get fresh data for *this* cuid
           switchMap(() => this.computingUnitService.getComputingUnit(cuid)),
           untilDestroyed(this)
         )
         .subscribe(unit => {
           this.updateUnitInList(unit);
         }); // merge into cache
     }
   ```
   
   In RxJS an `error` is a terminal notification: `switchMap` forwards an inner 
error to its downstream unchanged, and a subscriber that receives it is closed 
for good. The service is `providedIn: "root"` and the refresh subscription is 
created exactly once, from the constructor. After a single failed list call, 
`refreshComputingUnitList()` keeps pushing into a `Subject` that no longer has 
a subscriber, for the rest of the page's life. The same happens to the poll on 
its first failed `getComputingUnit`. The only trace is one `ERROR 
HttpErrorResponse` line that Angular's global error handler prints to the 
console.
   
   The failure that kills the pipeline can be anything the backend or the 
network produces: a 500, a 502 or 504 from the proxy while the computing-unit 
managing service restarts, or a transient connection error. Neither HTTP 
interceptor swallows errors (`unauthorized-http-interceptor.service.ts`, 
`blob-error-http-interceptor.service.ts` both rethrow), so nothing upstream 
stops the error before it reaches the pipeline.
   
   What the user sees afterwards, until the page is reloaded:
   
   - **Dashboard.** The Computing Units page asks for a refresh every second 
(`user-computing-unit.component.ts:94-98`), so it is the likeliest place to hit 
a transient error. From then on the list is frozen: units created elsewhere 
never appear, terminated units never disappear.
   - **Workspace dropdown.** Opening the computing-unit dropdown 
(`computing-unit-selection.component.ts:903`) no longer refreshes anything.
   - **Selected unit.** Once the poll is dead, the selected unit's status never 
changes again. A unit that dies keeps showing its last status, and none of the 
Failed / Terminating / reason states added by #7944 can reach the UI.
   - **Canvas run button.** Its text and disabled flag are recomputed on 
`getStatus()` emissions (`menu.component.ts`). With the poll dead those 
emissions stop, so the button can stay on "Connecting" after the socket is 
actually up, because the server's `WorkflowStateEvent("Uninitialized")` on 
connect emits nothing when the frontend is already Uninitialized 
(`execute-workflow.service.ts`).
   
   Note that `terminateComputingUnit()` in the same file does guard its own 
request with `catchError`, so the two long-lived pipelines are the only 
unguarded ones in the service.
   
   ### How to reproduce?
   
   Reproduced in Chrome on a locally running stack. The failing request was 
produced by making exactly one `GET api/computing-unit` fail from the browser 
and letting all later ones through.
   
   1. Open Dashboard → Compute. The page requests the list once a second.
   2. Let one of those requests fail. The console shows one `ERROR 
HttpErrorResponse`.
   3. No further list request is sent, however long the page stays open.
   4. From a second tab, create or terminate a computing unit. The second tab 
shows the change at once; the first tab never does, until it is reloaded.
   
   The selected-unit poll (`GET api/computing-unit/<cuid>` every 2 s) stops the 
same way after one failed request.
   
   Without instrumenting the browser: open the Dashboard's Compute page, stop 
the computing-unit managing service for a few seconds so that at least one 
refresh fails, start it again, then change a unit from another tab.
   
   ### Version/Branch
   
   1.4.0-incubating-SNAPSHOT (main)
   
   ### Commit Hash (Optional)
   
   _No response_
   
   ### What browsers are you seeing the problem on?
   
   _No response_
   
   ### Relevant log output
   
   ```shell
   
   ```


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to