1- import { ObjectId } from '../bson' ;
1+ import type { Document , ObjectId } from '../bson' ;
22import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants' ;
33import { MongoError , MongoRuntimeError } from '../error' ;
44import { shuffle } from '../utils' ;
@@ -33,6 +33,8 @@ export interface TopologyDescriptionOptions {
3333export class TopologyDescription {
3434 type : TopologyType ;
3535 setName ?: string ;
36+ maxSetVersion ?: number ;
37+ maxElectionId ?: ObjectId ;
3638 servers : Map < string , ServerDescription > ;
3739 stale : boolean ;
3840 compatible : boolean ;
@@ -42,28 +44,23 @@ export class TopologyDescription {
4244 localThresholdMS : number ;
4345 commonWireVersion ?: number ;
4446
45- maxElectionIdSetVersionPair : ElectionIdSetVersionPair ;
46-
47- get maxElectionId ( ) : ObjectId | undefined {
48- return this . maxElectionIdSetVersionPair . electionId ;
49- }
50- get maxSetVersion ( ) : number | undefined {
51- return this . maxElectionIdSetVersionPair . setVersion ;
52- }
53-
5447 /**
5548 * Create a TopologyDescription
5649 */
5750 constructor (
5851 topologyType : TopologyType ,
5952 serverDescriptions ?: Map < string , ServerDescription > ,
6053 setName ?: string ,
61- maxElectionIdSetVersionPair ?: ElectionIdSetVersionPair ,
54+ maxSetVersion ?: number ,
55+ maxElectionId ?: ObjectId ,
6256 commonWireVersion ?: number ,
6357 options ?: TopologyDescriptionOptions
6458 ) {
6559 options = options ?? { } ;
6660
61+ // TODO: consider assigning all these values to a temporary value `s` which
62+ // we use `Object.freeze` on, ensuring the internal state of this type
63+ // is immutable.
6764 this . type = topologyType ?? TopologyType . Unknown ;
6865 this . servers = serverDescriptions ?? new Map ( ) ;
6966 this . stale = false ;
@@ -75,10 +72,13 @@ export class TopologyDescription {
7572 this . setName = setName ;
7673 }
7774
78- this . maxElectionIdSetVersionPair = {
79- electionId : maxElectionIdSetVersionPair ?. electionId ,
80- setVersion : maxElectionIdSetVersionPair ?. setVersion
81- } ;
75+ if ( maxSetVersion ) {
76+ this . maxSetVersion = maxSetVersion ;
77+ }
78+
79+ if ( maxElectionId ) {
80+ this . maxElectionId = maxElectionId ;
81+ }
8282
8383 if ( commonWireVersion ) {
8484 this . commonWireVersion = commonWireVersion ;
@@ -134,8 +134,6 @@ export class TopologyDescription {
134134 ) ;
135135 }
136136 }
137-
138- Object . freeze ( this ) ;
139137 }
140138
141139 /**
@@ -188,7 +186,8 @@ export class TopologyDescription {
188186 this . type ,
189187 serverDescriptions ,
190188 this . setName ,
191- this . maxElectionIdSetVersionPair ,
189+ this . maxSetVersion ,
190+ this . maxElectionId ,
192191 this . commonWireVersion ,
193192 { heartbeatFrequencyMS : this . heartbeatFrequencyMS , localThresholdMS : this . localThresholdMS }
194193 ) ;
@@ -202,7 +201,7 @@ export class TopologyDescription {
202201 const address = serverDescription . address ;
203202
204203 // potentially mutated values
205- let { type : topologyType , setName, maxElectionIdSetVersionPair , commonWireVersion } = this ;
204+ let { type : topologyType , setName, maxSetVersion , maxElectionId , commonWireVersion } = this ;
206205
207206 if ( serverDescription . setName && setName && serverDescription . setName !== setName ) {
208207 serverDescription = new ServerDescription ( address , undefined ) ;
@@ -229,7 +228,8 @@ export class TopologyDescription {
229228 TopologyType . Single ,
230229 serverDescriptions ,
231230 setName ,
232- maxElectionIdSetVersionPair ,
231+ maxSetVersion ,
232+ maxElectionId ,
233233 commonWireVersion ,
234234 { heartbeatFrequencyMS : this . heartbeatFrequencyMS , localThresholdMS : this . localThresholdMS }
235235 ) ;
@@ -258,13 +258,15 @@ export class TopologyDescription {
258258 const result = updateRsFromPrimary (
259259 serverDescriptions ,
260260 serverDescription ,
261- maxElectionIdSetVersionPair ,
262- setName
261+ setName ,
262+ maxSetVersion ,
263+ maxElectionId
263264 ) ;
264265
265- topologyType = result . topologyType ;
266- setName = result . setName ;
267- maxElectionIdSetVersionPair = result . electionIdSetVersionPair ;
266+ topologyType = result [ 0 ] ;
267+ setName = result [ 1 ] ;
268+ maxSetVersion = result [ 2 ] ;
269+ maxElectionId = result [ 3 ] ;
268270 } else if ( NON_PRIMARY_RS_MEMBERS . has ( serverType ) ) {
269271 const result = updateRsNoPrimaryFromMember ( serverDescriptions , serverDescription , setName ) ;
270272 topologyType = result [ 0 ] ;
@@ -280,13 +282,15 @@ export class TopologyDescription {
280282 const result = updateRsFromPrimary (
281283 serverDescriptions ,
282284 serverDescription ,
283- maxElectionIdSetVersionPair ,
284- setName
285+ setName ,
286+ maxSetVersion ,
287+ maxElectionId
285288 ) ;
286289
287- topologyType = result . topologyType ;
288- setName = result . setName ;
289- maxElectionIdSetVersionPair = result . electionIdSetVersionPair ;
290+ topologyType = result [ 0 ] ;
291+ setName = result [ 1 ] ;
292+ maxSetVersion = result [ 2 ] ;
293+ maxElectionId = result [ 3 ] ;
290294 } else if ( NON_PRIMARY_RS_MEMBERS . has ( serverType ) ) {
291295 topologyType = updateRsWithPrimaryFromMember (
292296 serverDescriptions ,
@@ -302,7 +306,8 @@ export class TopologyDescription {
302306 topologyType ,
303307 serverDescriptions ,
304308 setName ,
305- maxElectionIdSetVersionPair ,
309+ maxSetVersion ,
310+ maxElectionId ,
306311 commonWireVersion ,
307312 { heartbeatFrequencyMS : this . heartbeatFrequencyMS , localThresholdMS : this . localThresholdMS }
308313 ) ;
@@ -359,54 +364,65 @@ function topologyTypeForServerType(serverType: ServerType): TopologyType {
359364 }
360365}
361366
362- function compareObjectId ( oid1 : ObjectId , oid2 : ObjectId ) : 0 | 1 | - 1 {
363- const res = oid1 . id . compare ( oid2 . id ) ;
364- return res === 0 ? 0 : res > 0 ? 1 : - 1 ;
367+ // TODO: improve these docs when ObjectId is properly typed
368+ function compareObjectId ( oid1 : Document , oid2 : Document ) : number {
369+ if ( oid1 == null ) {
370+ return - 1 ;
371+ }
372+
373+ if ( oid2 == null ) {
374+ return 1 ;
375+ }
376+
377+ if ( oid1 . id instanceof Buffer && oid2 . id instanceof Buffer ) {
378+ const oid1Buffer = oid1 . id ;
379+ const oid2Buffer = oid2 . id ;
380+ return oid1Buffer . compare ( oid2Buffer ) ;
381+ }
382+
383+ const oid1String = oid1 . toString ( ) ;
384+ const oid2String = oid2 . toString ( ) ;
385+ return oid1String . localeCompare ( oid2String ) ;
365386}
366387
367388function updateRsFromPrimary (
368389 serverDescriptions : Map < string , ServerDescription > ,
369390 serverDescription : ServerDescription ,
370- maxElectionIdSetVersionPair : ElectionIdSetVersionPair ,
371- setName ?: string
372- ) : {
373- topologyType : TopologyType ;
374- setName ?: string ;
375- electionIdSetVersionPair : ElectionIdSetVersionPair ;
376- } {
391+ setName ?: string ,
392+ maxSetVersion ?: number ,
393+ maxElectionId ?: ObjectId
394+ ) : [ TopologyType , string ?, number ?, ObjectId ?] {
377395 setName = setName || serverDescription . setName ;
378396 if ( setName !== serverDescription . setName ) {
379397 serverDescriptions . delete ( serverDescription . address ) ;
380- return {
381- topologyType : checkHasPrimary ( serverDescriptions ) ,
382- setName,
383- electionIdSetVersionPair : maxElectionIdSetVersionPair
384- } ;
398+ return [ checkHasPrimary ( serverDescriptions ) , setName , maxSetVersion , maxElectionId ] ;
385399 }
386400
387- const incomingElectionIdSetVersionPair = {
388- electionId : serverDescription . electionId ,
389- setVersion : serverDescription . setVersion
390- } ;
391-
392- const newMaxElectionIdSetVersionPair = greaterElectionIdSetVersionPair (
393- maxElectionIdSetVersionPair ,
394- incomingElectionIdSetVersionPair
395- ) ;
396-
397- if ( newMaxElectionIdSetVersionPair !== maxElectionIdSetVersionPair ) {
398- // The primary we know about has become stale
399- // make new ServerDescription for that address
400- // this makes it unknown
401- serverDescriptions . set (
402- serverDescription . address ,
403- new ServerDescription ( serverDescription . address )
404- ) ;
405- return {
406- topologyType : checkHasPrimary ( serverDescriptions ) ,
407- setName,
408- electionIdSetVersionPair : newMaxElectionIdSetVersionPair
409- } ;
401+ const electionId = serverDescription . electionId ? serverDescription . electionId : null ;
402+ if ( serverDescription . setVersion && electionId ) {
403+ if ( maxSetVersion && maxElectionId ) {
404+ if (
405+ maxSetVersion > serverDescription . setVersion ||
406+ compareObjectId ( maxElectionId , electionId ) > 0
407+ ) {
408+ // this primary is stale, we must remove it
409+ serverDescriptions . set (
410+ serverDescription . address ,
411+ new ServerDescription ( serverDescription . address )
412+ ) ;
413+
414+ return [ checkHasPrimary ( serverDescriptions ) , setName , maxSetVersion , maxElectionId ] ;
415+ }
416+ }
417+
418+ maxElectionId = serverDescription . electionId ;
419+ }
420+
421+ if (
422+ serverDescription . setVersion != null &&
423+ ( maxSetVersion == null || serverDescription . setVersion > maxSetVersion )
424+ ) {
425+ maxSetVersion = serverDescription . setVersion ;
410426 }
411427
412428 // We've heard from the primary. Is it the same primary as before?
@@ -436,11 +452,7 @@ function updateRsFromPrimary(
436452 serverDescriptions . delete ( address ) ;
437453 } ) ;
438454
439- return {
440- topologyType : checkHasPrimary ( serverDescriptions ) ,
441- setName,
442- electionIdSetVersionPair : newMaxElectionIdSetVersionPair
443- } ;
455+ return [ checkHasPrimary ( serverDescriptions ) , setName , maxSetVersion , maxElectionId ] ;
444456}
445457
446458function updateRsWithPrimaryFromMember (
@@ -497,31 +509,3 @@ function checkHasPrimary(serverDescriptions: Map<string, ServerDescription>): To
497509
498510 return TopologyType . ReplicaSetNoPrimary ;
499511}
500-
501- interface ElectionIdSetVersionPair {
502- electionId ?: ObjectId ;
503- setVersion ?: number ;
504- }
505-
506- const OID_ZERO = new ObjectId ( '00' . repeat ( 12 ) ) ;
507-
508- function greaterElectionIdSetVersionPair (
509- a : ElectionIdSetVersionPair ,
510- b : ElectionIdSetVersionPair
511- ) : ElectionIdSetVersionPair {
512- if ( compareObjectId ( a . electionId ?? OID_ZERO , b . electionId ?? OID_ZERO ) === 0 ) {
513- // ObjectIds are equal so we have to check setVersion
514- if ( ( a . setVersion ?? - 1 ) >= ( b . setVersion ?? - 1 ) ) {
515- if ( a . electionId == null && a . setVersion == null ) {
516- return a ;
517- }
518- return a ;
519- } else {
520- return b ;
521- }
522- } else if ( compareObjectId ( a . electionId ?? OID_ZERO , b . electionId ?? OID_ZERO ) === 1 ) {
523- return a ;
524- } else {
525- return b ;
526- }
527- }
0 commit comments