fix(content_libraries): keep collection num_children in sync after item updates#38322
fix(content_libraries): keep collection num_children in sync after item updates#38322kingoftech-v01 wants to merge 1 commit intoopenedx:masterfrom
Conversation
…em updates update_library_collection_items relied on Collection.post_save to emit LIBRARY_COLLECTION_UPDATED, but post_save fires before the M2M entities commit, so the synchronous reindex in searchable_doc_for_collection computed num_children from the stale membership. Emit the signal explicitly with background=True after the add/remove so the async reindex runs post-commit with the correct count, matching the pattern already used by set_library_item_collections, delete_library_block, restore_library_block, delete_container, and restore_container. Also mark LibraryCollectionsView with non_atomic_requests so Celery tasks dispatched from the view are enqueued after the request transaction commits, matching every other view in openedx/core/djangoapps/content_libraries/rest_api/. Closes openedx#35776
|
Thanks for the pull request, @kingoftech-v01! This repository is currently maintained by 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 approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo 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:
🔘 Submit a signed contributor agreement (CLA)
If you've signed an agreement in the past, you may need to re-sign. Once you've signed the CLA, please allow 1 business day for it to be processed. 🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere 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:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
Description
When adding or removing items from a library collection, the search index's
num_childrencount was stale. Users saw incorrect counts on collection cards until a later unrelated edit triggered another reindex.User impact (Library Author): Collection counts in the Studio library view reflect the actual number of items immediately after add/remove operations.
Root Cause
update_library_collection_items(previouslyupdate_library_collection_components) relied on Django'sCollection.post_savesignal to emitLIBRARY_COLLECTION_UPDATED. That signal fires before the M2Mentitiesrelationship commits. The synchronous reindex handler (search/handlers.py:234) then runssearchable_doc_for_collection, which computesnum_children = filter_publishable_entities(collection.entities, has_draft=True).count()against the stale pre-commit M2M state. The final count is never corrected because them2m_changedhandler only emitsCONTENT_OBJECT_ASSOCIATIONS_CHANGEDper item, notLIBRARY_COLLECTION_UPDATED.Git history:
7665f13547, 2024-09-11, Yusuf Musleh) originally had the explicit emission.5446877a86, 2024-09-26, Jillian Vogel) refactored topost_savesignals and removed the explicit emit.d5850c812c, 2024-11-19, Chris Chávez) — was merged (contrary to the bug report label) and restored the pattern indelete_library_block,revert_changes, and laterdelete_container/restore_container.update_library_collection_itemswas left as the lone outlier.Fix
Two surgical changes:
openedx/core/djangoapps/content_libraries/api/collections.py— emitLIBRARY_COLLECTION_UPDATEDwithbackground=Trueexplicitly afteradd_to_collection/remove_from_collection. Thebackground=Trueflag routes the reindex to a Celery task that runs post-transaction, after the M2M commit, so it sees the correct count. This mirrorsset_library_item_collections,delete_library_block,restore_library_block,delete_container, andrestore_container.openedx/core/djangoapps/content_libraries/rest_api/collections.py— decorateLibraryCollectionsViewwith@method_decorator(transaction.non_atomic_requests, name="dispatch"), matching every other view in the same subpackage (blocks.py,libraries.py,containers.py). Without this, a Celery.delay()dispatch from inside the per-request atomic transaction could be consumed before the transaction commits.Supporting Information
7665f13547)5446877a86)d5850c812c) and later containers workset_library_item_collections(collections.py:226-239)Testing Instructions
searchable_doc_for_collection(collection_key)["num_children"]matchescollection.entities.count().Two new/updated tests in
openedx/core/djangoapps/content_libraries/tests/test_api.py:test_update_library_collection_components_event— updated expectation from 4 to 5 events and added a final-call assertion verifying thebackground=Trueemit.test_bug_35776_regression_update_items_emits_background_updated— regression test covering both add and remove paths, assertingbackground=Trueemissions.Deadline
None
Other Information
LIBRARY_COLLECTION_UPDATEDper update_items call (one frompost_save, one explicit after M2M) — the second overrides the first, final state is correct. Matches the behavior already accepted forset_library_item_collections.non_atomic_requestsdecorator matches every other view incontent_libraries/rest_api/. The sub-operations in the view are already individually atomic where needed.Closes #35776