Skip to content

Fix infinite route-loader spinner from per-navigation root cache inva…#5936

Open
naomigassler wants to merge 1 commit into
DSpace:mainfrom
naomigassler:fix/root-cache-navigation-deadlock
Open

Fix infinite route-loader spinner from per-navigation root cache inva…#5936
naomigassler wants to merge 1 commit into
DSpace:mainfrom
naomigassler:fix/root-cache-navigation-deadlock

Conversation

@naomigassler

@naomigassler naomigassler commented Jul 8, 2026

Copy link
Copy Markdown

PR: Fix infinite route-loader spinner on navigation (stale root-endpoint cache deadlock)

Target branch: DSpace/dspace-angular:main
Closes / relates to: #3584, #3697, #5855

Description

BrowserInitService invalidates the root API endpoint cache on every
NavigationStart. On a subsequent request, HALEndpointService.getEndpointMapAt
(hit on essentially every request via getEndpoint()) discards the now-stale
root /server/api entry and triggers a re-fetch that can deadlock — the
route resolver never completes, NavigationEnd never fires, and the
ds-base-root route-loader spinner hangs indefinitely on a frozen store.

The root endpoint map is effectively static between navigations, so
re-invalidating it on each NavigationStart is unnecessary. Backend-down
detection still happens at init and through normal request-failure handling.

Root cause

  • BrowserInitServiceinvalidateRootCache() on every NavigationStart.
  • Marks the root /server/api endpoint cache stale.
  • Next getEndpoint()getEndpointMapAt discards the stale root → re-fetch
    deadlocks → resolver never resolves → NavigationEnd never fires → spinner
    hangs forever.
  • Higher REST latency makes it fire on nearly every revisit (why it looks
    intermittent).

Steps to reproduce

  1. Open any listing (collection items, MyDSpace, Browse, Search — even
    Communities & Collections).
  2. Navigate listing → listing → listing.
  3. The UI hangs on the route-loader spinner; a full page reload clears it.

Reproducible on a small instance (~7k items) — not load- or scale-dependent.
Higher REST/proxy latency increases the frequency.

Fix

Remove the per-NavigationStart invalidateRootCache() call in
browser-init.service.ts; keep the one-time invalidation at init. No behavior
change to backend-down detection.

How to test

  1. Before the fix: reproduce the hang via the steps above.
  2. After the fix: the same navigation sequence no longer hangs; the spinner
    always resolves.
  3. Backend-down handling still works: stop the REST backend and confirm the
    app still detects/handles it (init-time + request-failure paths).

Tests

Added a spec asserting the root endpoint cache is invalidated once at init
and not on subsequent NavigationStart events.

Checklist notes

  • One-file logic fix; no new dependencies; no user-facing strings (i18n N/A).
  • Passes yarn lint and yarn check-circ-deps.
  • TypeDoc added/updated on any modified public method.

…lidation

BrowserInitService invalidated the root /server/api endpoint cache on every
NavigationStart. That marks the root request stale (RootDataService
.invalidateRootCache -> RequestService.setStaleByHref). HALEndpointService
.getEndpointMapAt, used by getEndpoint() for every data request, discards a
stale root via filter(rd => !rd.isStale), and its re-fetch can lose the race
with the next invalidation, so getEndpoint() never resolves, the route
resolver never completes, and the route loader spins forever.

The root endpoint map is static between navigations, so invalidating it on
every navigation is unnecessary. Remove the per-NavigationStart invalidation
and keep the one-time invalidation at app init; backend availability is still
established there and surfaces through normal request failures.

Fixes DSpace#3584, DSpace#3697.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant