Skip to content

fix: prevent duplicate syncs from the Review Content Updates tab - #3316

Open
syedsajjadkazmii wants to merge 2 commits into
openedx:masterfrom
edly-io:fix/review-tab-duplicate-sync
Open

syedsajjadkazmii wants to merge 2 commits into
openedx:masterfrom
edly-io:fix/review-tab-duplicate-sync

Conversation

@syedsajjadkazmii

@syedsajjadkazmii syedsajjadkazmii commented Oct 19, 2026 •

Copy link
Copy Markdown

Description

Fixes duplicate sync requests from the course Libraries > Review Content Updates tab. Users had to click "Update" an unpredictable number of times, and each extra click sent another POST /api/contentstore/v2/downstreams/<usage_key>/sync.

Impacted role: Course Author.

Cause

Clicking "Update" puts the LoadingButton in its pending state until updateBlock resolves. updateBlock resolved as soon as the sync POST returned. The card, however, is only removed after two more requests:

  1. the ready-to-sync links query is refetched (invalidateLinksQuery was not awaited), then
  2. the search index query is re-run with the new usage_key IN [...] filter.

Until both finished, the old card showed an active "Update" button. Measured on a production instance with a single click: sync POST 0.8 s, spinner cleared at 10.25 s, links refetched at 10.53 s, card removed at 10.75 s. The server logs show the same block synced twice, 5 s apart. The gap grows with network and server latency, which is why the bug is intermittent.

Changes

  • updateBlock awaits the links refetch, so the button stays pending until the list is current. If the item is still ready to sync after the refetch, the button becomes active again (no stuck spinner).
  • Search hits that are no longer in the ready-to-sync links are dropped, so a synced card does not stay on screen while the search results catch up.
  • updateBlock had an empty useCallback dependency list, so it used outOfSyncItemsByKey from the first render. For an item that became ready to sync after the page loaded, the lookup threw after a successful sync: the user saw "Failed to update component" and the card was not refreshed. It now depends on outOfSyncItemsByKey.
  • invalidateLinksQuery returns the invalidateQueries promise so it can be awaited. It is added to the no-floating-promises known-safe calls next to invalidateQueries, so existing fire-and-forget callers are unchanged.

Supporting information

Reported by course authors on an Open edX production instance (Teak-based Studio).

Testing instructions

  1. In a library, edit and publish a component that is used in a course.
  2. In Studio, open the course Libraries page, Review Content Updates tab.
  3. Throttle the network in browser dev tools (e.g. "Slow 4G").
  4. Click "Update" on the card and keep clicking it.
  5. Before: the spinner stops, the card stays with an active "Update" button, and further clicks send more sync POSTs. After: the button stays pending until the card disappears, and only one sync POST is sent.

Automated tests added in CourseLibraries.test.tsx:

  • the button stays pending until the links refetch, a second click sends nothing, and the card is removed;
  • the button becomes active again (and works) if the item is still out of sync after the refetch;
  • an item that became ready to sync after page load updates without the failure toast;
  • two cards updated back to back both stay pending until the refetch that follows the second sync;
  • a failed sync shows the failure toast, keeps the card and re-enables the button.

The first and third fail on master.

Other information

No backend changes. The ignore flow and the preview modal still refresh the links without waiting; they close a modal and are not affected by this bug.

Best Practices Checklist

  • Any new files are using TypeScript (.ts, .tsx).
  • Avoid propTypes and defaultProps in any new or modified code.
  • Tests should use the helpers in src/testUtils.tsx (specifically initializeMocks)
  • Do not add new fields to the Redux state/store. Use React Context to share state among multiple components.
  • Use React Query to load data from REST APIs. See any apiHooks.ts in this repo for examples.
  • All new i18n messages in messages.ts files have a description for translators to use.
  • Avoid using ../ in import paths. To import from parent folders, use @src, e.g. import { initializeMocks } from '@src/testUtils'; instead of from '../../../../testUtils'

🤖 Generated with Claude Code

The "Update" button on a review card stopped its spinner as soon as the
sync POST returned, but the card only went away after the ready-to-sync
links and then the search results were refetched. In that gap the old
card showed an active "Update" button, and clicking it sent the same
sync again.

- Wait for the links refetch before resolving the Update click, so the
  button stays pending until the list is current.
- Drop search hits that are no longer in the ready-to-sync links, so a
  synced card is not shown while the search results catch up.
- Add outOfSyncItemsByKey to the updateBlock dependencies. It was
  captured from the first render, so an item that became ready to sync
  later failed after a successful sync and its card was not refreshed.
@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Oct 19, 2026
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @syedsajjadkazmii!

This repository is currently maintained by @bradenmacdonald.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

Read the link data before the sync so a later lookup cannot fail after a
successful sync, and fix the invalidateLinksQuery docstring typo.
@taimoor-ahmed-1
taimoor-ahmed-1 requested a review from a team October 19, 2026 10:48
syedsajjadkazmii added a commit to edly-io/frontend-app-authoring that referenced this pull request Oct 19, 2026
Backport of openedx#3316 to release/ulmo.2 for EDL.

The "Update" button on a review card stopped its spinner as soon as the
sync POST returned, but the card only went away after the ready-to-sync
links and then the search results were refetched. In that gap the old
card showed an active "Update" button, and clicking it sent the same
sync again.

- Wait for the links refetch before resolving the Update click, so the
  button stays pending until the list is current.
- Drop search hits that are no longer in the ready-to-sync links, so a
  synced card is not shown while the search results catch up.
- Add outOfSyncItemsByKey to the updateBlock dependencies. It was
  captured from the first render, so an item that became ready to sync
  later failed after a successful sync and its card was not refreshed.
@mphilbrick211 mphilbrick211 added the needs test run Author's first PR to this repository, awaiting test authorization from Axim label Oct 22, 2026
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Needs Tests Run or CLA Signed in Contributions Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs test run Author's first PR to this repository, awaiting test authorization from Axim open-source-contribution PR author is not from Axim or 2U

Projects

Status: Needs Tests Run or CLA Signed

Development

Successfully merging this pull request may close these issues.

3 participants