fix: avoid item bank side effects during field data prefetch - #414
Open
mraman-2U wants to merge 1 commit into
Open
fix: avoid item bank side effects during field data prefetch#414mraman-2U wants to merge 1 commit into
mraman-2U wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts LMS field-data descendant prefetching to better support XBlocks with learner-specific (“dynamic”) children by only prefetching the learner-selected subset (and introducing a side-effect-free prefetch API for item banks), with accompanying test coverage.
Changes:
- Added
_children_for_field_data_cache()and updatedFieldDataCache.add_block_descendents()to prefetch only selected children for dynamic blocks (and use a read-only API when available). - Updated
ItemBank/LegacyLibraryContentselection APIs to support aread_onlymode and addedget_child_blocks_for_prefetch()to avoid analytics/state side effects during prefetch. - Added new unit tests covering dynamic-child prefetch behavior and ensuring read-only prefetch does not publish analytics or persist selection.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
lms/djangoapps/courseware/model_data.py |
Introduces _children_for_field_data_cache() and switches descendant collection to use it (including read-only mode). |
lms/djangoapps/courseware/tests/test_model_data.py |
Adds tests asserting dynamic blocks only prefetch selected children and that read-only prefetch uses the side-effect-free API when present. |
xmodule/item_bank_block.py |
Adds read_only support to selection + introduces get_child_blocks_for_prefetch() for side-effect-free child retrieval. |
xmodule/tests/test_item_bank.py |
Adds a regression test ensuring read-only child prefetching does not publish analytics or persist selection. |
Suppressed comments (1)
xmodule/item_bank_block.py:246
- The
selected_childrendocstring is now more misleading: it says it returns “block_ids” and that it “reads and updates” user_state, but the method actually returns(block_type, block_id)pairs and, with the newread_onlyflag, it may avoid publishing analytics / persisting state. Please update the docstring to match the real return type and theread_onlybehavior (seemake_selectiondocstring above, which documents the tuple structure).
def selected_children(self, read_only=False):
"""
Returns a [] of block_ids indicating which of the possible children
have been selected to display to the current user.
This reads and updates the "selected" field, which has user_state scope.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
mraman-2U
force-pushed
the
mraman-2U/assessments-not-loading
branch
from
August 5, 2026 01:17
221d1d4 to
6fcdac9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
xmodule/item_bank_block.py:270
selected_children(read_only=True)still callsmake_selection(...), which can randomly choose a new set of children whenself.selectedis empty or stale. Because the read-only path doesn’t persist the result, the blocks prefetched can differ from the blocks later rendered/persisted in non-read-only mode, defeating the goal of “prefetch only what will be rendered” and potentially causing unnecessary loads.
max_count = self.max_count
if max_count < 0:
max_count = len(self.children)
block_keys = self.make_selection(self.selected, self.children, max_count) # pylint: disable=no-member
selected = block_keys['selected']
if not read_only:
self.publish_selected_children_events(
block_keys,
self.format_block_keys_for_analytics,
self._publish_event,
)
if not read_only and any(block_keys[changed] for changed in ('invalid', 'overlimit', 'added')):
# Save our selections to the user state, to ensure consistency:
self.selected = selected # TODO: this doesn't save from the LMS "Progress" page.
return selected if read_only else self.selected
xmodule/tests/test_item_bank.py:246
- This test asserts that
get_child_blocks_for_prefetch()returns a selected child even whenitem_bank.selectedis empty, while also asserting selection is not persisted. That combination can allow prefetch/render mismatches (prefetch may choose a random child that differs from the later persisted/rendered selection). Consider seeding the test with an existing persisted selection and asserting prefetch returns that selection without publishing or mutating state.
def test_prefetch_child_blocks_does_not_publish_or_persist_selection(self):
"""
Read-only child selection should avoid analytics events and user-state writes.
"""
self._bind_course_block(self.item_bank)
selected_children = self.item_bank.get_child_blocks_for_prefetch()
assert len(selected_children) == 1
self.publisher.assert_not_called()
assert self.item_bank.selected == []
santhosh-apphelix-2u
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request updates how descendant blocks are prefetched and cached in the LMS, improving support for dynamic blocks (like library content or item banks) that expose different children per learner. The main change ensures that only the relevant, learner-selected child blocks are prefetched, avoiding unnecessary loading and side effects for blocks that won't be rendered for the current user. The changes also introduce a read-only prefetch API for dynamic blocks, and add comprehensive tests to verify the correct behavior.
Dynamic block prefetch improvements:
_children_for_field_data_cachefunction inmodel_data.pyto ensure that only learner-selected children are prefetched for dynamic blocks, using the appropriate API depending on whether the operation is read-only.FieldDataCache.add_block_descendentsto use_children_for_field_data_cache, preventing unnecessary loading of unselected children for dynamic blocks. [1] [2]Dynamic block API changes:
item_bank_block.py, updatedselected_childrenand_get_selected_child_blocksto support aread_onlyparameter, and addedget_child_blocks_for_prefetchto return selected child blocks without publishing events or mutating user state. [1] [2] [3]Testing and verification:
TestFieldDataCacheDynamicChildrentests to verify that only selected children are prefetched for dynamic blocks, that the read-only prefetch API is used when appropriate, and that static blocks continue to prefetch all children.test_item_bank.py.These changes make field data caching more efficient and side-effect-free for dynamic blocks, improving both performance and correctness in learner-specific content rendering.<!--
Note: Please refer to the Support Development Guidelines on the wiki page to consider backporting to active releases:
https://openedx.atlassian.net/wiki/spaces/COMM/pages/4248436737/Support+Guidelines+for+active+releases
Please give your pull request a short but descriptive title.
Use conventional commits to separate and summarize commits logically:
https://open-edx-proposals.readthedocs.io/en/latest/oep-0051-bp-conventional-commits.html
Use this template as a guide. Omit sections that don't apply.
You may link to information rather than copy it, but only if the link is publicly readable.
If the linked information must be private (because it contains secrets), clearly label the link as private.
-->
Description
Describe what this pull request changes, and why. Include implications for people using this change.
Design decisions and their rationales should be documented in the repo (docstring / ADR), per
OEP-19, and can be
linked here.
Useful information to include:
"Developer", and "Operator".
changes.
Supporting information
Link to other information about the change, such as Jira issues, GitHub issues, or Discourse discussions.
Be sure to check they are publicly readable, or if not, repeat the information here.
Testing instructions
Please provide detailed step-by-step instructions for testing this change.
Deadline
"None" if there's no rush, or provide a specific date or event (and reason) if there is one.
Other information
Include anything else that will help reviewers and consumers understand the change.