Side effects of Old Mongo Removal

This post is related to a side effect that we faced after the removal of Mongo.

Context:

We’re aware that there have been efforts to remove old Mongo from open edX and the latest of those PRs was https://github.com/openedx/edx-platform/pull/31134 (There are surely more).

We believe that this had some impacts on Rapid Response xBlock. Rapid response xBlock is an xBlock Aside that we created to add some extended functionality to Problem Blocks. The Rapid Response xBlock allows the instructors to enable the multiple choice problems for rapid response and see the live student responses.

Additional Context:
We have written some integration tests that we run against the master branch of open edX to test the functionality end to end.

We have been refactoring our tests over time with the evolving Open edX code. Things we have used/tried in the past to fix the tests include:

  1. Use of TEST_DATA_MONGO_AMNESTY_MODULESTORE
  2. Refactoring from get_module_system_for_user to prepare_runtime_for_user

Problem:

We acted instantly to check if the xBlock aside itself is breaking or not and luckily that works. Only the TestSuit is breaking.

Our Tests started to break after [OldMongo FC-0004] Remove support for children in Old Mongo by UvgenGen · Pull Request #31134 · openedx/edx-platform · GitHub was merged. The problem is related to the field scope.

Here is the complete error log from one of our tests here
__________________________________________________________________________________________ TestEvents.test_submission_less_than_one __________________________________________________________________________________________

self = <tests.test_events.TestEvents testMethod=test_submission_less_than_one>

    def setUp(self):
>       super().setUp()

tests/test_events.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/utils.py:74: in setUp
    self.course = self.import_test_course()
tests/utils.py:113: in import_test_course
    courses = import_course_from_xml(
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py:775: in import_course_from_xml
    return list(manager.run_imports())
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py:557: in run_imports
    source_courselike, courselike, data_path = self.get_courselike(courselike_key, runtime, dest_id)
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py:593: in get_courselike
    course, course_data_path = self.import_courselike(
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py:430: in import_courselike
    course = _update_and_import_block(
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py:865: in _update_and_import_block
    block = store.import_xblock(
/edx/app/edxapp/edx-platform/lms/djangoapps/ccx/modulestore.py:219: in import_xblock
    return restore(self._modulestore.import_xblock(
/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py:90: in inner
    retval = func(field_decorator=strip_key_collection, *args, **kwargs)
/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py:110: in wrapper
    return func(*args, **kwargs)
/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py:814: in import_xblock
    return store.import_xblock(user_id, course_key, block_type, block_id, fields, runtime, **kwargs)
/edx/app/edxapp/edx-platform/xmodule/modulestore/mongo/base.py:1170: in import_xblock
    xblock = self.create_xblock(runtime, course_key, block_type, block_id, fields)
/edx/app/edxapp/edx-platform/xmodule/modulestore/mongo/draft.py:256: in create_xblock
    new_block = super().create_xblock(  # lint-amnesty, pylint: disable=super-with-arguments
/edx/app/edxapp/edx-platform/xmodule/modulestore/mongo/base.py:1119: in create_xblock
    xblock.save()
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/mixins.py:250: in save
    self.force_save_fields(fields_to_save)
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/mixins.py:265: in force_save_fields
    self._field_data.set_many(self, fields_to_save_json)
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/runtime.py:226: in set_many
    self._kvs.set_many(updated_dict)
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/runtime.py:85: in set_many
    self.set(key, value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = MongoKeyValueStore({'wiki_slug': 'SGAU.SGA101.2017_SGA'}, {'discussion_topics': {'General': {'id': 'i4x-SGAU-SGA101-co..._staff_only': False}], 'display_name': 'SGA Course', 'graceperiod': '2 days', 'start': '2015-01-01T00:00:00Z'})<{}, {}>
key = Key(scope=Scope.children, user_id=None, block_scope_id=BlockUsageLocator(CourseLocator('SGAU', 'SGA101', '2017_SGA', None, None), 'course', '2017_SGA'), field_name='children', block_family='xblock.v1')
value = [BlockUsageLocator(CourseLocator('SGAU', 'SGA101', '2017_SGA', None, None), 'chapter', 'chapter')]

    def set(self, key, value):
        if key.scope == Scope.settings:
            self._metadata[key.field_name] = value
        elif key.scope == Scope.content:
            self._data[key.field_name] = value
        else:
>           raise InvalidScopeError(
                key,
                (Scope.settings, Scope.content),
            )
E           xblock.exceptions.InvalidScopeError

/edx/app/edxapp/edx-platform/xmodule/modulestore/mongo/base.py:115: InvalidScopeError


What we are lookging for?

  • Any documentation about the checklist while migration from old Mongo?
  • Is there any documentation on migrating tests from Old Mongo?
1 Like

Sorry to hear that it’s been a rough migration process for you folks. FWIW, anything labelled AMNESTY will eventually get removed from the system.

The error above happens because it’s trying to store a parent-child relationship, and Old Mongo has been made so that it no longer has those–you can only read the root CourseBlock for backwards compatibility with different apps that expect to read basic course metadata and configuration from it.

The reason it’s going to Old Mongo is because there are CourseKeys and UsageKeys that are using the long deprecated Old Mongo style identifiers:

The updated equivalents of those would be:

course_key = CourseKey.from_string("course-v1:SGAU+SGA101+2017_SGA")
problem_usage_key = UsageKey.from_string(
    "block-v1:SGAU+SGA101+2017_SGA+type@problem+block@2582bbb68672426297e525b49a383eb8"
)

You can also create them this way:

course_key = CourseLocator("SGAU", "SGA101", "2017_SGA")
problem_usage_key = course_key.make_usage_key(
    "problem",
    "2582bbb68672426297e525b49a383eb8"
)

The difference between this CourseLocator and the one in the test is that the default constructor will create it with deprecated=False, while running from from_string with an old style ID will force it to be created with deprecated=True.

The main guidance I would have is to make sure you’re not using any of the old-style IDs anywhere in your tests. Make sure:

  • org/course/run course IDs get converted to course-v1:org+course+run
  • i4x:// gets converted to the block-v1: style (which includes the run)
  • c4x:// gets converted to the asset-v1: style (which includes the run)
1 Like

@dave Thanks for your detailed response and suggestions. We’ll let you know if we face any issues when we try them.