Infinite route-loader spinner: per-navigation invalidateRootCache() deadlocks getEndpointMapAt when the root endpoint goes stale

未关闭
#5,855 6 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@naomigassler 已经在做这个了。

开始于 2026年7月9日。

  • #5936 来自 @naomigassler —— 已关闭,未合并

评估

这个 Issue 还没有评估数据。

描述

affects: 10.x affects: 8.x affects: 9.x bug performance / caching

Describe the bug

After the UI has been open a while, navigating to / revisiting a page can leave the app stuck on the route-loader spinner (<ds-loading> in ds-base-root, driven by AppComponent.isRouteLoading$) forever. The shell stays responsive (navbar, sidebar, overlays work); a full reload or clicking any other in-app link recovers it, but the routed content never renders. While stuck: nothing is pending on the network, the NgRx store is frozen, and CPU/memory are idle — a logical deadlock, not a loop or leak. It is much harder to reproduce with DevTools open and far worse in production than on a local dev server (cf. #3584, #3697).

To Reproduce

Authenticated, ideally behind a reverse proxy / with non-trivial REST latency:

  1. Load a content page (e.g. a Community/Collection).
  2. Navigate around / wait a bit.
  3. Navigate back (or keep navigating). The spinner appears and never resolves; reload or any other link recovers it. Higher REST latency → more reliable (essentially every revisit through a proxy).

Expected behavior

Navigation completes and the page renders; a transiently-stale root endpoint must not wedge navigation indefinitely.

Root cause

  1. BrowserInitService.listenForRouteChanges() calls rootDataService.invalidateRootCache() on every NavigationStart → RequestService.setStaleByHref('/server/api') marks the root request stale (regardless of its TTL).
  2. HALEndpointService.getEndpointMapAt() — used by getEndpoint(), which every data request needs — does filter(rd => !rd.isStale), discarding the stale root; its tap(... getEndpointMapAt()) re-request does not reliably produce a fresh one, so it never emits.
  3. So getEndpoint() never resolves → the route resolver never completes → the router never emits NavigationEnd → isRouteLoading$ (cleared only on NavigationEnd/Cancel) stays true → the route loader spins forever, store frozen.

Evidence (frozen store at the hang): root /server/api is SuccessStale with TTL not expired (explicitly invalidated, not aged out); 0 requests pending (nothing re-fetching); the only RequestStaleAction caller is setStaleByHref ← invalidateRootCache ← the NavigationStart subscription. Consistent with #2669 (invalidateRootCache ↔ HALEndpointService stale interaction) after #2510 (7.6.1) made HAL endpoint requests stale-able.

Proposed fix

The root endpoint map is static between navigations, so the per-NavigationStart invalidation is unnecessary. Removing it eliminates the deadlock (backend availability is still established at app init and surfaces through normal request failures):

 import {
-  NavigationStart,
   Router,
 } from '@angular/router';
@@ listenForRouteChanges()
   protected listenForRouteChanges(): void {
-    // we'll always be too late for the first NavigationStart event with the router subscribe below,
-    // so this statement is for the very first route operation.
+    // Invalidate the root endpoint cache once, for the very first route operation.
     this.rootDataService.invalidateRootCache();
-
-    this.router.events.pipe(
-      filter(event => event instanceof NavigationStart),
-    ).subscribe(() => {
-      this.rootDataService.invalidateRootCache();
-    });
   }

(Alternative, if the per-navigation backend check is to be kept: make getEndpointMapAt tolerate a stale-but-completed root, e.g. filter(rd => !rd.isStale || rd.hasCompleted), so a stale endpoint map is used while the tap() refreshes it — fixing the deadlock without changing the invalidation cadence.)

Verified: the change removes the hang and the E2E suite is unaffected.

Environment

  • dspace-angular 9.3 (Angular 20.3); listenForRouteChanges()/getEndpointMapAt() are unchanged on main as of this report.
  • DSpace 9.3 REST backend behind an nginx reverse proxy; CSR (SSR disabled); reproduced in Safari and Firefox.

Related

#2669, #2510, #3584, #3697, #3888

主要语言
TypeScript
星标
180
派生
577
平均合并
1 天 23 小时
30 天内合并 PR
95

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

DSpace/dspace-angular 的其他 Issue

查看 DSpace/dspace-angular 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。