Skip to content

Commit 74ec258

Browse files
committed
Merge branch 'master' of github.com:mongodb/mongo-python-driver
2 parents 8eab432 + 06872f7 commit 74ec258

File tree

9 files changed

+52
-18
lines changed

9 files changed

+52
-18
lines changed

.evergreen/scripts/resync-all-specs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ then
3333
# we're running locally
3434
python3 ./.evergreen/scripts/resync-all-specs.py
3535
else
36-
/opt/devtools/bin/python3.11 ./.evergreen/scripts/resync-all-specs.py "$PR_DESC"
36+
/opt/devtools/bin/python3.11 ./.evergreen/scripts/resync-all-specs.py --filename "$PR_DESC"
3737
if [[ -f $PR_DESC ]]; then
3838
# changes were made -> call scrypt to create PR for us
3939
.evergreen/scripts/create-spec-pr.sh "$PR_DESC"

.github/workflows/test-python.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Install just
2626
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
2727
- name: Install uv
28-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
28+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
2929
with:
3030
enable-cache: true
3131
python-version: "3.9"
@@ -65,7 +65,7 @@ jobs:
6565
- name: Install just
6666
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
6767
- name: Install uv
68-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
68+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
6969
with:
7070
enable-cache: true
7171
python-version: ${{ matrix.python-version }}
@@ -88,7 +88,7 @@ jobs:
8888
- name: Install just
8989
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
9090
- name: Install uv
91-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
91+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
9292
with:
9393
enable-cache: true
9494
python-version: "3.9"
@@ -111,7 +111,7 @@ jobs:
111111
with:
112112
persist-credentials: false
113113
- name: Install uv
114-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
114+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
115115
with:
116116
enable-cache: true
117117
python-version: "3.9"
@@ -130,7 +130,7 @@ jobs:
130130
with:
131131
persist-credentials: false
132132
- name: Install uv
133-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
133+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
134134
with:
135135
enable-cache: true
136136
python-version: "3.9"
@@ -152,7 +152,7 @@ jobs:
152152
with:
153153
persist-credentials: false
154154
- name: Install uv
155-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
155+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
156156
with:
157157
enable-cache: true
158158
python-version: "${{matrix.python}}"
@@ -231,7 +231,7 @@ jobs:
231231
with:
232232
persist-credentials: false
233233
- name: Install uv
234-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
234+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
235235
with:
236236
python-version: '3.9'
237237
- name: Start MongoDB
@@ -257,7 +257,7 @@ jobs:
257257
with:
258258
persist-credentials: false
259259
- name: Install uv
260-
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v5
260+
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v5
261261
with:
262262
python-version: '3.9'
263263
- name: Start MongoDB

.github/workflows/zizmor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
with:
1919
persist-credentials: false
2020
- name: Run zizmor 🌈
21-
uses: zizmorcore/zizmor-action@0f0557ab4a0b31211d42435e42df31cbd63fdd59
21+
uses: zizmorcore/zizmor-action@1c7106082dbc1753372e3924b7da1b9417011a21

doc/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ PyMongo 4.14 brings a number of changes including:
1010
- Added :meth:`pymongo.asynchronous.mongo_client.AsyncMongoClient.append_metadata` and
1111
:meth:`pymongo.mongo_client.MongoClient.append_metadata` to allow instantiated MongoClients to send client metadata
1212
on-demand
13+
- Improved performance of selecting a server with the Primary selector.
1314

1415
- Introduces a minor breaking change. When encoding :class:`bson.binary.BinaryVector`, a ``ValueError`` will be raised
1516
if the 'padding' metadata field is < 0 or > 7, or non-zero for any type other than PACKED_BIT.

pymongo/topology_description.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from bson.objectid import ObjectId
3535
from pymongo import common
3636
from pymongo.errors import ConfigurationError, PyMongoError
37-
from pymongo.read_preferences import ReadPreference, _AggWritePref, _ServerMode
37+
from pymongo.read_preferences import Primary, ReadPreference, _AggWritePref, _ServerMode
3838
from pymongo.server_description import ServerDescription
3939
from pymongo.server_selectors import Selection
4040
from pymongo.server_type import SERVER_TYPE
@@ -324,6 +324,17 @@ def apply_selector(
324324
description = self.server_descriptions().get(address)
325325
return [description] if description else []
326326

327+
# Primary selection fast path.
328+
if self.topology_type == TOPOLOGY_TYPE.ReplicaSetWithPrimary and type(selector) is Primary:
329+
for sd in self._server_descriptions.values():
330+
if sd.server_type == SERVER_TYPE.RSPrimary:
331+
sds = [sd]
332+
if custom_selector:
333+
sds = custom_selector(sds)
334+
return sds
335+
# No primary found, return an empty list.
336+
return []
337+
327338
selection = Selection.from_topology_description(self)
328339
# Ignore read preference for sharded clusters.
329340
if self.topology_type != TOPOLOGY_TYPE.Sharded:

requirements/docs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ sphinx_rtd_theme>=2,<4
33
readthedocs-sphinx-search~=0.3
44
sphinxcontrib-shellcheck>=1,<2
55
sphinx-autobuild>=2020.9.1
6-
furo==2024.8.6
6+
furo==2025.7.19

test/asynchronous/test_server_selection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ async def test_selector_called(self):
130130
test_collection = mongo_client.testdb.test_collection
131131
self.addAsyncCleanup(mongo_client.drop_database, "testdb")
132132

133-
# Do N operations and test selector is called at least N times.
133+
# Do N operations and test selector is called at least N-1 times due to fast path.
134134
await test_collection.insert_one({"age": 20, "name": "John"})
135135
await test_collection.insert_one({"age": 31, "name": "Jane"})
136136
await test_collection.update_one({"name": "Jane"}, {"$set": {"age": 21}})
137137
await test_collection.find_one({"name": "Roe"})
138-
self.assertGreaterEqual(selector.call_count, 4)
138+
self.assertGreaterEqual(selector.call_count, 3)
139139

140140
@async_client_context.require_replica_set
141141
async def test_latency_threshold_application(self):

test/test_server_selection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ def test_selector_called(self):
130130
test_collection = mongo_client.testdb.test_collection
131131
self.addCleanup(mongo_client.drop_database, "testdb")
132132

133-
# Do N operations and test selector is called at least N times.
133+
# Do N operations and test selector is called at least N-1 times due to fast path.
134134
test_collection.insert_one({"age": 20, "name": "John"})
135135
test_collection.insert_one({"age": 31, "name": "Jane"})
136136
test_collection.update_one({"name": "Jane"}, {"$set": {"age": 21}})
137137
test_collection.find_one({"name": "Roe"})
138-
self.assertGreaterEqual(selector.call_count, 4)
138+
self.assertGreaterEqual(selector.call_count, 3)
139139

140140
@client_context.require_replica_set
141141
def test_latency_threshold_application(self):

test/test_topology.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from pymongo import common
3131
from pymongo.errors import AutoReconnect, ConfigurationError, ConnectionFailure
3232
from pymongo.hello import Hello, HelloCompat
33-
from pymongo.read_preferences import ReadPreference, Secondary
33+
from pymongo.read_preferences import Primary, ReadPreference, Secondary
3434
from pymongo.server_description import ServerDescription
3535
from pymongo.server_selectors import any_server_selector, writable_server_selector
3636
from pymongo.server_type import SERVER_TYPE
@@ -51,7 +51,10 @@ def get_topology_type(self):
5151

5252

5353
def create_mock_topology(
54-
seeds=None, replica_set_name=None, monitor_class=DummyMonitor, direct_connection=False
54+
seeds=None,
55+
replica_set_name=None,
56+
monitor_class=DummyMonitor,
57+
direct_connection=False,
5558
):
5659
partitioned_seeds = list(map(common.partition_node, seeds or ["a"]))
5760
topology_settings = TopologySettings(
@@ -123,6 +126,25 @@ def test_timeout_configuration(self):
123126
# The monitor, not its pool, is responsible for calling hello.
124127
self.assertTrue(monitor._pool.is_sdam)
125128

129+
def test_selector_fast_path(self):
130+
topology = create_mock_topology(seeds=["a", "b:27018"], replica_set_name="foo")
131+
description = topology.description
132+
description._topology_type = TOPOLOGY_TYPE.ReplicaSetWithPrimary
133+
134+
# There is no primary yet, so it should give an empty list.
135+
self.assertEqual(description.apply_selector(Primary()), [])
136+
137+
# If we set a primary server, we should get it back.
138+
sd = list(description._server_descriptions.values())[0]
139+
sd._server_type = SERVER_TYPE.RSPrimary
140+
self.assertEqual(description.apply_selector(Primary()), [sd])
141+
142+
# If there is a custom selector, it should be applied.
143+
def custom_selector(servers):
144+
return []
145+
146+
self.assertEqual(description.apply_selector(Primary(), custom_selector=custom_selector), [])
147+
126148

127149
class TestSingleServerTopology(TopologyTest):
128150
def test_direct_connection(self):

0 commit comments

Comments
 (0)