This repository was archived by the owner on Feb 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 106
NODE-1259 Refactor mongodb-core to use a single Topology type (part 1) #303
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
338e4c1
feat(topology): introduce a single Topology type, and test runner
mbroadst e029583
refactor(sdam): track logicalSessionTimeoutMinutes from ismasters
mbroadst 6948e33
test(sdam): skip monitoring tests for the moment
mbroadst ac80267
chore(sdam): create sdam subfolder, split topology up
mbroadst 80ad079
feat(sdam-monitoring): add basic monitoring for new Topology type
mbroadst fe1be0d
fix(topology-description): we can't use Object.values yet
mbroadst a7d0e64
fix(sdam): we can't use Array.includes yet
mbroadst f5e5590
refactor(topology-description): use Maps for server descriptions
mbroadst f2f08f2
refactor(calculateDurationMs): move this method to common utils
mbroadst f01e4c8
feat(MongoTimeoutError): add common class for timeout events
mbroadst a31dfd1
refactor(read-preference): simplify arguments management
mbroadst 92d1c30
feat(server-selection): add basic support for server selection
mbroadst 22f87f5
test(server-selection): add server selection test runner
mbroadst ca3b831
refactor(latency-window): ensure use of lowest bound for minimum
mbroadst 0db8da2
test(latency-window): modify selection tests to verify latency
mbroadst cc2e369
refactor(server-description): add `lastUpdateTime`, fix spelling
mbroadst 7438829
refactor(topology-description): add getter for common wire version
mbroadst 73157a6
test(max-staleness): reuse server selection test runner
mbroadst c4aec15
feat(max-staleness): properly support a max staleness reducer
mbroadst bb619f0
refactor(read-preference): support legacy non-array for tags
mbroadst ff2dcf0
test(max-staleness): correct old test runners for redpref changes
mbroadst 6f07c7c
refactor(topology): add stubs for all base topology methods
mbroadst aa3642f
feat(topology-description): add helper method for server ownership
mbroadst 8fe2bf7
refactor(topology): add Server type, add serverClosed events
mbroadst 4da938c
test(sdam): reenable removal test, testing `serverClosed` events
mbroadst 7995834
chore(sdam): `Server` skeleton takes a description for testing
mbroadst 135de8c
refactor(server-description): simplify ismaster parsing
mbroadst c00ffc0
test(sdam): accommodate for and verify omitted fields
mbroadst 198f43f
refactor(servrer-selectors): simplify nested ternary statement
mbroadst fb5d72a
refactor(topology): simplify default options, type detection
mbroadst 31eb08a
refactor(topology-description): remove assert, dedupe if branches
mbroadst 9653cb1
test(server-selection): `var` => `let`
mbroadst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
'use strict'; | ||
|
||
/** | ||
* Published when server description changes, but does NOT include changes to the RTT. | ||
* | ||
* @property {Object} topologyId A unique identifier for the topology | ||
* @property {ServerAddress} address The address (host/port pair) of the server | ||
* @property {ServerDescription} previousDescription The previous server description | ||
* @property {ServerDescription} newDescription The new server description | ||
*/ | ||
class ServerDescriptionChangedEvent { | ||
constructor(topologyId, address, previousDescription, newDescription) { | ||
Object.assign(this, { topologyId, address, previousDescription, newDescription }); | ||
} | ||
} | ||
|
||
/** | ||
* Published when server is initialized. | ||
* | ||
* @property {Object} topologyId A unique identifier for the topology | ||
* @property {ServerAddress} address The address (host/port pair) of the server | ||
*/ | ||
class ServerOpeningEvent { | ||
constructor(topologyId, address) { | ||
Object.assign(this, { topologyId, address }); | ||
} | ||
} | ||
|
||
/** | ||
* Published when server is closed. | ||
* | ||
* @property {ServerAddress} address The address (host/port pair) of the server | ||
* @property {Object} topologyId A unique identifier for the topology | ||
*/ | ||
class ServerClosedEvent { | ||
constructor(topologyId, address) { | ||
Object.assign(this, { topologyId, address }); | ||
} | ||
} | ||
|
||
/** | ||
* Published when topology description changes. | ||
* | ||
* @property {Object} topologyId | ||
* @property {TopologyDescription} previousDescription The old topology description | ||
* @property {TopologyDescription} newDescription The new topology description | ||
*/ | ||
class TopologyDescriptionChangedEvent { | ||
constructor(topologyId, previousDescription, newDescription) { | ||
Object.assign(this, { topologyId, previousDescription, newDescription }); | ||
} | ||
} | ||
|
||
/** | ||
* Published when topology is initialized. | ||
* | ||
* @param {Object} topologyId A unique identifier for the topology | ||
*/ | ||
class TopologyOpeningEvent { | ||
constructor(topologyId) { | ||
Object.assign(this, { topologyId }); | ||
} | ||
} | ||
|
||
/** | ||
* Published when topology is closed. | ||
* | ||
* @param {Object} topologyId A unique identifier for the topology | ||
*/ | ||
class TopologyClosedEvent { | ||
constructor(topologyId) { | ||
Object.assign(this, { topologyId }); | ||
} | ||
} | ||
|
||
/** | ||
* Fired when the server monitor’s ismaster command is started - immediately before | ||
* the ismaster command is serialized into raw BSON and written to the socket. | ||
* | ||
* @property {Object} connectionId The connection id for the command | ||
*/ | ||
class ServerHeartbeatStartedEvent { | ||
constructor(connectionId) { | ||
Object.assign(this, { connectionId }); | ||
} | ||
} | ||
|
||
/** | ||
* Fired when the server monitor’s ismaster succeeds. | ||
* | ||
* @param {Number} duration The execution time of the event | ||
* @param {Object} reply The command reply | ||
* @param {Object} connectionId The connection id for the command | ||
*/ | ||
class ServerHeartbeatSucceededEvent { | ||
constructor(duration, reply, connectionId) { | ||
Object.assign(this, { duration, reply, connectionId }); | ||
} | ||
} | ||
|
||
/** | ||
* Fired when the server monitor’s ismaster fails, either with an “ok: 0” or a socket exception. | ||
* | ||
* @param {Number} duration The execution time of the event | ||
* @param {MongoError|Object} failure The command failure | ||
* @param {Object} connectionId The connection id for the command | ||
*/ | ||
class ServerHearbeatFailedEvent { | ||
constructor(duration, failure, connectionId) { | ||
Object.assign(this, { duration, failure, connectionId }); | ||
} | ||
} | ||
|
||
module.exports = { | ||
ServerDescriptionChangedEvent, | ||
ServerOpeningEvent, | ||
ServerClosedEvent, | ||
TopologyDescriptionChangedEvent, | ||
TopologyOpeningEvent, | ||
TopologyClosedEvent, | ||
ServerHeartbeatStartedEvent, | ||
ServerHeartbeatSucceededEvent, | ||
ServerHearbeatFailedEvent | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
const EventEmitter = require('events'); | ||
|
||
class Server extends EventEmitter { | ||
constructor(description) { | ||
super(); | ||
|
||
this.s = { | ||
description | ||
}; | ||
} | ||
|
||
get description() { | ||
return this.s.description; | ||
} | ||
|
||
/** | ||
* Initiate server connect | ||
* | ||
* @param {Array} [options.auth] Array of auth options to apply on connect | ||
*/ | ||
connect(options, callback) { | ||
options = options || {}; | ||
|
||
if (typeof callback === 'function') { | ||
callback(null, null); | ||
} | ||
} | ||
|
||
/** | ||
* Destroy the server connection | ||
* | ||
* @param {Boolean} [options.emitClose=false] Emit close event on destroy | ||
* @param {Boolean} [options.emitDestroy=false] Emit destroy event on destroy | ||
* @param {Boolean} [options.force=false] Force destroy the pool | ||
*/ | ||
destroy(callback) { | ||
if (typeof callback === 'function') { | ||
callback(null, null); | ||
} | ||
} | ||
} | ||
|
||
module.exports = Server; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
'use strict'; | ||
|
||
// An enumeration of server types we know about | ||
const ServerType = { | ||
Standalone: 'Standalone', | ||
Mongos: 'Mongos', | ||
PossiblePrimary: 'PossiblePrimary', | ||
RSPrimary: 'RSPrimary', | ||
RSSecondary: 'RSSecondary', | ||
RSArbiter: 'RSArbiter', | ||
RSOther: 'RSOther', | ||
RSGhost: 'RSGhost', | ||
Unknown: 'Unknown' | ||
}; | ||
|
||
const WRITABLE_SERVER_TYPES = new Set([ | ||
ServerType.RSPrimary, | ||
ServerType.Standalone, | ||
ServerType.Mongos | ||
]); | ||
|
||
const ISMASTER_FIELDS = [ | ||
'minWireVersion', | ||
'maxWireVersion', | ||
'me', | ||
'hosts', | ||
'passives', | ||
'arbiters', | ||
'tags', | ||
'setName', | ||
'setVersion', | ||
'electionId', | ||
'primary', | ||
'logicalSessionTimeoutMinutes' | ||
]; | ||
|
||
/** | ||
* The client's view of a single server, based on the most recent ismaster outcome. | ||
* | ||
* Internal type, not meant to be directly instantiated | ||
*/ | ||
class ServerDescription { | ||
/** | ||
* Create a ServerDescription | ||
* @param {String} address The address of the server | ||
* @param {Object} [ismaster] An optional ismaster response for this server | ||
* @param {Object} [options] Optional settings | ||
* @param {Number} [options.roundTripTime] The round trip time to ping this server (in ms) | ||
*/ | ||
constructor(address, ismaster, options) { | ||
options = options || {}; | ||
ismaster = Object.assign( | ||
{ | ||
minWireVersion: 0, | ||
maxWireVersion: 0, | ||
hosts: [], | ||
passives: [], | ||
arbiters: [], | ||
tags: [] | ||
}, | ||
ismaster | ||
); | ||
|
||
this.address = address; | ||
this.error = null; | ||
this.roundTripTime = options.roundTripTime || 0; | ||
this.lastUpdateTime = Date.now(); | ||
this.lastWriteDate = ismaster.lastWrite ? ismaster.lastWrite.lastWriteDate : null; | ||
this.opTime = ismaster.lastWrite ? ismaster.lastWrite.opTime : null; | ||
this.type = parseServerType(ismaster); | ||
|
||
// direct mappings | ||
ISMASTER_FIELDS.forEach(field => { | ||
if (typeof ismaster[field] !== 'undefined') this[field] = ismaster[field]; | ||
}); | ||
|
||
// normalize case for hosts | ||
this.hosts = this.hosts.map(host => host.toLowerCase()); | ||
this.passives = this.passives.map(host => host.toLowerCase()); | ||
this.arbiters = this.arbiters.map(host => host.toLowerCase()); | ||
} | ||
|
||
get allHosts() { | ||
return this.hosts.concat(this.arbiters).concat(this.passives); | ||
} | ||
|
||
/** | ||
* @return {Boolean} Is this server available for reads | ||
*/ | ||
get isReadable() { | ||
return this.type === ServerType.RSSecondary || this.isWritable; | ||
} | ||
|
||
/** | ||
* @return {Boolean} Is this server available for writes | ||
*/ | ||
get isWritable() { | ||
return WRITABLE_SERVER_TYPES.has(this.type); | ||
} | ||
} | ||
|
||
/** | ||
* Parses an `ismaster` message and determines the server type | ||
* | ||
* @param {Object} ismaster The `ismaster` message to parse | ||
* @return {ServerType} | ||
*/ | ||
function parseServerType(ismaster) { | ||
if (!ismaster || !ismaster.ok) { | ||
return ServerType.Unknown; | ||
} | ||
|
||
if (ismaster.isreplicaset) { | ||
return ServerType.RSGhost; | ||
} | ||
|
||
if (ismaster.msg && ismaster.msg === 'isdbgrid') { | ||
return ServerType.Mongos; | ||
} | ||
|
||
if (ismaster.setName) { | ||
if (ismaster.hidden) { | ||
return ServerType.RSOther; | ||
} else if (ismaster.ismaster) { | ||
return ServerType.RSPrimary; | ||
} else if (ismaster.secondary) { | ||
return ServerType.RSSecondary; | ||
} else if (ismaster.arbiterOnly) { | ||
return ServerType.RSArbiter; | ||
} else { | ||
return ServerType.RSOther; | ||
} | ||
} | ||
|
||
return ServerType.Standalone; | ||
} | ||
|
||
module.exports = { | ||
ServerDescription, | ||
ServerType | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be done with
class
andextends
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, but I was just following existing convention in the file. I'd prefer to convert them in a separate commit wholesale