Skip to content

Fix submission stuck on "Saving..." (serialize save effects, backport #5145)#17

Merged
milanmajchrak merged 3 commits into
datashare-UoEMainLibrary-dspace-8_xfrom
fix/submission-saving-stuck-concatmap
Jul 9, 2026
Merged

Fix submission stuck on "Saving..." (serialize save effects, backport #5145)#17
milanmajchrak merged 3 commits into
datashare-UoEMainLibrary-dspace-8_xfrom
fix/submission-saving-stuck-concatmap

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

🎥 Reproduction — before vs after (local DSpace 8.3 Docker stack)

Reproduced end-to-end on a local stack (published dspace/dspace:dspace-8_x-test backend + this frontend). A Playwright script delays every submission PATCH by ~3 s (models a slow save) and fires two overlapping autosaves (two quick edits of the autosave-on-change dc.title field). Same simulated conditions in both runs — only the frontend code differs.

BEFORE (this fork's current code — bug): the second save cancels the in-flight first save mid-transaction (switchMap); commitPending is orphaned true; the second save is silently dropped by the take(1)+filter(!commitPending) guard (only 1 PATCH ever leaves the client); savePending never resets → the footer is stuck on Saving… for the full 18 s window (real user must reload and loses data).

saving hang — before

AFTER (this PR — switchMapconcatMap): the saves are serialized; the first completes and clears commitPending before the second runs; Saving… clears within ~4 s.

saving hang — after

Objective result logged by the script (identical steps):

run footer "Saving…" visible during 18 s observation outcome
before (base branch, switchMap) visible the entire 18 s ❌ STUCK
after (this PR, concatMap) gone by t≈4 s ✅ COMPLETED

Full-quality clips/stills in the pr-repro-media branch (before.mp4 · after.mp4). (The media is hosted on the separate pr-repro-media branch, so it is not part of this PR's code changes.)


Problem

When creating/editing a submission, the form can get permanently stuck showing Saving.... The user must reload the page, which discards the in-memory ngrx store and loses the metadata they had typed.

Root cause

The four submission save effects in submission-objects.effects.ts use switchMap. When two save actions of the same type overlap, switchMap cancels the in-flight save's inner observable after it dispatched StartTransactionPatchOperationsAction (commitPending = true) but before its response handler runs. Commit/Rollback are dispatched only inside that cancelled observable, so commitPending stays true forever. The next save is then silently dropped by the take(1) + filter(!commitPending) guard in submitJsonPatchOperations(), so no SAVE_*_SUCCESS/SAVE_*_ERROR is emitted, savePending stays true, and the UI is stuck on Saving....

Overlapping same-type saves are easy during normal editing because:

  • the dispatchSave guard find(isPending => !isPending) is defeated by startWith(false) in getSubmissionSaveProcessingStatus, so it does not actually serialize saves;
  • section/upload/save-for-later saves and save-on-change (on fields with validation errors) dispatch save actions directly without that guard;
  • if the submission.autosave.timer is enabled in config, the periodic autosave makes it far more frequent.

Fix

Change the four save effects from switchMap to concatMap. concatMap serializes them, so each inner observable completes (dispatching Commit/Rollback and clearing commitPending) before the next save runs — eliminating the race.

Upstream / backport

This is a backport of upstream DSpace/dspace-angular#5145 (commit a3c14c5f, [DURACOM-453]), which first shipped in DSpace 10.0. The change is not present in any 8.x or 9.x release and was not backported upstream, so DSpace 8.3 (this fork) does not have it.

Scope matches upstream exactly:

Risk & testing

Tiny, low-risk change (1 import + 4 operators). Suggested QA: create a new submission, rapidly edit metadata / switch sections / upload while a save is in flight; confirm the form never gets stuck on Saving.... Full CI/build recommended.

🤖 Generated with Claude Code

…chMap -> concatMap)

Backport of the save-effect change from upstream PR DSpace#5145
(commit a3c14c5, first released in DSpace 10.0; not present in 8.x/9.x).

The four submission save effects used switchMap. When two save actions of the
same type overlap (common during editing: unguarded section/upload/save-for-later
saves, save-on-change on validation errors, or an enabled autosave timer),
switchMap cancels the in-flight save's inner observable AFTER it dispatched
StartTransactionPatchOperationsAction (commitPending=true) but BEFORE its response
handler runs. Commit/Rollback are dispatched only inside that cancelled observable,
so commitPending stays true forever; the next save is then dropped by the
take(1)+filter(!commitPending) guard in submitJsonPatchOperations, no
SAVE_*_SUCCESS/ERROR is emitted, savePending stays true, and the form is stuck on
"Saving..." until the user reloads (losing unsaved metadata).

concatMap serializes the save effects so each inner observable completes
(dispatching Commit/Rollback and clearing commitPending) before the next save runs,
eliminating the race.

Only the four save effects are changed (saveSubmission$, saveForLaterSubmission$,
saveSection$, saveAndDeposit$), matching upstream. deposit/discard/updateSection
intentionally keep switchMap. The improved catchError from DSpace#5145 is not backported
(it depends on parseErrorResponse and action signatures not present in 8.3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fa2e305e-584e-4430-b32a-6a139718ce3f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

milanmajchrak and others added 2 commits July 8, 2026 14:38
Deterministic reproduction on a local DSpace 8.3 stack. A Playwright script
delays every submission PATCH by ~3s (models a slow save) and fires two
overlapping autosaves (two edits of the autosave-on-change dc.title field).

- before (base, switchMap): the second save cancels the in-flight first save
  mid-transaction; commitPending is orphaned true; the second save is dropped by
  the take(1)+filter(!commitPending) guard (only 1 PATCH ever leaves the client);
  savePending never resets -> footer stuck on "Saving..." for the full 18s window.
- after (this PR, concatMap): the saves are serialized; the first completes and
  clears commitPending before the second runs; "Saving..." clears within ~4s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The before/after clips are now hosted on the separate 'pr-repro-media' branch and
referenced from this PR's description, so they are no longer part of the code diff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@milanmajchrak
milanmajchrak merged commit b9c6e82 into datashare-UoEMainLibrary-dspace-8_x Jul 9, 2026
10 checks passed
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