Skip to content

Commit c9912b2

Browse files
feat: remove mongodb_proxy in favor of MongoDB's built-in retryReads (#37011)
This PR removes the usage of the custom `mongodb_proxy` package from edx-platform, replacing it with MongoDB's built-in retry functionality via `retryReads=True`. Changes: * Removed all references to `mongodb_proxy.MongoProxy` from connection logic. * Updated `connect_to_mongodb()` to always use `retryReads=True`. * Uninstalled `openedx-mongodbproxy` from edx-platform requirements. Fixes: #35210
1 parent 7e8d888 commit c9912b2

12 files changed

Lines changed: 29 additions & 61 deletions

File tree

requirements/edx/base.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,6 @@ openedx-learning==0.26.0
844844
# via
845845
# -c requirements/edx/../constraints.txt
846846
# -r requirements/edx/kernel.in
847-
openedx-mongodbproxy==0.3.0
848-
# via -r requirements/edx/kernel.in
849847
optimizely-sdk==5.2.0
850848
# via -r requirements/edx/bundled.in
851849
ora2==6.16.3
@@ -958,7 +956,6 @@ pymongo==4.4.0
958956
# event-tracking
959957
# mongoengine
960958
# openedx-forum
961-
# openedx-mongodbproxy
962959
pynacl==1.5.0
963960
# via
964961
# edx-django-utils

requirements/edx/development.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,10 +1405,6 @@ openedx-learning==0.26.0
14051405
# -c requirements/edx/../constraints.txt
14061406
# -r requirements/edx/doc.txt
14071407
# -r requirements/edx/testing.txt
1408-
openedx-mongodbproxy==0.3.0
1409-
# via
1410-
# -r requirements/edx/doc.txt
1411-
# -r requirements/edx/testing.txt
14121408
optimizely-sdk==5.2.0
14131409
# via
14141410
# -r requirements/edx/doc.txt
@@ -1654,7 +1650,6 @@ pymongo==4.4.0
16541650
# event-tracking
16551651
# mongoengine
16561652
# openedx-forum
1657-
# openedx-mongodbproxy
16581653
pynacl==1.5.0
16591654
# via
16601655
# -r requirements/edx/doc.txt

requirements/edx/doc.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,8 +1009,6 @@ openedx-learning==0.26.0
10091009
# via
10101010
# -c requirements/edx/../constraints.txt
10111011
# -r requirements/edx/base.txt
1012-
openedx-mongodbproxy==0.3.0
1013-
# via -r requirements/edx/base.txt
10141012
optimizely-sdk==5.2.0
10151013
# via -r requirements/edx/base.txt
10161014
ora2==6.16.3
@@ -1163,7 +1161,6 @@ pymongo==4.4.0
11631161
# event-tracking
11641162
# mongoengine
11651163
# openedx-forum
1166-
# openedx-mongodbproxy
11671164
pynacl==1.5.0
11681165
# via
11691166
# -r requirements/edx/base.txt

requirements/edx/kernel.in

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ openedx-events # Open edX Events from Hooks Extension Frame
121121
openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50)
122122
openedx-forum # Open edX forum v2 application
123123
openedx-learning # Open edX Learning core (experimental)
124-
openedx-mongodbproxy
125124
openedx-django-wiki
126125
path
127126
piexif # Exif image metadata manipulation, used in the profile_images app

requirements/edx/testing.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,8 +1068,6 @@ openedx-learning==0.26.0
10681068
# via
10691069
# -c requirements/edx/../constraints.txt
10701070
# -r requirements/edx/base.txt
1071-
openedx-mongodbproxy==0.3.0
1072-
# via -r requirements/edx/base.txt
10731071
optimizely-sdk==5.2.0
10741072
# via -r requirements/edx/base.txt
10751073
ora2==6.16.3
@@ -1255,7 +1253,6 @@ pymongo==4.4.0
12551253
# event-tracking
12561254
# mongoengine
12571255
# openedx-forum
1258-
# openedx-mongodbproxy
12591256
pynacl==1.5.0
12601257
# via
12611258
# -r requirements/edx/base.txt

xmodule/contentstore/mongo.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from bson.son import SON
1313
from fs.osfs import OSFS
1414
from gridfs.errors import NoFile, FileExists
15-
from mongodb_proxy import autoretry_read
1615
from opaque_keys.edx.keys import AssetKey
1716

1817
from xmodule.contentstore.content import XASSET_LOCATION_TAG
@@ -29,6 +28,7 @@ class MongoContentStore(ContentStore):
2928
MongoDB-backed ContentStore.
3029
"""
3130
# lint-amnesty, pylint: disable=unused-argument
31+
3232
def __init__(
3333
self, host, db,
3434
port=27017, tz_aware=True, user=None, password=None, bucket='fs', collection=None, **kwargs
@@ -39,16 +39,13 @@ def __init__(
3939
:param collection: ignores but provided for consistency w/ other doc_store_config patterns
4040
"""
4141
# GridFS will throw an exception if the Database is wrapped in a MongoProxy. So don't wrap it.
42-
# The appropriate methods below are marked as autoretry_read - those methods will handle
43-
# the AutoReconnect errors.
4442
self.connection_params = {
4543
'db': db,
4644
'host': host,
4745
'port': port,
4846
'tz_aware': tz_aware,
4947
'user': user,
5048
'password': password,
51-
'proxy': False,
5249
**kwargs
5350
}
5451
self.bucket = bucket
@@ -164,7 +161,6 @@ def delete(self, location_or_id):
164161
# Deletes of non-existent files are considered successful
165162
self.fs.delete(location_or_id)
166163

167-
@autoretry_read()
168164
def find(self, location, throw_on_not_found=True, as_stream=False): # lint-amnesty, pylint: disable=arguments-differ
169165
content_id, __ = self.asset_db_key(location)
170166

@@ -292,7 +288,6 @@ def remove_redundant_content_for_courses(self):
292288
self.fs_files.remove(query)
293289
return assets_to_delete
294290

295-
@autoretry_read()
296291
def _get_all_content_for_course(self,
297292
course_key,
298293
get_thumbnails=False,
@@ -404,7 +399,6 @@ def set_attrs(self, location, attr_dict):
404399
if result.matched_count == 0:
405400
raise NotFoundError(asset_db_key)
406401

407-
@autoretry_read()
408402
def get_attrs(self, location):
409403
"""
410404
Gets all of the attributes associated with the given asset. Note, returns even built in attrs

xmodule/modulestore/mongo/base.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import pymongo
2424
from bson.son import SON
2525
from fs.osfs import OSFS
26-
from mongodb_proxy import autoretry_read
2726
from opaque_keys.edx.keys import CourseKey, UsageKey
2827
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator
2928
from path import Path as path
@@ -85,6 +84,7 @@ class MongoKeyValueStore(InheritanceKeyValueStore):
8584
A KeyValueStore that maps keyed data access to one of the 3 data areas
8685
known to the MongoModuleStore (data, children, and metadata)
8786
"""
87+
8888
def __init__(self, data, metadata):
8989
super().__init__()
9090
if not isinstance(data, dict):
@@ -465,7 +465,6 @@ def __init__(self, contentstore, doc_store_config, fs_root, render_template,
465465
fs_service=None,
466466
user_service=None,
467467
signal_handler=None,
468-
retry_wait_time=0.1,
469468
**kwargs):
470469
"""
471470
:param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware.
@@ -474,7 +473,6 @@ def __init__(self, contentstore, doc_store_config, fs_root, render_template,
474473
super().__init__(contentstore=contentstore, **kwargs)
475474

476475
self.doc_store_config = doc_store_config
477-
self.retry_wait_time = retry_wait_time
478476
self.do_connection(**self.doc_store_config)
479477

480478
if default_class is not None:
@@ -534,7 +532,7 @@ def do_connection(
534532
self.database = connect_to_mongodb(
535533
db, host,
536534
port=port, tz_aware=tz_aware, user=user, password=password,
537-
retry_wait_time=self.retry_wait_time, **kwargs
535+
**kwargs
538536
)
539537

540538
self.collection = self.database[collection]
@@ -569,7 +567,7 @@ def _drop_database(self, database=True, collections=True, connections=True):
569567
connection = self.collection.database.client
570568

571569
if database:
572-
connection.drop_database(self.collection.database.proxied_object)
570+
connection.drop_database(self.collection.database)
573571
elif collections:
574572
self.collection.drop()
575573
else:
@@ -578,7 +576,6 @@ def _drop_database(self, database=True, collections=True, connections=True):
578576
if connections:
579577
connection.close()
580578

581-
@autoretry_read()
582579
def fill_in_run(self, course_key):
583580
"""
584581
In mongo some course_keys are used without runs. This helper function returns
@@ -737,7 +734,6 @@ def _load_items(self, course_key, items, using_descriptor_system=None, for_paren
737734
for item in items
738735
]
739736

740-
@autoretry_read()
741737
def get_course_summaries(self, **kwargs):
742738
"""
743739
Returns a list of `CourseSummary`. This accepts an optional parameter of 'org' which
@@ -786,7 +782,6 @@ def extract_course_summary(course):
786782

787783
return courses_summaries
788784

789-
@autoretry_read()
790785
def get_courses(self, **kwargs):
791786
'''
792787
Returns a list of course descriptors. This accepts an optional parameter of 'org' which
@@ -821,7 +816,6 @@ def get_courses(self, **kwargs):
821816
)
822817
return [course for course in base_list if not isinstance(course, ErrorBlock)]
823818

824-
@autoretry_read()
825819
def _find_one(self, location):
826820
'''Look for a given location in the collection. If the item is not present, raise
827821
ItemNotFoundError.
@@ -867,7 +861,6 @@ def get_course(self, course_key, **kwargs): # lint-amnesty, pylint: disable=arg
867861
except ItemNotFoundError:
868862
return None
869863

870-
@autoretry_read()
871864
def has_course(self, course_key, ignore_case=False, **kwargs): # lint-amnesty, pylint: disable=arguments-differ
872865
"""
873866
Returns the course_id of the course if it was found, else None
@@ -962,7 +955,6 @@ def _id_dict_to_son(id_dict):
962955
for key in ('tag', 'org', 'course', 'category', 'name', 'revision')
963956
])
964957

965-
@autoretry_read()
966958
def get_items( # lint-amnesty, pylint: disable=arguments-differ
967959
self,
968960
course_id,

xmodule/modulestore/split_mongo/mongo_connection.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from django.db.transaction import TransactionManagementError
1818
import pymongo
1919
import pytz
20-
from mongodb_proxy import autoretry_read
2120
# Import this just to export it
2221
from pymongo.errors import DuplicateKeyError # pylint: disable=unused-import
2322
from edx_django_utils import monitoring
@@ -57,6 +56,7 @@ class Tagger:
5756
An object used by :class:`QueryTimer` to allow timed code blocks
5857
to add measurements and tags to the timer.
5958
"""
59+
6060
def __init__(self, default_sample_rate):
6161
self.added_tags = []
6262
self.measures = []
@@ -103,6 +103,7 @@ class QueryTimer:
103103
An object that allows timing a block of code while also recording measurements
104104
about that code.
105105
"""
106+
106107
def __init__(self, metric_base, sample_rate=1):
107108
"""
108109
Arguments:
@@ -198,6 +199,7 @@ class CourseStructureCache:
198199
If the 'course_structure_cache' doesn't exist, then don't do anything for
199200
for set and get.
200201
"""
202+
201203
def __init__(self):
202204
self.cache = None
203205
try:
@@ -264,9 +266,10 @@ class MongoPersistenceBackend:
264266
"""
265267
Segregation of pymongo functions from the data modeling mechanisms for split modulestore.
266268
"""
269+
267270
def __init__(
268271
self, db, collection, host, port=27017, tz_aware=True, user=None, password=None,
269-
asset_collection=None, retry_wait_time=0.1, with_mysql_subclass=False, **kwargs # lint-amnesty, pylint: disable=unused-argument
272+
asset_collection=None, with_mysql_subclass=False, **kwargs # lint-amnesty, pylint: disable=unused-argument
270273
):
271274
"""
272275
Create & open the connection, authenticate, and provide pointers to the collections
@@ -286,7 +289,6 @@ def __init__(
286289
'tz_aware': tz_aware,
287290
'user': user,
288291
'password': password,
289-
'retry_wait_time': retry_wait_time,
290292
**kwargs
291293
}
292294

@@ -362,7 +364,6 @@ def get_structure(self, key, course_context=None):
362364

363365
return structure
364366

365-
@autoretry_read()
366367
def find_structures_by_id(self, ids, course_context=None):
367368
"""
368369
Return all structures that specified in ``ids``.
@@ -379,7 +380,6 @@ def find_structures_by_id(self, ids, course_context=None):
379380
tagger.measure("structures", len(docs))
380381
return docs
381382

382-
@autoretry_read()
383383
def find_courselike_blocks_by_id(self, ids, block_type, course_context=None):
384384
"""
385385
Find all structures that specified in `ids`. Among the blocks only return block whose type is `block_type`.

xmodule/modulestore/split_mongo/split.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363

6464
from bson.objectid import ObjectId
6565
from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator
66-
from mongodb_proxy import autoretry_read
6766
from opaque_keys.edx.keys import CourseKey
6867
from opaque_keys.edx.locator import (
6968
BlockUsageLocator,
@@ -953,7 +952,6 @@ def _create_library_locator(self, library_info, branch):
953952
branch=branch,
954953
)
955954

956-
@autoretry_read()
957955
def get_courses(self, branch, **kwargs): # lint-amnesty, pylint: disable=arguments-differ
958956
"""
959957
Returns a list of course blocks matching any given qualifiers.
@@ -969,7 +967,6 @@ def get_courses(self, branch, **kwargs): # lint-amnesty, pylint: disable=argume
969967
# get the blocks for each course index (s/b the root)
970968
return self._get_structures_for_branch_and_locator(branch, self._create_course_locator, **kwargs)
971969

972-
@autoretry_read()
973970
def get_course_summaries(self, branch, **kwargs):
974971
"""
975972
Returns a list of `CourseSummary` which matching any given qualifiers.
@@ -1026,7 +1023,6 @@ def get_library_keys(self):
10261023
in self.find_matching_course_indexes(branch="library")
10271024
})
10281025

1029-
@autoretry_read()
10301026
def get_library_summaries(self, **kwargs):
10311027
"""
10321028
Returns a list of `LegacyLibrarySummary` objects.
@@ -3255,7 +3251,6 @@ def _update_block_in_structure(self, structure, block_key, content):
32553251
"""
32563252
structure['blocks'][block_key] = content
32573253

3258-
@autoretry_read()
32593254
def find_courses_by_search_target(self, field_name, field_value):
32603255
"""
32613256
Find all the courses which cached that they have the given field with the given value.
@@ -3325,6 +3320,7 @@ class SparseList(list):
33253320
Enable inserting items into a list in arbitrary order and then retrieving them.
33263321
"""
33273322
# taken from http://stackoverflow.com/questions/1857780/sparse-assignment-list-in-python
3323+
33283324
def __setitem__(self, index, value):
33293325
"""
33303326
Add value to the list ensuring the list is long enough to accommodate it at the given index

xmodule/modulestore/tests/test_split_mongo_mongo_connection.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@
1414
class TestHeartbeatFailureException(unittest.TestCase):
1515
""" Test that a heartbeat failure is thrown at the appropriate times """
1616

17-
@patch('pymongo.MongoClient')
18-
@patch('pymongo.database.Database')
19-
def test_heartbeat_raises_exception_when_connection_alive_is_false(self, *calls):
17+
@patch('pymongo.mongo_client.MongoClient')
18+
def test_heartbeat_raises_exception_when_connection_alive_is_false(self, MockClient):
2019
# pylint: disable=W0613
21-
with patch('mongodb_proxy.MongoProxy') as mock_proxy:
22-
mock_proxy.return_value.admin.command.side_effect = ConnectionFailure('Test')
23-
useless_conn = MongoPersistenceBackend('useless', 'useless', 'useless')
2420

25-
with pytest.raises(HeartbeatFailure):
26-
useless_conn.heartbeat()
21+
MockClient.return_value.admin.command.side_effect = ConnectionFailure('Test')
22+
useless_conn = MongoPersistenceBackend('useless', 'useless', 'useless')
23+
24+
with pytest.raises(HeartbeatFailure):
25+
useless_conn.heartbeat()

0 commit comments

Comments
 (0)