diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md index 8222adec4c..88bedd0382 100644 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md @@ -226,7 +226,8 @@ Fields: field in the server's hello or legacy hello response, in the case that the server reports an address different from the address the client uses. -- (=) `error`: information about the last error related to this server. Default null. +- (=) `error`: information about the last error related to this server. Default null. MUST contain or be able to produce + a string describing the error. - `roundTripTime`: the duration of the hello or legacy hello call. Default null. @@ -485,7 +486,13 @@ removed once the primary is checked. #### error If the client experiences any error when checking a server, it stores error information in the ServerDescription's error -field. +field. The message contained in this field MUST contain the substrings detailed in the table below when the +ServerDescription is changed to Unknown in the circumstances outlined. + +| circumstance | error substring | +| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| RSPrimary with a stale electionId/setVersion is discovered | `'primary marked stale due to electionId/setVersion mismatch, is stale compared to '` | +| New primary is elected/discovered | `'primary marked stale due to discovery of newer primary'` | #### roundTripTime @@ -871,7 +878,8 @@ if serverDescription.maxWireVersion >= 17: # MongoDB 6.0+ topologyDescription.maxSetVersion = serverDescription.setVersion else: # Stale primary. - # replace serverDescription with a default ServerDescription of type "Unknown" + # The error field MUST include the substring "primary marked stale due to electionId/setVersion mismatch" + replace serverDescription with a default ServerDescription of type "Unknown" checkIfHasPrimary() return else: @@ -889,7 +897,8 @@ else: ) ): # Stale primary. - # replace serverDescription with a default ServerDescription of type "Unknown" + # The error field MUST include the substring "primary marked stale due to electionId/setVersion mismatch" + replace serverDescription with a default ServerDescription of type "Unknown" checkIfHasPrimary() return @@ -906,6 +915,7 @@ for each server in topologyDescription.servers: if server.address != serverDescription.address: if server.type is RSPrimary: # See note below about invalidating an old primary. + # the error field MUST include the substring "primary marked stale due to discovery of newer primary" replace the server with a default ServerDescription of type "Unknown" for each address in serverDescription's "hosts", "passives", and "arbiters": @@ -921,9 +931,10 @@ checkIfHasPrimary() ``` A note on invalidating the old primary: when a new primary is discovered, the client finds the previous primary (there -should be none or one) and replaces its description with a default ServerDescription of type "Unknown." A multi-threaded -client MUST [request an immediate check](server-monitoring.md#requesting-an-immediate-check) for that server as soon as -possible. +should be none or one) and replaces its description with a default ServerDescription of type "Unknown". Additionally, +the `error` field of the new `ServerDescription` object MUST include a descriptive error explaining that it was +invalidated because the primary was determined to be stale. A multi-threaded client MUST +[request an immediate check](server-monitoring.md#requesting-an-immediate-check) for that server as soon as possible. If the old primary server version is 4.0 or earlier, the client MUST clear its connection pool for the old primary, too: the connections are all bad because the old primary has closed its sockets. If the old primary server version is 4.2 or @@ -934,6 +945,8 @@ See [replica set monitoring with and without a primary](#replica-set-monitoring- If the server is primary with an obsolete electionId or setVersion, it is likely a stale primary that is going to step down. Mark it Unknown and let periodic monitoring detect when it becomes secondary. See [using electionId and setVersion to detect stale primaries](#using-electionid-and-setversion-to-detect-stale-primaries). +Drivers MAY additionally specify whether this was due to an electionId or setVersion mismatch as described in the +[ServerDescripion.error section](#error). A note on checking "me": Unlike `updateRSWithPrimaryFromMember`, there is no need to remove the server if the address is not equal to "me": since the server address will not be a member of either "hosts", "passives", or "arbiters", the @@ -1921,16 +1934,6 @@ oversaw the specification process. ## Changelog -- 2024-11-11: Removed references to `getLastError` - -- 2024-11-04: Make the description of `TopologyDescription.servers` consistent with the spec tests. - -- 2024-08-16: Updated host b wire versions in `too_new` and `too_old` tests - -- 2024-08-09: Updated wire versions in tests to 4.0+. - -- 2024-05-08: Migrated from reStructuredText to Markdown. - - 2015-12-17: Require clients to compare (setVersion, electionId) tuples. - 2015-10-09: Specify electionID comparison method. @@ -2012,6 +2015,19 @@ oversaw the specification process. - 2024-01-17: Add section on expected client close behaviour +- 2024-05-08: Migrated from reStructuredText to Markdown. + +- 2024-08-09: Updated wire versions in tests to 4.0+. + +- 2024-08-16: Updated host b wire versions in `too_new` and `too_old` tests + +- 2024-11-04: Make the description of `TopologyDescription.servers` consistent with the spec tests. + +- 2024-11-11: Removed references to `getLastError` + +- 2025-01-22: Add error messages when a new primary is elected or a primary with a stale electionId or setVersion is + discovered. + ______________________________________________________________________ [^1]: "localThresholdMS" was called "secondaryAcceptableLatencyMS" in the Read Preferences Spec, before it was superseded diff --git a/source/server-discovery-and-monitoring/tests/README.md b/source/server-discovery-and-monitoring/tests/README.md index 8c94ccd3cc..e747fee538 100644 --- a/source/server-discovery-and-monitoring/tests/README.md +++ b/source/server-discovery-and-monitoring/tests/README.md @@ -71,6 +71,7 @@ following keys: - type: A ServerType name, like "RSSecondary". See [ServerType](../server-discovery-and-monitoring.md#servertype) for details pertaining to async and multi-threaded drivers. +- error: An optional string that must be a substring of the message on the `ServerDescription.error` object - setName: A string with the expected replica set name, or null. - setVersion: absent or an integer. - electionId: absent, null, or an ObjectId. diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary.json b/source/server-discovery-and-monitoring/tests/rs/new_primary.json index 1a84c69c91..69b07516b9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary.json +++ b/source/server-discovery-and-monitoring/tests/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/source/server-discovery-and-monitoring/tests/rs/new_primary.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary.yml index f2485a1863..50c996f52c 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary.yml @@ -63,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", - setName: + setName:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json index 509720d445..90ef0ce8dc 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "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/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml index 5641cfda95..6418301c08 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml @@ -63,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId: , + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -100,7 +101,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { type: "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json index 96533c61ee..9c1e2d4bdd 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "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/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml index f269797112..7abf69a8c0 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml @@ -63,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -100,7 +101,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { type: "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json index 5a91188ea8..b030bd2c53 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -124,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/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml index 391ec31213..4ee8612019 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml @@ -36,7 +36,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -99,6 +100,7 @@ phases: [ "a:27017": { type: "Unknown", setName: , + error: "primary marked stale due to electionId/setVersion mismatch", electionId: }, "b:27017": { diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json index f7417ad77b..653a5f29e8 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -124,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/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml index 57eeb573e4..bc6c538e93 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml @@ -36,7 +36,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -99,6 +100,7 @@ phases: [ "a:27017": { type: "Unknown", setName: , + error: "primary marked stale due to electionId/setVersion mismatch", electionId: }, "b:27017": { diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json index 97870d71d5..06c89609f5 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml index 3252e0f611..622597809e 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml @@ -61,7 +61,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json index e62c6963ed..87029e578b 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml index 0fe6819aa7..0fd735dcc5 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml @@ -61,7 +61,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json index 2f9b567b85..a63efeac12 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json +++ b/source/server-discovery-and-monitoring/tests/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 discovery of newer primary" }, "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/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml index 24d6accbe0..d02fba5d52 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml @@ -62,7 +62,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -99,7 +100,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { type: "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json index 551f3e12c2..eaf586d728 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json +++ b/source/server-discovery-and-monitoring/tests/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/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml index 68c88bc503..5359a1f67b 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml @@ -68,7 +68,8 @@ phases: [ "b:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" } }, topologyType: "ReplicaSetWithPrimary", @@ -106,7 +107,8 @@ phases: [ "b:27017":{ type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" } }, topologyType: "ReplicaSetWithPrimary",