The GitHub Actions job "Required Checks" on texera.git/main has failed. Run started by GitHub user github-merge-queue[bot] (triggered by github-merge-queue[bot]).
Head commit for run: 65386d38ba114ba7e25b56e6264b04b066def16b / Eugene Gu <[email protected]> fix(frontend): keep the computing-unit refresh and poll alive after a failed request (#8854) ### What changes were proposed in this PR? `ComputingUnitStatusService` keeps the computing-unit list and the selected unit's status fresh through two RxJS pipelines: the list refresh (`refreshComputingUnitListSignal → switchMap(listComputingUnits) → subscribe`) and the selected-unit poll (`interval(2000) → switchMap(getComputingUnit) → subscribe`). Neither inner HTTP observable had any error handling. In RxJS an error is a terminal notification: `switchMap` forwards an inner error downstream unchanged, and a subscriber that receives it is closed for good. Both subscriptions are created once, in the constructor of this root-provided service, and nothing ever re-creates them. So a single failed request (a 500, a 502/504 from the proxy while the computing-unit managing service restarts, a network blip) killed the pipeline for the rest of the page's life. The refresh signal kept being pushed every second by the dashboard, into a `Subject` with no subscriber left. The only trace was one `ERROR HttpErrorResponse` in the console; the UI showed nothing. What users saw until they reloaded the page: - the dashboard's Computing Units list froze: units created elsewhere never appeared, terminated ones never disappeared; - the workspace's computing-unit dropdown stopped refreshing; - the selected unit's status never changed again, so the Failed / Terminating states from #7944 could not reach the UI; - the canvas run button could stay on "Connecting" after the socket was actually up, because its state is recomputed on poll emissions. **The change** wraps the inner HTTP observable of both `switchMap`s in `catchError(this.swallowFetchError(...))`. The helper completes the failed fetch without emitting, which `switchMap` treats as "this round is over, wait for the next signal"; the stream stays subscribed and the next refresh signal or poll tick fetches again. Completing with `EMPTY` rather than an empty list keeps the cached list and the selected unit untouched during the failed round, so nothing flickers. The helper distinguishes two kinds of failure. An `HttpErrorResponse`, which is what `HttpClient` delivers for every transport-level failure (non-2xx status, dropped connection, timeout, unparsable body) and what both app interceptors rethrow, is dropped without logging: it is expected while the backend is unavailable, it would otherwise print once a second during a restart, and the failed requests remain visible in the browser's Network panel. Anything else, for example an exception thrown inside the managing service's `map(parseDashboardUnit)` step, is a bug rather than an environment condition, so it is written to `console.error` with the pipeline name before being dropped. Before this PR such a bug surfaced once as an uncaught error and then froze the page; now it is reported on every tick and the page keeps working. #### Before: one failed request freezes the dashboard The black panel in the top-right corner is a debugging overlay injected into the page for the reproduction (it counts `GET api/computing-unit` requests and can make exactly one of them fail); it is not part of Texera. Normal state: the dashboard requests the list once a second. <img width="1568" height="562" alt="bug1-01-normal-list-refreshing-every-second" src="https://github.com/user-attachments/assets/ab652efb-eebe-47a2-9d25-d136733275eb" /> One request is made to fail. Eight seconds later no further request has been sent, although the page still asks for a refresh every second. <img width="1568" height="562" alt="bug1-02-one-injected-failure-no-requests-for-8s" src="https://github.com/user-attachments/assets/9ae568da-292c-405f-b419-f8fc2fa9ae04" /> >From a second tab, a computing unit is terminated; that tab shows one unit immediately. <img width="1568" height="562" alt="bug1-03-other-tab-unit-4-removed-shows-1-unit" src="https://github.com/user-attachments/assets/dececa82-2f90-4131-a933-d80b0b42e33c" /> 87 seconds after the failure the first tab still shows two units and has sent no request. Reloading the page restores the list at once, so the backend was healthy the whole time. <img width="1568" height="562" alt="bug1-04-this-tab-frozen-87s-still-shows-2-units-BUG" src="https://github.com/user-attachments/assets/fe9b6f68-f54e-4ab0-baf0-ae208a41d56b" /> #### After: the same failure, on this branch Same page, same overlay, served from this branch. One request (45) is made to fail; eight seconds later eight more requests have gone out and the list is still refreshing. <img width="1568" height="562" alt="fixed-bug1-02-one-injected-failure-requests-keep-flowing-8-since" src="https://github.com/user-attachments/assets/d9c8efe1-f014-488f-abfe-8f02027002da" /> A unit created from a second tab appears on this page within a second (78 requests since the failure), and disappears again when it is terminated from the other tab (138 requests since the failure), all without a reload. <img width="1568" height="562" alt="fixed-bug1-03-unit-created-in-other-tab-appears-here" src="https://github.com/user-attachments/assets/775fc80e-7b3e-487c-8fab-8df660d4fbcd" /> <img width="1568" height="562" alt="fixed-bug1-04-unit-removed-in-other-tab-disappears-here-138-since-fail" src="https://github.com/user-attachments/assets/4dd8495c-fc35-40c3-9a67-c4d9ca899d6a" /> ### Any related issues, documentation, discussions? Closes #8850 Related follow-ups that depend on this fix landing first (not part of this PR): a selected unit whose pod vanishes polls back to Pending forever, and dropdown rows do not refresh while the dropdown is open. Both of their fixes call `refreshComputingUnitList()`, which only works if this pipeline is alive. ### How was this PR tested? - **New unit tests** in `computing-unit-status.service.spec.ts`, four in total, all red before the fix. Failures are modelled as `HttpErrorResponse` (status 500), which is what `HttpClient` delivers. `keeps refreshing the list through failed fetches` sends two failures then a success and asserts three fetches, no list emission for a failure, and the good list on the next success. `keeps polling the selected unit through a failed fetch` asserts that after a failed tick the selected unit object is untouched, the poll subscription is still open and `stopPollingSelectedUnit` was not called, and that the following four ticks all fetch and the next good result replaces the selection. `logs a non-HTTP list failure ...` and `logs a non-HTTP poll failure ...` feed a `TypeError`, then an `HttpErrorResponse`, then a success, and assert `console.error` is called exactly once, with the pipeline name and the `TypeError`, while the stream keeps going. - **Mutation check**, each mutation restored afterwards: removing the `catchError` from either pipeline, dropping the `instanceof HttpErrorResponse` check, or swapping the two pipeline labels each turns exactly the two tests for that pipeline or branch red. Replacing `EMPTY` with `of(null)` is a compile error, because the helper is typed `Observable<never>`; with the type loosened to let it compile, it turns three tests red, the two survival tests through their emission-count and selection assertions and the poll logging test. - **Spec file**: 34 passed. **Full frontend suite** (`ng test --watch=false`): 231 files, 6447 passed, 1 skipped (pre-existing), 0 failed. `prettier --check` clean on both files. - **Bug reproduced in a running local instance** (screenshots above): Chrome against the Angular dev server and the local computing-unit managing service, with a page-level `XMLHttpRequest` wrapper that fails exactly one list request. Reproducible without instrumentation by stopping the computing-unit managing service for a few seconds while the dashboard's Compute page is open, starting it again, and changing a unit from another tab. - **Fixed build exercised in the same local instance** (screenshots in the "After" section): after one injected failure the request counter kept increasing once a second (138 requests over the following two minutes), a unit created from another tab appeared within a second, and the same unit disappeared within a second after being terminated from the other tab. No reload. The logging branch does not change what the page does, so those screenshots still apply. ### Was this PR authored or co-authored using generative AI tooling? Yes. Generated-by: Claude Code (claude-fable-5-1). Co-authored with Claude; the author reviewed the change before submission. --------- Co-authored-by: Kunwoo (Chris) <[email protected]> Report URL: https://github.com/apache/texera/actions/runs/40445450351 With regards, GitHub Actions via GitBox
