Commit eb3517c2 by Tasawer

Fixing IntegrityError happened on concurrent requests for course enrollment

ECOM-1730
parent c7e6de0d
...@@ -1030,16 +1030,32 @@ class CourseEnrollment(models.Model): ...@@ -1030,16 +1030,32 @@ class CourseEnrollment(models.Model):
if user.id is None: if user.id is None:
user.save() user.save()
enrollment, created = cls.objects.get_or_create( try:
user=user, enrollment, created = cls.objects.get_or_create(
course_id=course_key, user=user,
) course_id=course_key,
)
# If we *did* just create a new enrollment, set some defaults # If we *did* just create a new enrollment, set some defaults
if created: if created:
enrollment.mode = CourseMode.DEFAULT_MODE_SLUG enrollment.mode = CourseMode.DEFAULT_MODE_SLUG
enrollment.is_active = False enrollment.is_active = False
enrollment.save() enrollment.save()
except IntegrityError:
log.info(
(
"An integrity error occurred while getting-or-creating the enrollment"
"for course key %s and student %s. This can occur if two processes try to get-or-create "
"the enrollment at the same time and the database is set to REPEATABLE READ. We will try "
"committing the transaction and retrying."
),
course_key, user
)
enrollment = cls.objects.get(
user=user,
course_id=course_key,
)
return enrollment return enrollment
......
...@@ -8,12 +8,13 @@ from nose.plugins.attrib import attr ...@@ -8,12 +8,13 @@ from nose.plugins.attrib import attr
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import IntegrityError
from course_modes.models import CourseMode from course_modes.models import CourseMode
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from util.testing import UrlResetMixin from util.testing import UrlResetMixin
from embargo.test_utils import restrict_course from embargo.test_utils import restrict_course
from student.tests.factories import UserFactory, CourseModeFactory from student.tests.factories import UserFactory, CourseModeFactory, CourseEnrollmentFactory
from student.models import CourseEnrollment, CourseFullError from student.models import CourseEnrollment, CourseFullError
from student.roles import ( from student.roles import (
CourseInstructorRole, CourseInstructorRole,
...@@ -281,3 +282,18 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase): ...@@ -281,3 +282,18 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
params['email_opt_in'] = email_opt_in params['email_opt_in'] = email_opt_in
return self.client.post(reverse('change_enrollment'), params) return self.client.post(reverse('change_enrollment'), params)
def test_get_or_create_integrity_error(self):
"""Verify that get_or_create_enrollment handles IntegrityError."""
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
with patch.object(CourseEnrollment.objects, "get_or_create") as mock_get_or_create:
mock_get_or_create.side_effect = IntegrityError
enrollment = CourseEnrollment.get_or_create_enrollment(
self.user,
self.course.id
)
self.assertEqual(enrollment.user, self.user)
self.assertEqual(enrollment.course.id, self.course.id)
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