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]