How to Test MongoDB and MySQL Call Counts and XBlock Instantiation Counts

An important performance measurement for code is the number of MongoDB and/or MySQL calls the code makes. Each call is likely a network round-trip in the production environment, which is relatively costly. So the number of calls should generally be minimized and tests should be written that monitors the amount of expected MongoDB / MySQL calls.

MongoDB Call Counting

There's a few Python context managers used to count MongoDB calls:

  • check_mongo_calls
  • check_mongo_calls_range

The context managers are part of edx-platform here:

from xmodule.modulestore.tests.factories import check_mongo_calls

Here's some code snippets which uses the context managers:

    with check_mongo_calls(reads):
        with check_sum_of_calls(XBlock, ['__init__'], xblocks, xblocks, include_arguments=False):
            self.grade_course(self.course)
    with check_mongo_calls_range(max_finds=max_mongo_calls, min_finds=min_mongo_calls):
        _course_overview_1 = CourseOverview.get_from_id(course.id)

To guarantee that no MongoDB calls are made in a section of code, use this pattern:

    with check_mongo_calls(0):
        _course_overview_2 = CourseOverview.get_from_id(course.id)

MySQL Call Counting

MySQL queries are counted with a context manager provided by the Django framework - django.test.TestCase.assertNumQueries.

    with self.assertNumQueries(4):
        outcomes.store_outcome_parameters(params, self.user, self.consumer)

To guarantee that no MySQL calls are made in a section of code, use this pattern:

    with self.assertNumQueries(0):
        outcomes.store_outcome_parameters(params, self.user, self.consumer)

XBlock Instantiation Counting

Instantiating XBlocks can be expensive, and large numbers of instantiations can be indicative of a bad query pattern.

with check_sum_of_calls(XBlock, ['__init__'], instantiations, instantiations, include_arguments=False):
    self.grade_course(self.course)