Optimize test course creation with bulk_operations.

Use bulk operations to both minimize the number of writes
to MongoDB, as well as to suppress signals which could
trigger things like search indexing or course structure
caching.
This commit is contained in:
David Ormsbee
2015-07-23 10:06:44 -04:00
parent cb21756393
commit 701f5fc1c6
3 changed files with 67 additions and 59 deletions

View File

@@ -371,54 +371,54 @@ class ModuleStoreTestCase(TestCase):
"""
Create an equivalent to the toy xml course
"""
# with self.store.bulk_operations(self.store.make_course_key(org, course, run)):
self.toy_loc = self.create_sample_course( # pylint: disable=attribute-defined-outside-init
org, course, run, TOY_BLOCK_INFO_TREE,
{
"textbooks": [["Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"]],
"wiki_slug": "toy",
"display_name": "Toy Course",
"graded": True,
"discussion_topics": {"General": {"id": "i4x-edX-toy-course-2012_Fall"}},
"graceperiod": datetime.timedelta(days=2, seconds=21599),
"start": datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc),
"xml_attributes": {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]},
"pdf_textbooks": [
{
"tab_title": "Sample Multi Chapter Textbook",
"id": "MyTextbook",
"chapters": [
{"url": "/static/Chapter1.pdf", "title": "Chapter 1"},
{"url": "/static/Chapter2.pdf", "title": "Chapter 2"}
]
}
],
"course_image": "just_a_test.jpg",
}
)
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.toy_loc):
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="short_description",
fields={"data": "A course about toys."}
with self.store.bulk_operations(self.store.make_course_key(org, course, run), emit_signals=False):
self.toy_loc = self.create_sample_course( # pylint: disable=attribute-defined-outside-init
org, course, run, TOY_BLOCK_INFO_TREE,
{
"textbooks": [["Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"]],
"wiki_slug": "toy",
"display_name": "Toy Course",
"graded": True,
"discussion_topics": {"General": {"id": "i4x-edX-toy-course-2012_Fall"}},
"graceperiod": datetime.timedelta(days=2, seconds=21599),
"start": datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc),
"xml_attributes": {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]},
"pdf_textbooks": [
{
"tab_title": "Sample Multi Chapter Textbook",
"id": "MyTextbook",
"chapters": [
{"url": "/static/Chapter1.pdf", "title": "Chapter 1"},
{"url": "/static/Chapter2.pdf", "title": "Chapter 2"}
]
}
],
"course_image": "just_a_test.jpg",
}
)
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="effort",
fields={"data": "6 hours"}
)
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="end_date",
fields={"data": "TBD"}
)
self.store.create_item(
self.user.id, self.toy_loc, "course_info", "handouts",
fields={"data": "<a href='/static/handouts/sample_handout.txt'>Sample</a>"}
)
self.store.create_item(
self.user.id, self.toy_loc, "static_tab", "resources",
fields={"display_name": "Resources"},
)
self.store.create_item(
self.user.id, self.toy_loc, "static_tab", "syllabus",
fields={"display_name": "Syllabus"},
)
return self.toy_loc
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.toy_loc):
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="short_description",
fields={"data": "A course about toys."}
)
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="effort",
fields={"data": "6 hours"}
)
self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="end_date",
fields={"data": "TBD"}
)
self.store.create_item(
self.user.id, self.toy_loc, "course_info", "handouts",
fields={"data": "<a href='/static/handouts/sample_handout.txt'>Sample</a>"}
)
self.store.create_item(
self.user.id, self.toy_loc, "static_tab", "resources",
fields={"display_name": "Resources"},
)
self.store.create_item(
self.user.id, self.toy_loc, "static_tab", "syllabus",
fields={"display_name": "Syllabus"},
)
return self.toy_loc

View File

@@ -99,7 +99,11 @@ class CourseFactory(XModuleFactory):
# pylint: disable=unused-argument
@classmethod
def _create(cls, target_class, **kwargs):
"""
Create and return a new course. For performance reasons, we do not emit
signals during this process, but if you need signals to run, you can
pass `emit_signals=True` to this method.
"""
# All class attributes (from this class and base classes) are
# passed in via **kwargs. However, some of those aren't actual field values,
# so pop those off for use separately
@@ -110,20 +114,23 @@ class CourseFactory(XModuleFactory):
name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name'))))
run = kwargs.pop('run', name)
user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test)
emit_signals = kwargs.get('emit_signals', False)
# Pass the metadata just as field=value pairs
kwargs.update(kwargs.pop('metadata', {}))
default_store_override = kwargs.pop('default_store', None)
with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred):
if default_store_override is not None:
with store.default_store(default_store_override):
course_key = store.make_course_key(org, number, run)
with store.bulk_operations(course_key, emit_signals=emit_signals):
if default_store_override is not None:
with store.default_store(default_store_override):
new_course = store.create_course(org, number, run, user_id, fields=kwargs)
else:
new_course = store.create_course(org, number, run, user_id, fields=kwargs)
else:
new_course = store.create_course(org, number, run, user_id, fields=kwargs)
last_course.loc = new_course.location
return new_course
last_course.loc = new_course.location
return new_course
class LibraryFactory(XModuleFactory):

View File

@@ -149,7 +149,7 @@ class ProceduralCourseTestMixin(object):
"""
Contains methods for testing courses generated procedurally
"""
def populate_course(self, branching=2):
def populate_course(self, branching=2, emit_signals=False):
"""
Add k chapters, k^2 sections, k^3 verticals, k^4 problems to self.course (where k = branching)
"""
@@ -172,4 +172,5 @@ class ProceduralCourseTestMixin(object):
)
descend(child, stack[1:])
descend(self.course, ['chapter', 'sequential', 'vertical', 'problem'])
with self.store.bulk_operations(self.course.id, emit_signals=emit_signals):
descend(self.course, ['chapter', 'sequential', 'vertical', 'problem'])