Skip to content
This repository was archived by the owner on Feb 4, 2022. It is now read-only.

Commit bb0c522

Browse files
committed
feat(sdam-monitoring): add basic monitoring for new Topology type
This adds an incomplete, but sufficient amount of SDAM monitoring to the new Topology type in order to pass the SDAM monitoring YAML tests. NODE-1259
1 parent b6e12af commit bb0c522

File tree

5 files changed

+389
-20
lines changed

5 files changed

+389
-20
lines changed

lib/sdam/monitoring.js

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'use strict';
2+
3+
/**
4+
* Published when server description changes, but does NOT include changes to the RTT.
5+
*
6+
* @property {Object} topologyId A unique identifier for the topology
7+
* @property {ServerAddress} address The address (host/port pair) of the server
8+
* @property {ServerDescription} previousDescription The previous server description
9+
* @property {ServerDescription} newDescription The new server description
10+
*/
11+
class ServerDescriptionChangedEvent {
12+
constructor(topologyId, address, previousDescription, newDescription) {
13+
Object.assign(this, { topologyId, address, previousDescription, newDescription });
14+
}
15+
}
16+
17+
/**
18+
* Published when server is initialized.
19+
*
20+
* @property {Object} topologyId A unique identifier for the topology
21+
* @property {ServerAddress} address The address (host/port pair) of the server
22+
*/
23+
class ServerOpeningEvent {
24+
constructor(topologyId, address) {
25+
Object.assign(this, { topologyId, address });
26+
}
27+
}
28+
29+
/**
30+
* Published when server is closed.
31+
*
32+
* @property {ServerAddress} address The address (host/port pair) of the server
33+
* @property {Object} topologyId A unique identifier for the topology
34+
*/
35+
class ServerClosedEvent {
36+
constructor(topologyId, address) {
37+
Object.assign(this, { topologyId, address });
38+
}
39+
}
40+
41+
/**
42+
* Published when topology description changes.
43+
*
44+
* @property {Object} topologyId
45+
* @property {TopologyDescription} previousDescription The old topology description
46+
* @property {TopologyDescription} newDescription The new topology description
47+
*/
48+
class TopologyDescriptionChangedEvent {
49+
constructor(topologyId, previousDescription, newDescription) {
50+
Object.assign(this, { topologyId, previousDescription, newDescription });
51+
}
52+
}
53+
54+
/**
55+
* Published when topology is initialized.
56+
*
57+
* @param {Object} topologyId A unique identifier for the topology
58+
*/
59+
class TopologyOpeningEvent {
60+
constructor(topologyId) {
61+
Object.assign(this, { topologyId });
62+
}
63+
}
64+
65+
/**
66+
* Published when topology is closed.
67+
*
68+
* @param {Object} topologyId A unique identifier for the topology
69+
*/
70+
class TopologyClosedEvent {
71+
constructor(topologyId) {
72+
Object.assign(this, { topologyId });
73+
}
74+
}
75+
76+
/**
77+
* Fired when the server monitor’s ismaster command is started - immediately before
78+
* the ismaster command is serialized into raw BSON and written to the socket.
79+
*
80+
* @property {Object} connectionId The connection id for the command
81+
*/
82+
class ServerHeartbeatStartedEvent {
83+
constructor(connectionId) {
84+
Object.assign(this, { connectionId });
85+
}
86+
}
87+
88+
/**
89+
* Fired when the server monitor’s ismaster succeeds.
90+
*
91+
* @param {Number} duration The execution time of the event
92+
* @param {Object} reply The command reply
93+
* @param {Object} connectionId The connection id for the command
94+
*/
95+
class ServerHeartbeatSucceededEvent {
96+
constructor(duration, reply, connectionId) {
97+
Object.assign(this, { duration, reply, connectionId });
98+
}
99+
}
100+
101+
/**
102+
* Fired when the server monitor’s ismaster fails, either with an “ok: 0” or a socket exception.
103+
*
104+
* @param {Number} duration The execution time of the event
105+
* @param {MongoError|Object} failure The command failure
106+
* @param {Object} connectionId The connection id for the command
107+
*/
108+
class ServerHearbeatFailedEvent {
109+
constructor(duration, failure, connectionId) {
110+
Object.assign(this, { duration, failure, connectionId });
111+
}
112+
}
113+
114+
module.exports = {
115+
ServerDescriptionChangedEvent,
116+
ServerOpeningEvent,
117+
ServerClosedEvent,
118+
TopologyDescriptionChangedEvent,
119+
TopologyOpeningEvent,
120+
TopologyClosedEvent,
121+
ServerHeartbeatStartedEvent,
122+
ServerHeartbeatSucceededEvent,
123+
ServerHearbeatFailedEvent
124+
};

lib/sdam/topology.js

+137-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,23 @@ const EventEmitter = require('events');
33
const ServerDescription = require('./server_description').ServerDescription;
44
const TopologyDescription = require('./topology_description').TopologyDescription;
55
const TopologyType = require('./topology_description').TopologyType;
6+
const monitoring = require('./monitoring');
7+
8+
// Global state
9+
let globalTopologyCounter = 0;
610

711
/**
812
* A container of server instances representing a connection to a MongoDB topology.
13+
*
14+
* @fires Topology#serverOpening
15+
* @fires Topology#serverClosed
16+
* @fires Topology#serverDescriptionChanged
17+
* @fires Topology#topologyOpening
18+
* @fires Topology#topologyClosed
19+
* @fires Topology#topologyDescriptionChanged
20+
* @fires Topology#serverHeartbeatStarted
21+
* @fires Topology#serverHeartbeatSucceeded
22+
* @fires Topology#serverHeartbeatFailed
923
*/
1024
class Topology extends EventEmitter {
1125
/**
@@ -26,13 +40,16 @@ class Topology extends EventEmitter {
2640
? TopologyType.ReplicaSetNoPrimary
2741
: TopologyType.Unknown;
2842

43+
const topologyId = globalTopologyCounter++;
2944
const serverDescriptions = seedlist.reduce((result, seed) => {
3045
const address = seed.port ? `${seed.host}:${seed.port}` : `${seed.host}:27017`;
3146
result[address] = new ServerDescription(address);
3247
return result;
3348
}, {});
3449

3550
this.s = {
51+
// the id of this topology
52+
id: topologyId,
3653
// passed in options
3754
options: Object.assign({}, options),
3855
// initial seedlist of servers to connect to
@@ -58,11 +75,37 @@ class Topology extends EventEmitter {
5875

5976
/**
6077
* Initiate server connect
61-
* @method
62-
* @param {array} [options.auth=null] Array of auth options to apply on connect
78+
*
79+
* @param {Object} [options] Optional settings
80+
* @param {Array} [options.auth=null] Array of auth options to apply on connect
6381
*/
6482
connect(/* options */) {
65-
return;
83+
// emit SDAM monitoring events
84+
this.emit('topologyOpening', new monitoring.TopologyOpeningEvent(this.s.id));
85+
86+
// emit an event for the topology change
87+
this.emit(
88+
'topologyDescriptionChanged',
89+
new monitoring.TopologyDescriptionChangedEvent(
90+
this.s.id,
91+
new TopologyDescription(TopologyType.Unknown), // initial is always Unknown
92+
this.s.description
93+
)
94+
);
95+
96+
// emit ServerOpeningEvents for each server in our topology
97+
Object.keys(this.s.description.servers).forEach(serverAddress => {
98+
// publish an open event for each ServerDescription created
99+
this.emit('serverOpening', new monitoring.ServerOpeningEvent(this.s.id, serverAddress));
100+
});
101+
}
102+
103+
/**
104+
* Close this topology
105+
*/
106+
close() {
107+
// emit an event for close
108+
this.emit('topologyClosed', new monitoring.TopologyClosedEvent(this.s.id));
66109
}
67110

68111
/**
@@ -81,10 +124,99 @@ class Topology extends EventEmitter {
81124
* @param {object} serverDescription the server to update
82125
*/
83126
update(serverDescription) {
127+
// these will be used for monitoring events later
128+
const previousTopologyDescription = this.s.description;
129+
const previousServerDescription = this.s.description.servers[serverDescription.address];
130+
84131
// first update the TopologyDescription
85132
this.s.description = this.s.description.update(serverDescription);
133+
134+
// emit monitoring events for this change
135+
this.emit(
136+
'serverDescriptionChanged',
137+
new monitoring.ServerDescriptionChangedEvent(
138+
this.s.id,
139+
serverDescription.address,
140+
previousServerDescription,
141+
this.s.description.servers[serverDescription.address]
142+
)
143+
);
144+
145+
this.emit(
146+
'topologyDescriptionChanged',
147+
new monitoring.TopologyDescriptionChangedEvent(
148+
this.s.id,
149+
previousTopologyDescription,
150+
this.s.description
151+
)
152+
);
86153
}
87154
}
88155

89-
module.exports.Topology = Topology;
90-
module.exports.ServerDescription = ServerDescription;
156+
/**
157+
* A server opening SDAM monitoring event
158+
*
159+
* @event Topology#serverOpening
160+
* @type {ServerOpeningEvent}
161+
*/
162+
163+
/**
164+
* A server closed SDAM monitoring event
165+
*
166+
* @event Topology#serverClosed
167+
* @type {ServerClosedEvent}
168+
*/
169+
170+
/**
171+
* A server description SDAM change monitoring event
172+
*
173+
* @event Topology#serverDescriptionChanged
174+
* @type {ServerDescriptionChangedEvent}
175+
*/
176+
177+
/**
178+
* A topology open SDAM event
179+
*
180+
* @event Topology#topologyOpening
181+
* @type {TopologyOpeningEvent}
182+
*/
183+
184+
/**
185+
* A topology closed SDAM event
186+
*
187+
* @event Topology#topologyClosed
188+
* @type {TopologyClosedEvent}
189+
*/
190+
191+
/**
192+
* A topology structure SDAM change event
193+
*
194+
* @event Topology#topologyDescriptionChanged
195+
* @type {TopologyDescriptionChangedEvent}
196+
*/
197+
198+
/**
199+
* A topology serverHeartbeatStarted SDAM event
200+
*
201+
* @event Topology#serverHeartbeatStarted
202+
* @type {ServerHeartbeatStartedEvent}
203+
*/
204+
205+
/**
206+
* A topology serverHeartbeatFailed SDAM event
207+
*
208+
* @event Topology#serverHeartbeatFailed
209+
* @type {ServerHearbeatFailedEvent}
210+
*/
211+
212+
/**
213+
* A topology serverHeartbeatSucceeded SDAM change event
214+
*
215+
* @event Topology#serverHeartbeatSucceeded
216+
* @type {ServerHeartbeatSucceededEvent}
217+
*/
218+
219+
module.exports = {
220+
Topology,
221+
ServerDescription
222+
};

lib/sdam/topology_description.js

+22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const assert = require('assert');
33
const ServerType = require('./server_description').ServerType;
44
const ServerDescription = require('./server_description').ServerDescription;
5+
const ReadPreference = require('../topologies/read_preference');
56

67
// contstants related to compatability checks
78
const MIN_SUPPORTED_SERVER_VERSION = '2.6';
@@ -195,6 +196,27 @@ class TopologyDescription {
195196
{}
196197
);
197198
}
199+
200+
/**
201+
* Determines if the topology has a readable server available. See the table in the
202+
* following section for behaviour rules.
203+
*
204+
* @param {ReadPreference} [readPreference] An optional read preference for determining if a readable server is present
205+
* @return {Boolean} Whether there is a readable server in this topology
206+
*/
207+
hasReadableServer(/* readPreference */) {
208+
// To be implemented when server selection is implemented
209+
}
210+
211+
/**
212+
* Determines if the topology has a writable server available. See the table in the
213+
* following section for behaviour rules.
214+
*
215+
* @return {Boolean} Whether there is a writable server in this topology
216+
*/
217+
hasWritableServer() {
218+
return this.hasReadableServer(ReadPreference.primary);
219+
}
198220
}
199221

200222
function topologyTypeForServerType(serverType) {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"devDependencies": {
2626
"chai": "^4.1.2",
27+
"chai-subset": "^1.6.0",
2728
"co": "^4.6.0",
2829
"conventional-changelog-cli": "^1.3.5",
2930
"eslint": "^4.6.1",

0 commit comments

Comments
 (0)