diff --git a/lms/djangoapps/verified_track_content/models.py b/lms/djangoapps/verified_track_content/models.py index 4b5b4f6134..20373aa377 100644 --- a/lms/djangoapps/verified_track_content/models.py +++ b/lms/djangoapps/verified_track_content/models.py @@ -49,7 +49,11 @@ def move_to_verified_cohort(sender, instance, **kwargs): # pylint: disable=unus 'verified_cohort_name': verified_cohort_name, 'default_cohort_name': random_cohort.name } - log.info("Queuing automatic cohorting for user '%s' in course '%s'", instance.user.username, course_key) + log.info( + "Queuing automatic cohorting for user '%s' in course '%s' " + "due to change in enrollment mode from '%s' to '%s'.", + instance.user.id, course_key, instance._old_mode, instance.mode # pylint: disable=protected-access + ) # Do the update with a 3-second delay in hopes that the CourseEnrollment transaction has been # completed before the celery task runs. We want a reasonably short delay in case the learner diff --git a/lms/djangoapps/verified_track_content/tasks.py b/lms/djangoapps/verified_track_content/tasks.py index fce883d0b4..bdaa943f46 100644 --- a/lms/djangoapps/verified_track_content/tasks.py +++ b/lms/djangoapps/verified_track_content/tasks.py @@ -2,6 +2,7 @@ Celery task for Automatic Verifed Track Cohorting MVP feature. """ from django.contrib.auth.models import User +from django.db.utils import IntegrityError from celery.task import task from celery.utils.log import get_task_logger @@ -30,31 +31,43 @@ def sync_cohort_with_mode(self, course_id, user_id, verified_cohort_name, defaul enrollment = CourseEnrollment.get_enrollment(user, course_key) # Note that this will enroll the user in the default cohort on initial enrollment. # That's good because it will force creation of the default cohort if necessary. - current_cohort = get_cohort(user, course_key) + try: + current_cohort = get_cohort(user, course_key) + except IntegrityError as integrity_error: + # It is quite common that get_cohort will throw an IntegrityError. This happens + # when 2 celery workers are both handling enrollment change events for the same user + # (for example, if the enrollment mode goes from None -> Audit -> Honor); if the user + # was not previously in a cohort, calling get_cohort will result in a cohort assignment. + LOGGER.info( + "HANDLING_INTEGRITY_ERROR: IntegrityError encountered for course '%s' and user '%s': %s", + course_id, user.id, unicode(integrity_error) + ) + current_cohort = get_cohort(user, course_key) + verified_cohort = get_cohort_by_name(course_key, verified_cohort_name) if enrollment.mode == CourseMode.VERIFIED and (current_cohort.id != verified_cohort.id): LOGGER.info( "MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort '%s' for course '%s'", - user.username, verified_cohort.name, course_id + user.id, verified_cohort.name, course_id ) add_user_to_cohort(verified_cohort, user.username) elif enrollment.mode != CourseMode.VERIFIED and current_cohort.id == verified_cohort.id: default_cohort = get_cohort_by_name(course_key, default_cohort_name) LOGGER.info( "MOVING_TO_DEFAULT: Moving user '%s' to the default cohort '%s' for course '%s'", - user.username, default_cohort.name, course_id + user.id, default_cohort.name, course_id ) add_user_to_cohort(default_cohort, user.username) else: LOGGER.info( "NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'. " "The user is already in cohort '%s'.", - user.username, course_id, enrollment.mode, current_cohort.name + user.id, course_id, enrollment.mode, current_cohort.name ) except Exception as exc: LOGGER.warning( "SYNC_COHORT_WITH_MODE_RETRY: Exception encountered for course '%s' and user '%s': %s", - course_id, user.username, unicode(exc) + course_id, user.id, unicode(exc) ) raise self.retry(exc=exc) diff --git a/openedx/core/djangoapps/course_groups/models.py b/openedx/core/djangoapps/course_groups/models.py index 14ce7a7fe8..3c3cad5791 100644 --- a/openedx/core/djangoapps/course_groups/models.py +++ b/openedx/core/djangoapps/course_groups/models.py @@ -90,7 +90,7 @@ class CohortMembership(models.Model): def save(self, *args, **kwargs): self.full_clean(validate_unique=False) - log.info("Saving CohortMembership for '%s' (id=%s) in '%s'", self.user.username, self.user.id, self.course_id) + log.info("Saving CohortMembership for user '%s' in '%s'", self.user.id, self.course_id) # Avoid infinite recursion if creating from get_or_create() call below. # This block also allows middleware to use CohortMembership.get_or_create without worrying about outer_atomic