From 7127ab863fd3aa5845f2b25a5a35c6b291fbe326 Mon Sep 17 00:00:00 2001 From: "J. Cliff Dyer" Date: Tue, 16 May 2017 11:46:24 -0400 Subject: [PATCH] Use a stable ordering for shuffled tasks. --- .../management/commands/compute_grades.py | 7 ++++--- .../commands/tests/test_compute_grades.py | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/grades/management/commands/compute_grades.py b/lms/djangoapps/grades/management/commands/compute_grades.py index ee32f01809..dae2000080 100644 --- a/lms/djangoapps/grades/management/commands/compute_grades.py +++ b/lms/djangoapps/grades/management/commands/compute_grades.py @@ -4,8 +4,8 @@ Command to compute all grades for specified courses. from __future__ import absolute_import, division, print_function, unicode_literals +import hashlib import logging -from random import shuffle from django.core.management.base import BaseCommand import six @@ -88,7 +88,8 @@ class Command(BaseCommand): Enqueue all tasks, in shuffled order. """ task_options = {'routing_key': options['routing_key']} if options.get('routing_key') else {} - for kwargs in self._shuffled_task_kwargs(options): + for seq_id, kwargs in enumerate(self._shuffled_task_kwargs(options)): + kwargs['seq_id'] = seq_id result = tasks.compute_grades_for_course_v2.apply_async(kwargs=kwargs, **task_options) log.info("Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format( task_name=tasks.compute_grades_for_course.name, @@ -116,7 +117,7 @@ class Command(BaseCommand): # The dictionaries with their extra overhead will be created # and consumed one at a time. all_args.append((six.text_type(course_key), offset, batch_size)) - shuffle(all_args) + all_args.sort(key=lambda x: hashlib.md5(b'{!r}'.format(x))) for args in all_args: yield { 'course_key': args[0], diff --git a/lms/djangoapps/grades/management/commands/tests/test_compute_grades.py b/lms/djangoapps/grades/management/commands/tests/test_compute_grades.py index 76cffdf084..064c7a2296 100644 --- a/lms/djangoapps/grades/management/commands/tests/test_compute_grades.py +++ b/lms/djangoapps/grades/management/commands/tests/test_compute_grades.py @@ -27,6 +27,14 @@ def _sorted_by_batch(calls): return sorted(calls, key=lambda x: (x[1]['kwargs']['course_key'], x[1]['kwargs']['offset'])) +class Any(object): + """ + Dummy object that compares equal to all other objects. + """ + def __eq__(self, other): + return True + + @ddt.ddt class TestComputeGrades(SharedModuleStoreTestCase): """ @@ -100,7 +108,8 @@ class TestComputeGrades(SharedModuleStoreTestCase): 'course_key': course_key, 'batch_size': 2, 'offset': offset, - 'estimate_first_attempted': estimate_first_attempted + 'estimate_first_attempted': estimate_first_attempted, + 'seq_id': Any(), } self.assertEqual( _sorted_by_batch(mock_task.apply_async.call_args_list), @@ -136,7 +145,8 @@ class TestComputeGrades(SharedModuleStoreTestCase): 'course_key': self.course_keys[1], 'batch_size': 2, 'offset': 0, - 'estimate_first_attempted': True + 'estimate_first_attempted': True, + 'seq_id': Any(), }, },), ({ @@ -144,7 +154,8 @@ class TestComputeGrades(SharedModuleStoreTestCase): 'course_key': self.course_keys[1], 'batch_size': 2, 'offset': 2, - 'estimate_first_attempted': True + 'estimate_first_attempted': True, + 'seq_id': Any(), }, },), ],