Skip to content

Commit 37613f1

Browse files
feat(NODE-6605): add error message when invalidating primary (#4340)
Co-authored-by: Bailey Pearson <[email protected]>
1 parent ea8a33f commit 37613f1

23 files changed

+187
-101
lines changed

src/error.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import type { Document } from './bson';
1+
import type { Document, ObjectId } from './bson';
22
import {
33
type ClientBulkWriteError,
44
type ClientBulkWriteResult
55
} from './operations/client_bulk_write/common';
66
import type { ServerType } from './sdam/common';
7-
import type { TopologyVersion } from './sdam/server_description';
7+
import type { ServerDescription, TopologyVersion } from './sdam/server_description';
88
import type { TopologyDescription } from './sdam/topology_description';
99

1010
/** @public */
@@ -340,6 +340,41 @@ export class MongoRuntimeError extends MongoDriverError {
340340
}
341341
}
342342

343+
/**
344+
* An error generated when a primary server is marked stale, never directly thrown
345+
*
346+
* @public
347+
* @category Error
348+
*/
349+
export class MongoStalePrimaryError extends MongoRuntimeError {
350+
/**
351+
* **Do not use this constructor!**
352+
*
353+
* Meant for internal use only.
354+
*
355+
* @remarks
356+
* This class is only meant to be constructed within the driver. This constructor is
357+
* not subject to semantic versioning compatibility guarantees and may change at any time.
358+
*
359+
* @public
360+
**/
361+
constructor(
362+
serverDescription: ServerDescription,
363+
maxSetVersion: number | null,
364+
maxElectionId: ObjectId | null,
365+
options?: { cause?: Error }
366+
) {
367+
super(
368+
`primary marked stale due to electionId/setVersion mismatch: server setVersion: ${serverDescription.setVersion}, server electionId: ${serverDescription.electionId}, topology setVersion: ${maxSetVersion}, topology electionId: ${maxElectionId}`,
369+
options
370+
);
371+
}
372+
373+
override get name(): string {
374+
return 'MongoStalePrimaryError';
375+
}
376+
}
377+
343378
/**
344379
* An error generated when a batch command is re-executed after one of the commands in the batch
345380
* has failed

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export {
7777
MongoServerClosedError,
7878
MongoServerError,
7979
MongoServerSelectionError,
80+
MongoStalePrimaryError,
8081
MongoSystemError,
8182
MongoTailableCursorError,
8283
MongoTopologyClosedError,

src/sdam/server_description.ts

+3
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ export class ServerDescription {
112112
this.minRoundTripTime = options?.minRoundTripTime ?? 0;
113113
this.lastUpdateTime = now();
114114
this.lastWriteDate = hello?.lastWrite?.lastWriteDate ?? 0;
115+
// NOTE: This actually builds the stack string instead of holding onto the getter and all its
116+
// associated references. This is done to prevent a memory leak.
115117
this.error = options.error ?? null;
118+
this.error?.stack;
116119
// TODO(NODE-2674): Preserve int64 sent from MongoDB
117120
this.topologyVersion = this.error?.topologyVersion ?? hello?.topologyVersion ?? null;
118121
this.setName = hello?.setName ?? null;

src/sdam/topology_description.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { EJSON, type ObjectId } from '../bson';
22
import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
3-
import { type MongoError, MongoRuntimeError } from '../error';
3+
import { type MongoError, MongoRuntimeError, MongoStalePrimaryError } from '../error';
44
import { compareObjectId, shuffle } from '../utils';
55
import { ServerType, TopologyType } from './common';
66
import { ServerDescription } from './server_description';
@@ -400,7 +400,9 @@ function updateRsFromPrimary(
400400
// replace serverDescription with a default ServerDescription of type "Unknown"
401401
serverDescriptions.set(
402402
serverDescription.address,
403-
new ServerDescription(serverDescription.address)
403+
new ServerDescription(serverDescription.address, undefined, {
404+
error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
405+
})
404406
);
405407

406408
return [checkHasPrimary(serverDescriptions), setName, maxSetVersion, maxElectionId];
@@ -416,7 +418,9 @@ function updateRsFromPrimary(
416418
// this primary is stale, we must remove it
417419
serverDescriptions.set(
418420
serverDescription.address,
419-
new ServerDescription(serverDescription.address)
421+
new ServerDescription(serverDescription.address, undefined, {
422+
error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
423+
})
420424
);
421425

422426
return [checkHasPrimary(serverDescriptions), setName, maxSetVersion, maxElectionId];
@@ -438,7 +442,12 @@ function updateRsFromPrimary(
438442
for (const [address, server] of serverDescriptions) {
439443
if (server.type === ServerType.RSPrimary && server.address !== serverDescription.address) {
440444
// Reset old primary's type to Unknown.
441-
serverDescriptions.set(address, new ServerDescription(server.address));
445+
serverDescriptions.set(
446+
address,
447+
new ServerDescription(server.address, undefined, {
448+
error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
449+
})
450+
);
442451

443452
// There can only be one primary
444453
break;

test/spec/server-discovery-and-monitoring/rs/new_primary_new_electionid.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
"a:27017": {
7777
"type": "Unknown",
7878
"setName": null,
79-
"electionId": null
79+
"electionId": null,
80+
"error": "primary marked stale due to electionId/setVersion mismatch"
8081
},
8182
"b:27017": {
8283
"type": "RSPrimary",
@@ -123,7 +124,8 @@
123124
"a:27017": {
124125
"type": "Unknown",
125126
"setName": null,
126-
"electionId": null
127+
"electionId": null,
128+
"error": "primary marked stale due to electionId/setVersion mismatch"
127129
},
128130
"b:27017": {
129131
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/new_primary_new_electionid.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ phases: [
6363
"a:27017": {
6464
type: "Unknown",
6565
setName: ,
66-
electionId:
66+
electionId: ,
67+
error: "primary marked stale due to electionId/setVersion mismatch"
6768
},
6869
"b:27017": {
6970
type: "RSPrimary",
@@ -100,7 +101,8 @@ phases: [
100101
"a:27017": {
101102
type: "Unknown",
102103
setName: ,
103-
electionId:
104+
electionId:,
105+
error: "primary marked stale due to electionId/setVersion mismatch"
104106
},
105107
"b:27017": {
106108
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/new_primary_new_setversion.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
"a:27017": {
7777
"type": "Unknown",
7878
"setName": null,
79-
"electionId": null
79+
"electionId": null,
80+
"error": "primary marked stale due to electionId/setVersion mismatch"
8081
},
8182
"b:27017": {
8283
"type": "RSPrimary",
@@ -123,7 +124,8 @@
123124
"a:27017": {
124125
"type": "Unknown",
125126
"setName": null,
126-
"electionId": null
127+
"electionId": null,
128+
"error": "primary marked stale due to electionId/setVersion mismatch"
127129
},
128130
"b:27017": {
129131
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/new_primary_new_setversion.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ phases: [
6363
"a:27017": {
6464
type: "Unknown",
6565
setName: ,
66-
electionId:
66+
electionId:,
67+
error: "primary marked stale due to electionId/setVersion mismatch"
6768
},
6869
"b:27017": {
6970
type: "RSPrimary",
@@ -100,7 +101,8 @@ phases: [
100101
"a:27017": {
101102
type: "Unknown",
102103
setName: ,
103-
electionId:
104+
electionId:,
105+
error: "primary marked stale due to electionId/setVersion mismatch"
104106
},
105107
"b:27017": {
106108
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/null_election_id-pre-6.0.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"$oid": "000000000000000000000002"
6767
},
6868
"minWireVersion": 0,
69-
"maxWireVersion": 21
69+
"maxWireVersion": 7
7070
}
7171
]
7272
],

test/spec/server-discovery-and-monitoring/rs/primary_disconnect_electionid.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"a:27017": {
4949
"type": "Unknown",
5050
"setName": null,
51-
"electionId": null
51+
"electionId": null,
52+
"error": "primary marked stale due to electionId/setVersion mismatch"
5253
},
5354
"b:27017": {
5455
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/primary_disconnect_electionid.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ phases: [
3636
"a:27017": {
3737
type: "Unknown",
3838
setName: ,
39-
electionId:
39+
electionId:,
40+
error: "primary marked stale due to electionId/setVersion mismatch"
4041
},
4142
"b:27017": {
4243
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/primary_disconnect_setversion.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"a:27017": {
4949
"type": "Unknown",
5050
"setName": null,
51-
"electionId": null
51+
"electionId": null,
52+
"error": "primary marked stale due to electionId/setVersion mismatch"
5253
},
5354
"b:27017": {
5455
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/primary_disconnect_setversion.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ phases: [
3636
"a:27017": {
3737
type: "Unknown",
3838
setName: ,
39-
electionId:
39+
electionId:,
40+
error: "primary marked stale due to electionId/setVersion mismatch"
4041
},
4142
"b:27017": {
4243
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/setversion_greaterthan_max_without_electionid.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
"a:27017": {
6666
"type": "Unknown",
6767
"setName": null,
68-
"electionId": null
68+
"electionId": null,
69+
"error": "primary marked stale due to electionId/setVersion mismatch"
6970
},
7071
"b:27017": {
7172
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/setversion_greaterthan_max_without_electionid.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ phases: [
6161
"a:27017": {
6262
type: "Unknown",
6363
setName: ,
64-
electionId:
64+
electionId:,
65+
error: "primary marked stale due to electionId/setVersion mismatch"
6566
},
6667
"b:27017": {
6768
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/setversion_without_electionid-pre-6.0.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
"a:27017": {
6666
"type": "Unknown",
6767
"setName": null,
68-
"electionId": null
68+
"electionId": null,
69+
"error": "primary marked stale due to electionId/setVersion mismatch"
6970
},
7071
"b:27017": {
7172
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/setversion_without_electionid-pre-6.0.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ phases: [
6161
"a:27017": {
6262
type: "Unknown",
6363
setName: ,
64-
electionId:
64+
electionId:,
65+
error: "primary marked stale due to electionId/setVersion mismatch"
6566
},
6667
"b:27017": {
6768
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/use_setversion_without_electionid-pre-6.0.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@
7373
"a:27017": {
7474
"type": "Unknown",
7575
"setName": null,
76-
"electionId": null
76+
"electionId": null,
77+
"error": "primary marked stale due to electionId/setVersion mismatch"
7778
},
7879
"b:27017": {
7980
"type": "RSPrimary",
@@ -117,7 +118,8 @@
117118
"a:27017": {
118119
"type": "Unknown",
119120
"setName": null,
120-
"electionId": null
121+
"electionId": null,
122+
"error": "primary marked stale due to electionId/setVersion mismatch"
121123
},
122124
"b:27017": {
123125
"type": "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/use_setversion_without_electionid-pre-6.0.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ phases: [
6262
"a:27017": {
6363
type: "Unknown",
6464
setName: ,
65-
electionId:
65+
electionId:,
66+
error: "primary marked stale due to electionId/setVersion mismatch"
6667
},
6768
"b:27017": {
6869
type: "RSPrimary",
@@ -99,7 +100,8 @@ phases: [
99100
"a:27017": {
100101
type: "Unknown",
101102
setName: ,
102-
electionId:
103+
electionId:,
104+
error: "primary marked stale due to electionId/setVersion mismatch"
103105
},
104106
"b:27017": {
105107
type: "RSPrimary",

test/spec/server-discovery-and-monitoring/rs/use_setversion_without_electionid.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
"b:27017": {
8282
"type": "Unknown",
8383
"setName": null,
84-
"electionId": null
84+
"electionId": null,
85+
"error": "primary marked stale due to electionId/setVersion mismatch"
8586
}
8687
},
8788
"topologyType": "ReplicaSetWithPrimary",
@@ -128,7 +129,8 @@
128129
"b:27017": {
129130
"type": "Unknown",
130131
"setName": null,
131-
"electionId": null
132+
"electionId": null,
133+
"error": "primary marked stale due to electionId/setVersion mismatch"
132134
}
133135
},
134136
"topologyType": "ReplicaSetWithPrimary",

test/spec/server-discovery-and-monitoring/rs/use_setversion_without_electionid.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ phases: [
6868
"b:27017": {
6969
type: "Unknown",
7070
setName: ,
71-
electionId:
71+
electionId:,
72+
error: "primary marked stale due to electionId/setVersion mismatch"
7273
}
7374
},
7475
topologyType: "ReplicaSetWithPrimary",
@@ -106,7 +107,8 @@ phases: [
106107
"b:27017":{
107108
type: "Unknown",
108109
setName: ,
109-
electionId:
110+
electionId:,
111+
error: "primary marked stale due to electionId/setVersion mismatch"
110112
}
111113
},
112114
topologyType: "ReplicaSetWithPrimary",

0 commit comments

Comments
 (0)