Commit 1838d9d4 by Andy Armstrong

Merge pull request #5020 from edx/hotfix/reduce-studio-mongo-calls

Hotfix - reduce number of Studio mongo calls
parents 5c6a8467 c14891e9
...@@ -867,7 +867,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): ...@@ -867,7 +867,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
# so we don't need to make an extra query to compute it. # so we don't need to make an extra query to compute it.
# set the branch to 'publish' in order to prevent extra lookups of draft versions # set the branch to 'publish' in order to prevent extra lookups of draft versions
with mongo_store.branch_setting(ModuleStoreEnum.Branch.published_only): with mongo_store.branch_setting(ModuleStoreEnum.Branch.published_only):
with check_mongo_calls(mongo_store, 4, 0): with check_mongo_calls(4, 0):
course = mongo_store.get_course(course_id, depth=2) course = mongo_store.get_course(course_id, depth=2)
# make sure we pre-fetched a known sequential which should be at depth=2 # make sure we pre-fetched a known sequential which should be at depth=2
...@@ -879,7 +879,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): ...@@ -879,7 +879,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
# Now, test with the branch set to draft. No extra round trips b/c it doesn't go deep enough to get # Now, test with the branch set to draft. No extra round trips b/c it doesn't go deep enough to get
# beyond direct only categories # beyond direct only categories
with mongo_store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): with mongo_store.branch_setting(ModuleStoreEnum.Branch.draft_preferred):
with check_mongo_calls(mongo_store, 4, 0): with check_mongo_calls(4, 0):
mongo_store.get_course(course_id, depth=2) mongo_store.get_course(course_id, depth=2)
def test_export_course_without_content_store(self): def test_export_course_without_content_store(self):
......
...@@ -201,10 +201,11 @@ class TestCourseListing(ModuleStoreTestCase): ...@@ -201,10 +201,11 @@ class TestCourseListing(ModuleStoreTestCase):
# Now count the db queries # Now count the db queries
store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
with check_mongo_calls(store, USER_COURSES_COUNT): with check_mongo_calls(USER_COURSES_COUNT):
_accessible_courses_list_from_groups(self.request) _accessible_courses_list_from_groups(self.request)
with check_mongo_calls(store, 1): # TODO: LMS-11220: Document why this takes 6 calls
with check_mongo_calls(6):
_accessible_courses_list(self.request) _accessible_courses_list(self.request)
def test_get_course_list_with_same_course_id(self): def test_get_course_list_with_same_course_id(self):
......
...@@ -157,15 +157,15 @@ class ContentStoreImportTest(ModuleStoreTestCase): ...@@ -157,15 +157,15 @@ class ContentStoreImportTest(ModuleStoreTestCase):
store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
# we try to refresh the inheritance tree for each update_item in the import # we try to refresh the inheritance tree for each update_item in the import
with check_exact_number_of_calls(store, store.refresh_cached_metadata_inheritance_tree, 28): with check_exact_number_of_calls(store, "refresh_cached_metadata_inheritance_tree", 28):
# _get_cached_metadata_inheritance_tree should be called only once # _get_cached_metadata_inheritance_tree should be called only once
with check_exact_number_of_calls(store, store._get_cached_metadata_inheritance_tree, 1): with check_exact_number_of_calls(store, "_get_cached_metadata_inheritance_tree", 1):
# with bulk-edit in progress, the inheritance tree should be recomputed only at the end of the import # with bulk-edit in progress, the inheritance tree should be recomputed only at the end of the import
# NOTE: On Jenkins, with memcache enabled, the number of calls here is only 1. # NOTE: On Jenkins, with memcache enabled, the number of calls here is only 1.
# Locally, without memcache, the number of calls is actually 2 (once more during the publish step) # Locally, without memcache, the number of calls is actually 2 (once more during the publish step)
with check_number_of_calls(store, store._compute_metadata_inheritance_tree, 2): with check_number_of_calls(store, "_compute_metadata_inheritance_tree", 2):
self.load_test_import_course() self.load_test_import_course()
def test_rewrite_reference_list(self): def test_rewrite_reference_list(self):
......
...@@ -98,17 +98,23 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -98,17 +98,23 @@ class CourseTestCase(ModuleStoreTestCase):
nonstaff.is_authenticated = True nonstaff.is_authenticated = True
return client, nonstaff return client, nonstaff
def populate_course(self): def populate_course(self, branching=2):
""" """
Add 2 chapters, 4 sections, 8 verticals, 16 problems to self.course (branching 2) Add k chapters, k^2 sections, k^3 verticals, k^4 problems to self.course (where k = branching)
""" """
user_id = self.user.id user_id = self.user.id
self.populated_usage_keys = {}
def descend(parent, stack): def descend(parent, stack):
xblock_type = stack.pop(0) if not stack:
for _ in range(2): return
xblock_type = stack[0]
for _ in range(branching):
child = ItemFactory.create(category=xblock_type, parent_location=parent.location, user_id=user_id) child = ItemFactory.create(category=xblock_type, parent_location=parent.location, user_id=user_id)
if stack: print child.location
descend(child, stack) self.populated_usage_keys.setdefault(xblock_type, []).append(child.location)
descend(child, stack[1:])
descend(self.course, ['chapter', 'sequential', 'vertical', 'problem']) descend(self.course, ['chapter', 'sequential', 'vertical', 'problem'])
......
...@@ -28,6 +28,7 @@ from opaque_keys.edx.keys import UsageKey ...@@ -28,6 +28,7 @@ from opaque_keys.edx.keys import UsageKey
from .access import has_course_access from .access import has_course_access
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from models.settings.course_grading import CourseGradingModel
__all__ = ['OPEN_ENDED_COMPONENT_TYPES', __all__ = ['OPEN_ENDED_COMPONENT_TYPES',
'ADVANCED_COMPONENT_POLICY_KEY', 'ADVANCED_COMPONENT_POLICY_KEY',
......
...@@ -571,7 +571,7 @@ def _get_xblock(usage_key, user): ...@@ -571,7 +571,7 @@ def _get_xblock(usage_key, user):
""" """
store = modulestore() store = modulestore()
try: try:
return store.get_item(usage_key) return store.get_item(usage_key, depth=None)
except ItemNotFoundError: except ItemNotFoundError:
if usage_key.category in CREATE_IF_NOT_FOUND: if usage_key.category in CREATE_IF_NOT_FOUND:
# Create a new one for certain categories only. Used for course info handouts. # Create a new one for certain categories only. Used for course info handouts.
...@@ -596,6 +596,9 @@ def _get_module_info(xblock, rewrite_static_links=True): ...@@ -596,6 +596,9 @@ def _get_module_info(xblock, rewrite_static_links=True):
course_id=xblock.location.course_key course_id=xblock.location.course_key
) )
# Pre-cache has changes for the entire course because we'll need it for the ancestor info
modulestore().has_changes(modulestore().get_course(xblock.location.course_key, depth=None))
# Note that children aren't being returned until we have a use case. # Note that children aren't being returned until we have a use case.
return create_xblock_info(xblock, data=data, metadata=own_metadata(xblock), include_ancestor_info=True) return create_xblock_info(xblock, data=data, metadata=own_metadata(xblock), include_ancestor_info=True)
...@@ -756,7 +759,7 @@ def _compute_visibility_state(xblock, child_info, is_unit_with_changes): ...@@ -756,7 +759,7 @@ def _compute_visibility_state(xblock, child_info, is_unit_with_changes):
return VisibilityState.needs_attention return VisibilityState.needs_attention
is_unscheduled = xblock.start == DEFAULT_START_DATE is_unscheduled = xblock.start == DEFAULT_START_DATE
is_live = datetime.now(UTC) > xblock.start is_live = datetime.now(UTC) > xblock.start
children = child_info and child_info['children'] children = child_info and child_info.get('children', [])
if children and len(children) > 0: if children and len(children) > 0:
all_staff_only = True all_staff_only = True
all_unscheduled = True all_unscheduled = True
......
"""Tests for items views.""" """Tests for items views."""
import os
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
import ddt import ddt
from unittest import skipUnless
from mock import patch from mock import patch
from pytz import UTC from pytz import UTC
...@@ -26,7 +24,7 @@ from student.tests.factories import UserFactory ...@@ -26,7 +24,7 @@ from student.tests.factories import UserFactory
from xmodule.capa_module import CapaDescriptor from xmodule.capa_module import CapaDescriptor
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.factories import ItemFactory from xmodule.modulestore.tests.factories import ItemFactory, check_mongo_calls
from xmodule.x_module import STUDIO_VIEW, STUDENT_VIEW from xmodule.x_module import STUDIO_VIEW, STUDENT_VIEW
from xblock.exceptions import NoSuchHandlerError from xblock.exceptions import NoSuchHandlerError
from opaque_keys.edx.keys import UsageKey, CourseKey from opaque_keys.edx.keys import UsageKey, CourseKey
...@@ -83,7 +81,8 @@ class ItemTest(CourseTestCase): ...@@ -83,7 +81,8 @@ class ItemTest(CourseTestCase):
return self.response_usage_key(resp) return self.response_usage_key(resp)
class GetItem(ItemTest): @ddt.ddt
class GetItemTest(ItemTest):
"""Tests for '/xblock' GET url.""" """Tests for '/xblock' GET url."""
def _get_container_preview(self, usage_key): def _get_container_preview(self, usage_key):
...@@ -100,6 +99,25 @@ class GetItem(ItemTest): ...@@ -100,6 +99,25 @@ class GetItem(ItemTest):
self.assertIsNotNone(resources) self.assertIsNotNone(resources)
return html, resources return html, resources
@ddt.data(
(1, 21, 23, 35, 37),
(2, 22, 24, 38, 39),
(3, 23, 25, 41, 41),
)
@ddt.unpack
def test_get_query_count(self, branching_factor, chapter_queries, section_queries, unit_queries, problem_queries):
self.populate_course(branching_factor)
# Retrieve it
with check_mongo_calls(chapter_queries):
self.client.get(reverse_usage_url('xblock_handler', self.populated_usage_keys['chapter'][-1]))
with check_mongo_calls(section_queries):
self.client.get(reverse_usage_url('xblock_handler', self.populated_usage_keys['sequential'][-1]))
with check_mongo_calls(unit_queries):
self.client.get(reverse_usage_url('xblock_handler', self.populated_usage_keys['vertical'][-1]))
with check_mongo_calls(problem_queries):
self.client.get(reverse_usage_url('xblock_handler', self.populated_usage_keys['problem'][-1]))
def test_get_vertical(self): def test_get_vertical(self):
# Add a vertical # Add a vertical
resp = self.create_xblock(category='vertical') resp = self.create_xblock(category='vertical')
......
...@@ -24,6 +24,7 @@ from opaque_keys import InvalidKeyError ...@@ -24,6 +24,7 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xblock.runtime import Mixologist from xblock.runtime import Mixologist
from xblock.core import XBlock from xblock.core import XBlock
import functools
log = logging.getLogger('edx.modulestore') log = logging.getLogger('edx.modulestore')
...@@ -559,6 +560,36 @@ class ModuleStoreReadBase(ModuleStoreRead): ...@@ -559,6 +560,36 @@ class ModuleStoreReadBase(ModuleStoreRead):
raise ValueError(u"Cannot set default store to type {}".format(store_type)) raise ValueError(u"Cannot set default store to type {}".format(store_type))
yield yield
@staticmethod
def memoize_request_cache(func):
"""
Memoize a function call results on the request_cache if there's one. Creates the cache key by
joining the unicode of all the args with &; so, if your arg may use the default &, it may
have false hits
"""
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if self.request_cache:
cache_key = '&'.join([hashvalue(arg) for arg in args])
if cache_key in self.request_cache.data.setdefault(func.__name__, {}):
return self.request_cache.data[func.__name__][cache_key]
result = func(self, *args, **kwargs)
self.request_cache.data[func.__name__][cache_key] = result
return result
else:
return func(self, *args, **kwargs)
return wrapper
def hashvalue(arg):
"""
If arg is an xblock, use its location. otherwise just turn it into a string
"""
if isinstance(arg, XBlock):
return unicode(arg.location)
else:
return unicode(arg)
class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite): class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
''' '''
......
...@@ -589,13 +589,13 @@ class DraftModuleStore(MongoModuleStore): ...@@ -589,13 +589,13 @@ class DraftModuleStore(MongoModuleStore):
_internal([root_usage.to_deprecated_son() for root_usage in root_usages]) _internal([root_usage.to_deprecated_son() for root_usage in root_usages])
self.collection.remove({'_id': {'$in': to_be_deleted}}, safe=self.collection.safe) self.collection.remove({'_id': {'$in': to_be_deleted}}, safe=self.collection.safe)
@MongoModuleStore.memoize_request_cache
def has_changes(self, xblock): def has_changes(self, xblock):
""" """
Check if the xblock or its children have been changed since the last publish. Check if the xblock or its children have been changed since the last publish.
:param xblock: xblock to check :param xblock: xblock to check
:return: True if the draft and published versions differ :return: True if the draft and published versions differ
""" """
# don't check children if this block has changes (is not public) # don't check children if this block has changes (is not public)
if self.compute_publish_state(xblock) != PublishState.public: if self.compute_publish_state(xblock) != PublishState.public:
return True return True
......
...@@ -10,6 +10,7 @@ from xmodule.modulestore.django import modulestore, clear_existing_modulestores ...@@ -10,6 +10,7 @@ from xmodule.modulestore.django import modulestore, clear_existing_modulestores
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
import datetime import datetime
import pytz import pytz
from request_cache.middleware import RequestCache
from xmodule.tabs import CoursewareTab, CourseInfoTab, StaticTab, DiscussionTab, ProgressTab, WikiTab from xmodule.tabs import CoursewareTab, CourseInfoTab, StaticTab, DiscussionTab, ProgressTab, WikiTab
from xmodule.modulestore.tests.sample_courses import default_block_info_tree, TOY_BLOCK_INFO_TREE from xmodule.modulestore.tests.sample_courses import default_block_info_tree, TOY_BLOCK_INFO_TREE
from xmodule.modulestore.tests.mongo_connection import MONGO_PORT_NUM, MONGO_HOST from xmodule.modulestore.tests.mongo_connection import MONGO_PORT_NUM, MONGO_HOST
...@@ -275,6 +276,8 @@ class ModuleStoreTestCase(TestCase): ...@@ -275,6 +276,8 @@ class ModuleStoreTestCase(TestCase):
# the next time they are accessed. # the next time they are accessed.
# We do this at *both* setup and teardown just to be safe. # We do this at *both* setup and teardown just to be safe.
clear_existing_modulestores() clear_existing_modulestores()
# clear RequestCache to emulate its clearance after each http request.
RequestCache().clear_request_cache()
# Call superclass implementation # Call superclass implementation
super(ModuleStoreTestCase, self)._post_teardown() super(ModuleStoreTestCase, self)._post_teardown()
......
import pprint
import pymongo.message
from factory import Factory, lazy_attribute_sequence, lazy_attribute from factory import Factory, lazy_attribute_sequence, lazy_attribute
from factory.containers import CyclicDefinitionError from factory.containers import CyclicDefinitionError
from uuid import uuid4 from uuid import uuid4
...@@ -214,88 +217,83 @@ class ItemFactory(XModuleFactory): ...@@ -214,88 +217,83 @@ class ItemFactory(XModuleFactory):
@contextmanager @contextmanager
def check_exact_number_of_calls(object_with_method, method, num_calls, method_name=None): def check_exact_number_of_calls(object_with_method, method_name, num_calls):
""" """
Instruments the given method on the given object to verify the number of calls to the Instruments the given method on the given object to verify the number of calls to the
method is exactly equal to 'num_calls'. method is exactly equal to 'num_calls'.
""" """
with check_number_of_calls(object_with_method, method, num_calls, num_calls, method_name): with check_number_of_calls(object_with_method, method_name, num_calls, num_calls):
yield yield
@contextmanager def check_number_of_calls(object_with_method, method_name, maximum_calls, minimum_calls=1):
def check_number_of_calls(object_with_method, method, maximum_calls, minimum_calls=1, method_name=None):
""" """
Instruments the given method on the given object to verify the number of calls to the method is Instruments the given method on the given object to verify the number of calls to the method is
less than or equal to the expected maximum_calls and greater than or equal to the expected minimum_calls. less than or equal to the expected maximum_calls and greater than or equal to the expected minimum_calls.
""" """
method_wrap = Mock(wraps=method) return check_sum_of_calls(object_with_method, [method_name], maximum_calls, minimum_calls)
wrap_patch = patch.object(object_with_method, method_name or method.__name__, method_wrap)
try:
wrap_patch.start() @contextmanager
def check_sum_of_calls(object_, methods, maximum_calls, minimum_calls=1):
"""
Instruments the given methods on the given object to verify that the total sum of calls made to the
methods falls between minumum_calls and maximum_calls.
"""
mocks = {
method: Mock(wraps=getattr(object_, method))
for method in methods
}
with patch.multiple(object_, **mocks):
yield yield
finally: call_count = sum(mock.call_count for mock in mocks.values())
wrap_patch.stop() calls = pprint.pformat({
method_name: mock.call_args_list
for method_name, mock in mocks.items()
})
# Assertion errors don't handle multi-line values, so pretty-print to std-out instead
if not minimum_calls <= call_count <= maximum_calls:
print "Expected between {} and {} calls, {} were made. Calls: {}".format(
minimum_calls,
maximum_calls,
call_count,
calls,
)
# verify the counter actually worked by ensuring we have counted greater than (or equal to) the minimum calls # verify the counter actually worked by ensuring we have counted greater than (or equal to) the minimum calls
assert_greater_equal(method_wrap.call_count, minimum_calls) assert_greater_equal(call_count, minimum_calls)
# now verify the number of actual calls is less than (or equal to) the expected maximum # now verify the number of actual calls is less than (or equal to) the expected maximum
assert_less_equal(method_wrap.call_count, maximum_calls) assert_less_equal(call_count, maximum_calls)
@contextmanager @contextmanager
def check_mongo_calls(mongo_store, num_finds=0, num_sends=None): def check_mongo_calls(num_finds=0, num_sends=None):
""" """
Instruments the given store to count the number of calls to find (incl find_one) and the number Instruments the given store to count the number of calls to find (incl find_one) and the number
of calls to send_message which is for insert, update, and remove (if you provide num_sends). At the of calls to send_message which is for insert, update, and remove (if you provide num_sends). At the
end of the with statement, it compares the counts to the num_finds and num_sends. end of the with statement, it compares the counts to the num_finds and num_sends.
:param mongo_store: the MongoModulestore or subclass to watch or a SplitMongoModuleStore
:param num_finds: the exact number of find calls expected :param num_finds: the exact number of find calls expected
:param num_sends: If none, don't instrument the send calls. If non-none, count and compare to :param num_sends: If none, don't instrument the send calls. If non-none, count and compare to
the given int value. the given int value.
""" """
if mongo_store.get_modulestore_type() == ModuleStoreEnum.Type.mongo: with check_sum_of_calls(
with check_exact_number_of_calls(mongo_store.collection, mongo_store.collection.find, num_finds): pymongo.message,
if num_sends is not None: ['query', 'get_more'],
with check_exact_number_of_calls( num_finds,
mongo_store.database.connection, num_finds
mongo_store.database.connection._send_message, # pylint: disable=protected-access
num_sends,
): ):
yield
else:
yield
elif mongo_store.get_modulestore_type() == ModuleStoreEnum.Type.split:
collections = [
mongo_store.db_connection.course_index,
mongo_store.db_connection.structures,
mongo_store.db_connection.definitions,
]
# could add else clause which raises exception or just rely on the below to suss that out
try:
find_wraps = []
wrap_patches = []
for collection in collections:
find_wrap = Mock(wraps=collection.find)
find_wraps.append(find_wrap)
wrap_patch = patch.object(collection, 'find', find_wrap)
wrap_patches.append(wrap_patch)
wrap_patch.start()
if num_sends is not None: if num_sends is not None:
connection = mongo_store.db_connection.database.connection with check_sum_of_calls(
with check_exact_number_of_calls( pymongo.message,
connection, ['insert', 'update', 'delete'],
connection._send_message, # pylint: disable=protected-access
num_sends, num_sends,
num_sends
): ):
yield yield
else: else:
yield yield
finally:
map(lambda wrap_patch: wrap_patch.stop(), wrap_patches)
call_count = sum([find_wrap.call_count for find_wrap in find_wraps])
assert_equal(call_count, num_finds)
...@@ -19,23 +19,35 @@ class TestPublish(SplitWMongoCourseBoostrapper): ...@@ -19,23 +19,35 @@ class TestPublish(SplitWMongoCourseBoostrapper):
# There are 12 created items and 7 parent updates # There are 12 created items and 7 parent updates
# create course: finds: 1 to verify uniqueness, 1 to find parents # create course: finds: 1 to verify uniqueness, 1 to find parents
# sends: 1 to create course, 1 to create overview # sends: 1 to create course, 1 to create overview
with check_mongo_calls(self.draft_mongo, 5, 2): with check_mongo_calls(5, 2):
super(TestPublish, self)._create_course(split=False) # 2 inserts (course and overview) super(TestPublish, self)._create_course(split=False) # 2 inserts (course and overview)
# with bulk will delay all inheritance computations which won't be added into the mongo_calls # with bulk will delay all inheritance computations which won't be added into the mongo_calls
with self.draft_mongo.bulk_write_operations(self.old_course_key): with self.draft_mongo.bulk_write_operations(self.old_course_key):
# finds: 1 for parent to add child # finds: 1 for parent to add child
# sends: 1 for insert, 1 for parent (add child) # sends: 1 for insert, 1 for parent (add child)
with check_mongo_calls(self.draft_mongo, 1, 2): with check_mongo_calls(1, 2):
self._create_item('chapter', 'Chapter1', {}, {'display_name': 'Chapter 1'}, 'course', 'runid', split=False) self._create_item('chapter', 'Chapter1', {}, {'display_name': 'Chapter 1'}, 'course', 'runid', split=False)
with check_mongo_calls(self.draft_mongo, 2, 2): with check_mongo_calls(2, 2):
self._create_item('chapter', 'Chapter2', {}, {'display_name': 'Chapter 2'}, 'course', 'runid', split=False) self._create_item('chapter', 'Chapter2', {}, {'display_name': 'Chapter 2'}, 'course', 'runid', split=False)
# update info propagation is 2 levels. create looks for draft and then published and then creates # For each vertical (2) created:
with check_mongo_calls(self.draft_mongo, 8, 6): # - load draft
# - load non-draft
# - get last error
# - load parent
# - load inheritable data
with check_mongo_calls(10, 6):
self._create_item('vertical', 'Vert1', {}, {'display_name': 'Vertical 1'}, 'chapter', 'Chapter1', split=False) self._create_item('vertical', 'Vert1', {}, {'display_name': 'Vertical 1'}, 'chapter', 'Chapter1', split=False)
self._create_item('vertical', 'Vert2', {}, {'display_name': 'Vertical 2'}, 'chapter', 'Chapter1', split=False) self._create_item('vertical', 'Vert2', {}, {'display_name': 'Vertical 2'}, 'chapter', 'Chapter1', split=False)
with check_mongo_calls(self.draft_mongo, 20, 12): # For each (4) item created
# - load draft
# - load non-draft
# - get last error
# - load parent
# - load inheritable data
# - load parent
with check_mongo_calls(24, 12):
self._create_item('html', 'Html1', "<p>Goodbye</p>", {'display_name': 'Parented Html'}, 'vertical', 'Vert1', split=False) self._create_item('html', 'Html1', "<p>Goodbye</p>", {'display_name': 'Parented Html'}, 'vertical', 'Vert1', split=False)
self._create_item( self._create_item(
'discussion', 'Discussion1', 'discussion', 'Discussion1',
...@@ -63,7 +75,7 @@ class TestPublish(SplitWMongoCourseBoostrapper): ...@@ -63,7 +75,7 @@ class TestPublish(SplitWMongoCourseBoostrapper):
split=False split=False
) )
with check_mongo_calls(self.draft_mongo, 0, 2): with check_mongo_calls(0, 2):
# 2 finds b/c looking for non-existent parents # 2 finds b/c looking for non-existent parents
self._create_item('static_tab', 'staticuno', "<p>tab</p>", {'display_name': 'Tab uno'}, None, None, split=False) self._create_item('static_tab', 'staticuno', "<p>tab</p>", {'display_name': 'Tab uno'}, None, None, split=False)
self._create_item('course_info', 'updates', "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {}, None, None, split=False) self._create_item('course_info', 'updates', "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {}, None, None, split=False)
...@@ -76,10 +88,11 @@ class TestPublish(SplitWMongoCourseBoostrapper): ...@@ -76,10 +88,11 @@ class TestPublish(SplitWMongoCourseBoostrapper):
vert_location = self.old_course_key.make_usage_key('vertical', block_id='Vert1') vert_location = self.old_course_key.make_usage_key('vertical', block_id='Vert1')
item = self.draft_mongo.get_item(vert_location, 2) item = self.draft_mongo.get_item(vert_location, 2)
# Vert1 has 3 children; so, publishes 4 nodes which may mean 4 inserts & 1 bulk remove # Vert1 has 3 children; so, publishes 4 nodes which may mean 4 inserts & 1 bulk remove
# TODO: LMS-11220: Document why find count is 25
# 25-June-2014 find calls are 19. Probably due to inheritance recomputation? # 25-June-2014 find calls are 19. Probably due to inheritance recomputation?
# 02-July-2014 send calls are 7. 5 from above, plus 2 for updating subtree edit info for Chapter1 and course # 02-July-2014 send calls are 7. 5 from above, plus 2 for updating subtree edit info for Chapter1 and course
# find calls are 22. 19 from above, plus 3 for finding the parent of Vert1, Chapter1, and course # find calls are 22. 19 from above, plus 3 for finding the parent of Vert1, Chapter1, and course
with check_mongo_calls(self.draft_mongo, 22, 7): with check_mongo_calls(25, 7):
self.draft_mongo.publish(item.location, self.user_id) self.draft_mongo.publish(item.location, self.user_id)
# verify status # verify status
......
...@@ -607,7 +607,7 @@ def _import_course_draft( ...@@ -607,7 +607,7 @@ def _import_course_draft(
_import_module(descriptor) _import_module(descriptor)
except Exception: except Exception:
logging.exception('There while importing draft descriptor %s', descriptor) logging.exception('while importing draft descriptor %s', descriptor)
def allowed_metadata_by_category(category): def allowed_metadata_by_category(category):
......
...@@ -326,7 +326,7 @@ class TestTOC(ModuleStoreTestCase): ...@@ -326,7 +326,7 @@ class TestTOC(ModuleStoreTestCase):
self.request = factory.get(chapter_url) self.request = factory.get(chapter_url)
self.request.user = UserFactory() self.request.user = UserFactory()
self.modulestore = self.store._get_modulestore_for_courseid(self.course_key) self.modulestore = self.store._get_modulestore_for_courseid(self.course_key)
with check_mongo_calls(self.modulestore, num_finds, num_sends): with check_mongo_calls(num_finds, num_sends):
self.toy_course = self.store.get_course(self.toy_loc, depth=2) self.toy_course = self.store.get_course(self.toy_loc, depth=2)
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
self.toy_loc, self.request.user, self.toy_course, depth=2) self.toy_loc, self.request.user, self.toy_course, depth=2)
...@@ -352,7 +352,7 @@ class TestTOC(ModuleStoreTestCase): ...@@ -352,7 +352,7 @@ class TestTOC(ModuleStoreTestCase):
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
with check_mongo_calls(self.modulestore, 0, 0): with check_mongo_calls(0, 0):
actual = render.toc_for_course( actual = render.toc_for_course(
self.request.user, self.request, self.toy_course, self.chapter, None, self.field_data_cache self.request.user, self.request, self.toy_course, self.chapter, None, self.field_data_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