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

Đang mở
#5,855 6 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@naomigassler đang làm issue này rồi.

Từ ngày 9/7/2026.

  • #5936 của @naomigassler — đã đóng, không merge

Đánh giá

Issue này chưa được đánh giá.

Mô tả

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

Ngôn ngữ chính
TypeScript
Star
180
Fork
577
Merge trung bình
2 ngày 1 giờ
Pull request đã merge (30 ngày)
100

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của DSpace/dspace-angular

Tất cả issue của DSpace/dspace-angular

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.