New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: fix _update_subsection_grades when restricted blocks are not cached #33963
fix: fix _update_subsection_grades when restricted blocks are not cached #33963
Conversation
Thanks for the pull request, @asadali145! Please note that it may take us up to several weeks or months to complete a review and merge your PR. Feel free to add as much of the following information to the ticket as you can:
All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here. Please let us know once your PR is ready for our review and all tests are green. |
c77864f
to
8455f7b
Compare
775a1ab
to
9e5fd0e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple of comments.
lms/djangoapps/grades/tasks.py
Outdated
if not subsections_to_update: | ||
update_course_in_cache(course_usage_key.course_key) | ||
|
||
course_structure = get_course_blocks(student, store.make_course_usage_key(course_key)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use the course_usage_key
we created above.
lms/djangoapps/grades/tasks.py
Outdated
# When access is restricted and course blocks are cached without the restricted blocks, | ||
# we will update the cache from the store and get the course blocks. | ||
if not subsections_to_update: | ||
update_course_in_cache(course_usage_key.course_key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be a long-running process and we should see the impact in ways like:
- Can we use a cache updating method that just updates the cache for
scored_block_usage_key
and not the whole course? - How long with it generally take on larger courses?
- I see a newer version of this method update_course_in_cache_v2 - would it be better to shift towards that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a cache updating method that just updates the cache for scored_block_usage_key and not the whole course?
I tried to look more into it and initially thought there might be a way to do it for the scored block. It looked like BlockStructureManager
might support but it didn't.
There is another way to get the block from the store directly but that does not cover all the use cases where recalculate_subsection_grade_v3
is used. Maybe someone having more in-depth knowledge could suggest a better approach if any.
How long with it generally take on larger courses?
I have tested it for 3 courses locally and here is the breakdown:
Demonstration Course: 5 Sections, 11 Subsections, 45 Units: 1.5 Secs
Course X: 40 Sections, 96 Subections, 185 Units: 10 Secs
Course Y: 13 Sections, 33 Subsections, 289 Units: 13 Secs
Also, we should note that this update won't be a frequent one. This approach is used at multiple places where we fetch the blocks of a course.
I see a newer version of this method update_course_in_cache_v2 - would it be better to shift towards that?
We are already doing the update inside a celery task and delaying it to another task will not serve the purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asadali145 I think problem with using update_course_in_cache
is it would not update blockstructure cache if STORAGE_BACKING_FOR_CACHE
switch is enabled and course published version is same as BlockStructureModel version. See implementation of is_up_to_date for details how it makes update decision.
I think we can clear the blockstructure cache using clear_course_from_cache
and then raise exception SUBSECTION_NOT_FOUND(need add it in one of the known retry exceptions). This would retry the recalculate_subsection_grade_v3
task.
lms/djangoapps/grades/tasks.py
Outdated
# When access is restricted and course blocks are cached without the restricted blocks, | ||
# we will update the cache from the store and get the course blocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a more readable version of this comment could be:
# When access is restricted and course blocks are cached without the restricted blocks, | |
# we will update the cache from the store and get the course blocks. | |
# Update the cache from the store and get the course blocks if access is restricted and course blocks are | |
# cached without those restricted blocks. |
8bfe54f
to
b020f10
Compare
@mphilbrick211 @timmc-edx This is a critical PR for us that fixes a recurring problem where learner's grades aren't persisted, causing the progress page and the gradebook to be incorrect. There's more detail in this discourse post linked in the PR description. Since this changes the caching behavior of grades, I think it would be best to have a 2U reviewer -- or at least someone who is familiar with running large instances. |
Oh, I just realized there's a parallel conversation about recruiting a reviewer over on openedx/wg-coordination#106 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asadali145 Although updating the block structure cache might be an acceptable way to get rid of the issue, But is there anyway we can prevent course structure to cache only those blocks where user has access? It appears it as complete course blocks cache instead of a user access based cache.
lms/djangoapps/grades/tasks.py
Outdated
# When access is restricted and course blocks are cached without the restricted blocks, | ||
# we will update the cache from the store and get the course blocks. | ||
if not subsections_to_update: | ||
update_course_in_cache(course_usage_key.course_key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asadali145 I think problem with using update_course_in_cache
is it would not update blockstructure cache if STORAGE_BACKING_FOR_CACHE
switch is enabled and course published version is same as BlockStructureModel version. See implementation of is_up_to_date for details how it makes update decision.
I think we can clear the blockstructure cache using clear_course_from_cache
and then raise exception SUBSECTION_NOT_FOUND(need add it in one of the known retry exceptions). This would retry the recalculate_subsection_grade_v3
task.
I will look for it. It would be nice if we could do this.
Sure, This makes much more sense. I will do this if I am unable to find an answer to your first question. |
I was looking into this and it turns out to be using the runtime. Method create_from_modulestore is responsible for the issue when it gets the children of an xBlock. When users do not have access to the child blocks of a Unit, there will be no children and the block structure cache will have missing blocks. get_block_for_descriptor is responsible for checking the access. We might be able to fix the issue by manipulating a user attribute and passing the user deep to the block structure manager methods but that can be very tricky. Changes at such a deep level will require us to test so many use cases and will require us to write/update many unit tests. I am going with your suggested change to clear the cache as it is very targeted and easy to manage. |
b020f10
to
ae65d4a
Compare
@ziafazal This is ready for another look. |
lms/djangoapps/grades/tasks.py
Outdated
# cached without the restricted blocks. | ||
if not subsections_to_update: | ||
clear_course_from_cache(course_usage_key.course_key) | ||
raise BlockStructureNotFound(scored_block_usage_key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should create a new exception named SubsectionNotFound
here instead of raising BlockStructureNotFound
because it appears block structure is available but few blocks are missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I use UsageKeyNotInBlockStructure
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that can be used too.
f816b76
to
f828be0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
@asadali145, can you squash your commits? The core contributor could do it when they merge, but there are so many, so I think it would be better if you did it so you can focus on the important ones and keep the commit history clean. After that, I think this is ready to merge. @mphilbrick211, is there anything else we need to do? |
@pdpinch nope, looks like you're good-to-go! Do we know who to ask for merge? |
f828be0
to
82685c1
Compare
Thank you @mphilbrick211. I can take care of the merge. |
@asadali145 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future. |
2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production. |
2U Release Notice: This PR has been deployed to the edX production environment. |
1 similar comment
2U Release Notice: This PR has been deployed to the edX production environment. |
Description
This PR fixes an issue with the grading when course blocks are cached without some of the restricted course blocks. Steps to reproduce the issue are mentioned in the testing steps.
Supporting information
Here is the detailed comment about the issue https://discuss.openedx.org/t/debugging-persistent-grade-issues/11155/8?u=asad_ali
Here are some of the GitHub issues:
https://github.com/mitodl/hq/issues/2460
https://github.com/mitodl/hq/issues/1978
https://github.com/mitodl/hq/issues/2687
Testing instructions
Audit
andVerified
for your course.Verified
enrollments.