Commit 75eee443 by kimth

Xqueue callback acquires lock on StudentModule to avoid race condition

parent 49d36746
...@@ -67,7 +67,7 @@ class StudentModuleCache(object): ...@@ -67,7 +67,7 @@ class StudentModuleCache(object):
""" """
A cache of StudentModules for a specific student A cache of StudentModules for a specific student
""" """
def __init__(self, user, descriptors): def __init__(self, user, descriptors, acquire_lock=False):
''' '''
Find any StudentModule objects that are needed by any descriptor Find any StudentModule objects that are needed by any descriptor
in descriptors. Avoids making multiple queries to the database. in descriptors. Avoids making multiple queries to the database.
...@@ -86,17 +86,23 @@ class StudentModuleCache(object): ...@@ -86,17 +86,23 @@ class StudentModuleCache(object):
self.cache = [] self.cache = []
chunk_size = 500 chunk_size = 500
for id_chunk in [module_ids[i:i + chunk_size] for i in xrange(0, len(module_ids), chunk_size)]: for id_chunk in [module_ids[i:i + chunk_size] for i in xrange(0, len(module_ids), chunk_size)]:
self.cache.extend(StudentModule.objects.filter( if acquire_lock:
student=user, self.cache.extend(StudentModule.objects.select_for_update().filter(
module_state_key__in=id_chunk) student=user,
) module_state_key__in=id_chunk)
)
else:
self.cache.extend(StudentModule.objects.filter(
student=user,
module_state_key__in=id_chunk)
)
else: else:
self.cache = [] self.cache = []
@classmethod @classmethod
def cache_for_descriptor_descendents(cls, user, descriptor, depth=None, descriptor_filter=lambda descriptor: True): def cache_for_descriptor_descendents(cls, user, descriptor, depth=None, descriptor_filter=lambda descriptor: True, acquire_lock=False):
""" """
descriptor: An XModuleDescriptor descriptor: An XModuleDescriptor
depth is the number of levels of descendent modules to load StudentModules for, in addition to depth is the number of levels of descendent modules to load StudentModules for, in addition to
...@@ -122,7 +128,7 @@ class StudentModuleCache(object): ...@@ -122,7 +128,7 @@ class StudentModuleCache(object):
descriptors = get_child_descriptors(descriptor, depth, descriptor_filter) descriptors = get_child_descriptors(descriptor, depth, descriptor_filter)
return StudentModuleCache(user, descriptors) return StudentModuleCache(user, descriptors, acquire_lock)
def _get_module_state_keys(self, descriptors): def _get_module_state_keys(self, descriptors):
''' '''
......
...@@ -160,7 +160,6 @@ def get_module(user, request, location, student_module_cache, position=None): ...@@ -160,7 +160,6 @@ def get_module(user, request, location, student_module_cache, position=None):
instance_state = instance_module.state if instance_module is not None else None instance_state = instance_module.state if instance_module is not None else None
shared_state = shared_module.state if shared_module is not None else None shared_state = shared_module.state if shared_module is not None else None
# TODO (vshnayder): fix hardcoded urls (use reverse)
# Setup system context for module instance # Setup system context for module instance
ajax_url = reverse('modx_dispatch', ajax_url = reverse('modx_dispatch',
...@@ -169,8 +168,6 @@ def get_module(user, request, location, student_module_cache, position=None): ...@@ -169,8 +168,6 @@ def get_module(user, request, location, student_module_cache, position=None):
dispatch=''), dispatch=''),
) )
# ajax_url = settings.MITX_ROOT_URL + '/modx/' + descriptor.location.url() + '/'
# Fully qualified callback URL for external queueing system # Fully qualified callback URL for external queueing system
xqueue_callback_url = request.build_absolute_uri('/')[:-1] # Trailing slash provided by reverse xqueue_callback_url = request.build_absolute_uri('/')[:-1] # Trailing slash provided by reverse
xqueue_callback_url += reverse('xqueue_callback', xqueue_callback_url += reverse('xqueue_callback',
...@@ -304,7 +301,8 @@ def xqueue_callback(request, course_id, userid, id, dispatch): ...@@ -304,7 +301,8 @@ def xqueue_callback(request, course_id, userid, id, dispatch):
# Retrieve target StudentModule # Retrieve target StudentModule
user = User.objects.get(id=userid) user = User.objects.get(id=userid)
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(user, modulestore().get_item(id)) student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
user, modulestore().get_item(id), depth=0, acquire_lock=True)
instance = get_module(user, request, id, student_module_cache) instance = get_module(user, request, id, student_module_cache)
instance_module = get_instance_module(user, instance, student_module_cache) instance_module = get_instance_module(user, instance, student_module_cache)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment