From 309b1a3b5d0594964176b1420b694b67fc8d49ed Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Fri, 31 Jan 2025 10:18:11 -0800 Subject: [PATCH 1/4] PYTHON-4960 Add tests --- test/discovery_and_monitoring/rs/new_primary.json | 3 ++- .../rs/new_primary_new_electionid.json | 6 ++++-- .../rs/new_primary_new_setversion.json | 6 ++++-- .../rs/primary_disconnect_electionid.json | 3 ++- .../rs/primary_disconnect_setversion.json | 3 ++- .../rs/setversion_greaterthan_max_without_electionid.json | 3 ++- .../rs/setversion_without_electionid-pre-6.0.json | 3 ++- .../rs/use_setversion_without_electionid-pre-6.0.json | 6 ++++-- .../rs/use_setversion_without_electionid.json | 6 ++++-- test/test_discovery_and_monitoring.py | 3 +++ 10 files changed, 29 insertions(+), 13 deletions(-) diff --git a/test/discovery_and_monitoring/rs/new_primary.json b/test/discovery_and_monitoring/rs/new_primary.json index 1a84c69c91..69b07516b9 100644 --- a/test/discovery_and_monitoring/rs/new_primary.json +++ b/test/discovery_and_monitoring/rs/new_primary.json @@ -58,7 +58,8 @@ "servers": { "a:27017": { "type": "Unknown", - "setName": null + "setName": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/new_primary_new_electionid.json b/test/discovery_and_monitoring/rs/new_primary_new_electionid.json index 509720d445..ec6e736d55 100644 --- a/test/discovery_and_monitoring/rs/new_primary_new_electionid.json +++ b/test/discovery_and_monitoring/rs/new_primary_new_electionid.json @@ -76,7 +76,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", @@ -123,7 +124,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/new_primary_new_setversion.json b/test/discovery_and_monitoring/rs/new_primary_new_setversion.json index 96533c61ee..1db05cbd5a 100644 --- a/test/discovery_and_monitoring/rs/new_primary_new_setversion.json +++ b/test/discovery_and_monitoring/rs/new_primary_new_setversion.json @@ -76,7 +76,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", @@ -123,7 +124,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json b/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json index 5a91188ea8..d87f7a3612 100644 --- a/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json +++ b/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json @@ -48,7 +48,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json b/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json index f7417ad77b..0a59fd9ce6 100644 --- a/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json +++ b/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json @@ -48,7 +48,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json b/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json index 97870d71d5..e345165947 100644 --- a/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json +++ b/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json @@ -65,7 +65,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json b/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json index e62c6963ed..6b6cd39255 100644 --- a/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json +++ b/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json @@ -65,7 +65,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json b/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json index 2f9b567b85..50c6666ee5 100644 --- a/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json +++ b/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json @@ -73,7 +73,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", @@ -117,7 +118,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/use_setversion_without_electionid.json b/test/discovery_and_monitoring/rs/use_setversion_without_electionid.json index 551f3e12c2..eaf586d728 100644 --- a/test/discovery_and_monitoring/rs/use_setversion_without_electionid.json +++ b/test/discovery_and_monitoring/rs/use_setversion_without_electionid.json @@ -81,7 +81,8 @@ "b:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" } }, "topologyType": "ReplicaSetWithPrimary", @@ -128,7 +129,8 @@ "b:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" } }, "topologyType": "ReplicaSetWithPrimary", diff --git a/test/test_discovery_and_monitoring.py b/test/test_discovery_and_monitoring.py index ce7a52f1a0..3fd3a73a65 100644 --- a/test/test_discovery_and_monitoring.py +++ b/test/test_discovery_and_monitoring.py @@ -166,6 +166,9 @@ def check_outcome(self, topology, outcome): server_type_name(expected_server_type), server_type_name(actual_server_description.server_type), ) + expected_error = expected_server.get("error") + if expected_error: + self.assertIn(expected_error, str(actual_server_description.error)) self.assertEqual(expected_server.get("setName"), actual_server_description.replica_set_name) From 372b364600a65a48abf888766cc8e607c2deb83e Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Fri, 31 Jan 2025 10:37:06 -0800 Subject: [PATCH 2/4] PYTHON-4960 Add more information to ServerDescriptions when marking a primary as stale --- pymongo/topology_description.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index f669fefd2e..742bbf8c6e 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -33,7 +33,7 @@ from bson.min_key import MinKey from bson.objectid import ObjectId from pymongo import common -from pymongo.errors import ConfigurationError +from pymongo.errors import ConfigurationError, PyMongoError from pymongo.read_preferences import ReadPreference, _AggWritePref, _ServerMode from pymongo.server_description import ServerDescription from pymongo.server_selectors import Selection @@ -563,7 +563,11 @@ def _update_rs_from_primary( if None not in new_election_tuple: if None not in max_election_tuple and new_election_tuple < max_election_tuple: # Stale primary, set to type Unknown. - sds[server_description.address] = server_description.to_unknown() + sds[server_description.address] = server_description.to_unknown( + PyMongoError( + f"primary marked stale due to electionId/setVersion mismatch, {new_election_tuple} is stale compared to {max_election_tuple}" + ) + ) return _check_has_primary(sds), replica_set_name, max_set_version, max_election_id max_election_id = server_description.election_id @@ -578,7 +582,11 @@ def _update_rs_from_primary( max_election_safe = tuple(MinKey() if i is None else i for i in max_election_tuple) if new_election_safe < max_election_safe: # Stale primary, set to type Unknown. - sds[server_description.address] = server_description.to_unknown() + sds[server_description.address] = server_description.to_unknown( + PyMongoError( + f"primary marked stale due to electionId/setVersion mismatch, {new_election_tuple} is stale compared to {max_election_tuple}" + ) + ) return _check_has_primary(sds), replica_set_name, max_set_version, max_election_id else: max_election_id = server_description.election_id @@ -591,7 +599,9 @@ def _update_rs_from_primary( and server.address != server_description.address ): # Reset old primary's type to Unknown. - sds[server.address] = server.to_unknown() + sds[server.address] = server.to_unknown( + PyMongoError("primary marked stale due to discovery of newer primary") + ) # There can be only one prior primary. break From 2057ae784fb67c26f2bbed8cfa140ec55c510c4e Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Tue, 4 Feb 2025 13:11:56 -0800 Subject: [PATCH 3/4] PYTHON-4960 Resync tests --- .../rs/new_primary_new_electionid.json | 2 +- .../rs/new_primary_new_setversion.json | 2 +- .../rs/primary_disconnect_electionid.json | 3 ++- .../rs/primary_disconnect_setversion.json | 3 ++- .../rs/setversion_greaterthan_max_without_electionid.json | 2 +- .../rs/setversion_without_electionid-pre-6.0.json | 2 +- .../rs/use_setversion_without_electionid-pre-6.0.json | 2 +- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/discovery_and_monitoring/rs/new_primary_new_electionid.json b/test/discovery_and_monitoring/rs/new_primary_new_electionid.json index ec6e736d55..90ef0ce8dc 100644 --- a/test/discovery_and_monitoring/rs/new_primary_new_electionid.json +++ b/test/discovery_and_monitoring/rs/new_primary_new_electionid.json @@ -77,7 +77,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/new_primary_new_setversion.json b/test/discovery_and_monitoring/rs/new_primary_new_setversion.json index 1db05cbd5a..9c1e2d4bdd 100644 --- a/test/discovery_and_monitoring/rs/new_primary_new_setversion.json +++ b/test/discovery_and_monitoring/rs/new_primary_new_setversion.json @@ -77,7 +77,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json b/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json index d87f7a3612..b030bd2c53 100644 --- a/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json +++ b/test/discovery_and_monitoring/rs/primary_disconnect_electionid.json @@ -49,7 +49,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -125,6 +125,7 @@ "a:27017": { "type": "Unknown", "setName": null, + "error": "primary marked stale due to electionId/setVersion mismatch", "electionId": null }, "b:27017": { diff --git a/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json b/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json index 0a59fd9ce6..653a5f29e8 100644 --- a/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json +++ b/test/discovery_and_monitoring/rs/primary_disconnect_setversion.json @@ -49,7 +49,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -125,6 +125,7 @@ "a:27017": { "type": "Unknown", "setName": null, + "error": "primary marked stale due to electionId/setVersion mismatch", "electionId": null }, "b:27017": { diff --git a/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json b/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json index e345165947..06c89609f5 100644 --- a/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json +++ b/test/discovery_and_monitoring/rs/setversion_greaterthan_max_without_electionid.json @@ -66,7 +66,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json b/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json index 6b6cd39255..87029e578b 100644 --- a/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json +++ b/test/discovery_and_monitoring/rs/setversion_without_electionid-pre-6.0.json @@ -66,7 +66,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json b/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json index 50c6666ee5..a63efeac12 100644 --- a/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json +++ b/test/discovery_and_monitoring/rs/use_setversion_without_electionid-pre-6.0.json @@ -74,7 +74,7 @@ "type": "Unknown", "setName": null, "electionId": null, - "error": "primary marked stale due to electionId/setVersion mismatch" + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", From d7229938ff4c455451866897ef50109f6bc42060 Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Tue, 4 Mar 2025 15:39:34 -0800 Subject: [PATCH 4/4] PYTHON-4960 Apply test changes to async --- test/asynchronous/test_discovery_and_monitoring.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/asynchronous/test_discovery_and_monitoring.py b/test/asynchronous/test_discovery_and_monitoring.py index 7c4095ebb8..c3c2bb1a6c 100644 --- a/test/asynchronous/test_discovery_and_monitoring.py +++ b/test/asynchronous/test_discovery_and_monitoring.py @@ -180,6 +180,9 @@ def check_outcome(self, topology, outcome): server_type_name(expected_server_type), server_type_name(actual_server_description.server_type), ) + expected_error = expected_server.get("error") + if expected_error: + self.assertIn(expected_error, str(actual_server_description.error)) self.assertEqual(expected_server.get("setName"), actual_server_description.replica_set_name)