Skip to content

Commit 0fb612a

Browse files
committed
refactor: convert global modulestore to request-cached
Having a global modulestore reference is a holdover from the early days, when the XML modulestore would be extremely expensive to initialize. That is no longer the case, and we should eliminate the global here to reduce the chances of multi-threading bugs that might mutate the global. Unfortunately, a lot of code currently assumes that calls to the modulestore() function are essentially free, instead of the 1-2 ms it really is. There are cases where this may be called thousands of times deep in loops somewhere while doing course content traversal, so it's risky to eliminate the caching behavior altogether. The thought here is that we'll tie to the RequestCache, which should at least not be shared across users/threads.
1 parent d49de13 commit 0fb612a

2 files changed

Lines changed: 19 additions & 38 deletions

File tree

xmodule/modulestore/django.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from django.utils.translation import get_language, to_locale # lint-amnesty, pylint: disable=wrong-import-position
2727
from edx_django_utils.cache import DEFAULT_REQUEST_CACHE # lint-amnesty, pylint: disable=wrong-import-position
2828

29+
from openedx.core.lib.cache_utils import request_cached
2930
from xmodule.contentstore.django import contentstore # lint-amnesty, pylint: disable=wrong-import-position
3031
from xmodule.modulestore.draft_and_published import BranchSettingMixin # lint-amnesty, pylint: disable=wrong-import-position
3132
from xmodule.modulestore.mixed import MixedModuleStore # lint-amnesty, pylint: disable=wrong-import-position
@@ -323,46 +324,27 @@ def fetch_disabled_xblock_types():
323324
**_options
324325
)
325326

326-
327-
# A singleton instance of the Mixed Modulestore
328-
_MIXED_MODULESTORE = None
329-
330-
327+
@request_cached("modulestore")
331328
def modulestore():
332329
"""
333330
Returns the Mixed modulestore
334331
"""
335-
global _MIXED_MODULESTORE # pylint: disable=global-statement
336-
if _MIXED_MODULESTORE is None:
337-
_MIXED_MODULESTORE = create_modulestore_instance(
338-
settings.MODULESTORE['default']['ENGINE'],
339-
contentstore(),
340-
settings.MODULESTORE['default'].get('DOC_STORE_CONFIG', {}),
341-
settings.MODULESTORE['default'].get('OPTIONS', {})
342-
)
343-
344-
if settings.FEATURES.get('CUSTOM_COURSES_EDX'):
345-
# TODO: This import prevents a circular import issue, but is
346-
# symptomatic of a lib having a dependency on code in lms. This
347-
# should be updated to have a setting that enumerates modulestore
348-
# wrappers and then uses that setting to wrap the modulestore in
349-
# appropriate wrappers depending on enabled features.
350-
from lms.djangoapps.ccx.modulestore import CCXModulestoreWrapper
351-
_MIXED_MODULESTORE = CCXModulestoreWrapper(_MIXED_MODULESTORE)
352-
353-
return _MIXED_MODULESTORE
354-
355-
356-
def clear_existing_modulestores():
357-
"""
358-
Clear the existing modulestore instances, causing
359-
them to be re-created when accessed again.
360-
361-
This is useful for flushing state between unit tests.
362-
"""
363-
global _MIXED_MODULESTORE # pylint: disable=global-statement
364-
_MIXED_MODULESTORE = None
365-
332+
mixed_modulestore = create_modulestore_instance(
333+
settings.MODULESTORE['default']['ENGINE'],
334+
contentstore(),
335+
settings.MODULESTORE['default'].get('DOC_STORE_CONFIG', {}),
336+
settings.MODULESTORE['default'].get('OPTIONS', {})
337+
)
338+
if settings.FEATURES.get('CUSTOM_COURSES_EDX'):
339+
# TODO: This import prevents a circular import issue, but is
340+
# symptomatic of a lib having a dependency on code in lms. This
341+
# should be updated to have a setting that enumerates modulestore
342+
# wrappers and then uses that setting to wrap the modulestore in
343+
# appropriate wrappers depending on enabled features.
344+
from lms.djangoapps.ccx.modulestore import CCXModulestoreWrapper
345+
mixed_modulestore = CCXModulestoreWrapper(mixed_modulestore)
346+
347+
return mixed_modulestore
366348

367349
class XBlockI18nService:
368350
"""

xmodule/modulestore/tests/django_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from xmodule.contentstore.content import StaticContent
2020
from xmodule.contentstore.django import _CONTENTSTORE
2121
from xmodule.modulestore import ModuleStoreEnum
22-
from xmodule.modulestore.django import SignalHandler, clear_existing_modulestores, modulestore
22+
from xmodule.modulestore.django import SignalHandler, modulestore
2323
from xmodule.modulestore.tests.factories import XMODULE_FACTORY_LOCK
2424
from xmodule.modulestore.tests.mongo_connection import MONGO_HOST, MONGO_PORT_NUM
2525

@@ -321,7 +321,6 @@ def start_modulestore_isolation(cls):
321321
override.__enter__() # pylint: disable=unnecessary-dunder-call
322322
cls.__settings_overrides.append(override)
323323
XMODULE_FACTORY_LOCK.enable()
324-
clear_existing_modulestores()
325324
cls.store = modulestore()
326325

327326
@classmethod

0 commit comments

Comments
 (0)