Skip to content

Commit 630d1c3

Browse files
committed
feat(graceful): stop agents asynchronously
1 parent c8095ba commit 630d1c3

File tree

19 files changed

+267
-82
lines changed

19 files changed

+267
-82
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ scripts
88
test
99
*.spec.js
1010
*.spec.e2e.js
11+
*.mock.js
1112
example
1213

1314
# .gitignore contents

lib/agent/agent.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ Agent.prototype.start = function () {
1515
}
1616
}
1717

18-
Agent.prototype.stop = function () {
18+
Agent.prototype.stop = function (callback) {
1919
if (this.timer) {
2020
this.timer.end()
2121
}
22+
23+
if (callback) {
24+
process.nextTick(callback)
25+
}
2226
}
2327

2428
Agent.prototype.initialize = function () { }

lib/agent/api/index.js

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ CollectorApi.prototype._send = function (destinationUrl, data, callback, options
103103
res.pipe(bl(function (err, result) {
104104
if (err) {
105105
debug('#_send', '[Error]', err)
106-
return
106+
return callback(err)
107107
}
108108
callback(null, result)
109109
}))
@@ -126,37 +126,39 @@ CollectorApi.prototype._send = function (destinationUrl, data, callback, options
126126
}
127127
}
128128

129-
CollectorApi.prototype.sendRpmMetrics = function (data) {
129+
CollectorApi.prototype.sendRpmMetrics = function (data, callback) {
130+
callback = callback || function () {}
130131
if (!isNumber(this.serviceKey)) {
131132
debug('#sendRpmMetrics', '[Error] Service key not present, cannot send rpm metrics')
132-
return
133+
return callback(new Error('serviceKey is missing'))
133134
}
134135

135136
var url = util.format(this.COLLECTOR_API_RPM_METRICS, this.serviceKey)
136-
this._send(url, this._withInstanceInfo(data))
137+
this._send(url, this._withInstanceInfo(data), callback)
137138
}
138139

139140
CollectorApi.prototype.ping = function () {
140141
if (!isNumber(this.serviceKey)) {
141142
debug('#ping', '[Error] Service key not present, cannot do healthcheck')
142-
return
143143
}
144144

145145
var url = util.format(this.COLLECTOR_API_HEALTHCHECK, this.serviceKey)
146146
this._send(url, this._withInstanceInfo({}))
147147
}
148148

149-
CollectorApi.prototype.sendApmMetrics = function (data) {
149+
CollectorApi.prototype.sendApmMetrics = function (data, callback) {
150+
callback = callback || function () {}
150151
if (!isNumber(this.serviceKey)) {
151152
debug('#sendApmMetrics', '[Error] Service key not present, cannot send metrics')
152-
return
153+
return callback(new Error('serviceKey is missing'))
153154
}
154155

155156
var url = util.format(this.COLLECTOR_API_METRICS, this.serviceKey)
156-
this._send(url, this._withInstanceInfo(data))
157+
this._send(url, this._withInstanceInfo(data), callback)
157158
}
158159

159160
CollectorApi.prototype.sendMemorySnapshot = function (data, callback) {
161+
callback = callback || function () {}
160162
if (!isNumber(this.serviceKey)) {
161163
debug('#sendMemorySnapshot', '[Error] Service key not present, cannot send heapdump')
162164
return callback(new Error('serviceKey is missing'))
@@ -167,6 +169,7 @@ CollectorApi.prototype.sendMemorySnapshot = function (data, callback) {
167169
}
168170

169171
CollectorApi.prototype.sendCpuProfile = function (data, callback) {
172+
callback = callback || function () {}
170173
if (!isNumber(this.serviceKey)) {
171174
debug('#sendCpuProfile', '[Error] Service key not present, cannot send cpu profile')
172175
return callback(new Error('serviceKey is missing'))
@@ -176,33 +179,36 @@ CollectorApi.prototype.sendCpuProfile = function (data, callback) {
176179
this._send(url, this._withInstanceInfo(data), callback)
177180
}
178181

179-
CollectorApi.prototype.sendExternalEdgeMetrics = function (data) {
182+
CollectorApi.prototype.sendExternalEdgeMetrics = function (data, callback) {
183+
callback = callback || function () {}
180184
if (!isNumber(this.serviceKey)) {
181185
debug('#sendExternalEdgeMetrics', '[Error] Service key not present, cannot send metrics')
182-
return
186+
return callback(new Error('serviceKey is missing'))
183187
}
184188

185189
var url = util.format(this.COLLECTOR_API_EXTERNAL_EDGE_METRICS, this.serviceKey)
186-
this._send(url, this._withInstanceInfo(data))
190+
this._send(url, this._withInstanceInfo(data), callback)
187191
}
188192

189-
CollectorApi.prototype.sendIncomingEdgeMetrics = function (data) {
193+
CollectorApi.prototype.sendIncomingEdgeMetrics = function (data, callback) {
194+
callback = callback || function () {}
190195
if (!isNumber(this.serviceKey)) {
191196
debug('#sendIncomingEdgeMetrics', '[Error] Service key not present, cannot send metrics')
192-
return
197+
return callback(new Error('serviceKey is missing'))
193198
}
194199

195200
var url = util.format(this.COLLECTOR_API_INCOMING_EDGE_METRICS, this.serviceKey)
196201
this._send(url, this._withInstanceInfo({
197202
timestamp: (new Date()).toISOString(),
198203
data: data
199-
}))
204+
}), callback)
200205
}
201206

202207
CollectorApi.prototype.getUpdates = function (data, callback) {
208+
callback = callback || function () {}
203209
if (!isNumber(this.serviceKey)) {
204210
debug('#getUpdates', '[Error] Service key not present, cannot get updates')
205-
return
211+
return callback(new Error('serviceKey is missing'))
206212
}
207213

208214
var url = util.format(this.COLLECTOR_API_CONTROL, this.serviceKey)
@@ -222,17 +228,18 @@ CollectorApi.prototype.getUpdates = function (data, callback) {
222228
})
223229
}
224230

225-
CollectorApi.prototype.sendCustomMetrics = function (data) {
231+
CollectorApi.prototype.sendCustomMetrics = function (data, callback) {
232+
callback = callback || function () {}
226233
if (!isNumber(this.serviceKey)) {
227234
debug('#sendCustomMetrics', '[Error] Service key not present, cannot send metrics')
228-
return
235+
return callback(new Error('serviceKey is missing'))
229236
}
230237

231238
var url = util.format(this.COLLECTOR_API_CUSTOM_METRICS, this.serviceKey)
232239
this._send(url, this._withInstanceInfo({
233240
timestamp: (new Date()).toISOString(),
234241
data: data
235-
}))
242+
}), callback)
236243
}
237244

238245
CollectorApi.prototype.sendDependencies = function (dependencies) {

lib/agent/api/index.spec.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var util = require('util')
22
var url = require('url')
33
var https = require('https')
4-
4+
var FakeReadable = require('../util').FakeStream.FakeReadable
5+
var FakeWritable = require('../util').FakeStream.FakeWritable
56
var CollectorApi = require('./')
67

78
var expect = require('chai').expect
@@ -10,6 +11,7 @@ var libPackage = require('../../../package')
1011

1112
describe('The Trace CollectorApi module', function () {
1213
var defaultConfig
14+
1315
beforeEach(function () {
1416
defaultConfig = {
1517
serviceName: 'testName',
@@ -116,6 +118,49 @@ describe('The Trace CollectorApi module', function () {
116118
expect(sendStub).to.have.been.calledWith(sendUrl, data)
117119
})
118120

121+
it('calls callback on successful send', function (done) {
122+
var serviceKey = 12
123+
124+
var path = util.format(defaultConfig.collectorApiRpmMetricsEndpoint, serviceKey)
125+
var sendUrl = url.resolve(defaultConfig.collectorApiUrl, path)
126+
127+
var collectorApi = CollectorApi.create(defaultConfig)
128+
collectorApi.serviceKey = serviceKey
129+
130+
this.sandbox.stub(https, 'request', function (_, cb) {
131+
process.nextTick(function () {
132+
cb(new FakeReadable())
133+
})
134+
return new FakeWritable()
135+
})
136+
137+
collectorApi._send(sendUrl, {}, function (err) {
138+
done(err)
139+
})
140+
})
141+
142+
it('calls callback on send failure', function (done) {
143+
var serviceKey = 12
144+
145+
var collectorApi = CollectorApi.create(defaultConfig)
146+
collectorApi.serviceKey = serviceKey
147+
148+
this.sandbox.stub(https, 'request', function (_, cb) {
149+
process.nextTick(function () {
150+
cb(new FakeReadable(new Error('uh-oh')))
151+
})
152+
return new FakeWritable()
153+
})
154+
155+
collectorApi._send('test-url', {}, function (err) {
156+
if (err) {
157+
done()
158+
} else {
159+
done('Should have been called with error')
160+
}
161+
})
162+
})
163+
119164
it('sends incomingEdgeMetrics with the required properties', function (done) {
120165
var serviceKey = 12
121166
var collectorApi = CollectorApi.create(defaultConfig)

lib/agent/index.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,24 @@ Agent.prototype.start = function () {
111111
})
112112
}
113113

114-
Agent.prototype.stop = function () {
114+
Agent.prototype.stop = function (callback) {
115115
debug('#stop', 'Stopping agents...')
116-
this.agents.forEach(function (agent) {
117-
agent.stop()
118-
debug('#stop', agent.name + ' stopped')
116+
var agents = this.agents
117+
var counter = 1
118+
var error
119+
120+
agents.forEach(function (agent) {
121+
agent.stop(function (err) {
122+
if (!error && err) {
123+
error = err
124+
}
125+
126+
if (counter >= agents.length) {
127+
callback(error)
128+
} else {
129+
counter++
130+
}
131+
})
119132
})
120133
}
121134

lib/agent/metrics/apm/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ function ApmMetrics (options) {
4545

4646
inherits(ApmMetrics, Agent)
4747

48-
ApmMetrics.prototype.sendMetrics = function () {
48+
ApmMetrics.prototype.sendMetrics = function (callback) {
49+
callback = callback || function () {}
4950
var eventloop = this.getEventLoop()
5051
var gc = this.getGC()
5152

@@ -63,7 +64,7 @@ ApmMetrics.prototype.sendMetrics = function () {
6364
databag.gc = gc
6465
}
6566

66-
this.collectorApi.sendApmMetrics(databag)
67+
this.collectorApi.sendApmMetrics(databag, callback)
6768

6869
this.reset()
6970
}
@@ -141,6 +142,11 @@ ApmMetrics.prototype.reset = function () {
141142
}
142143
}
143144

145+
ApmMetrics.prototype.stop = function (callback) {
146+
Agent.prototype.stop.call(this)
147+
this.sendMetrics(callback)
148+
}
149+
144150
function create (options) {
145151
return new ApmMetrics(options)
146152
}

lib/agent/metrics/custom/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,22 @@ CustomMetrics.prototype.record = function (name, value) {
4242
}
4343
}
4444

45-
CustomMetrics.prototype.sendMetrics = function () {
45+
CustomMetrics.prototype.sendMetrics = function (callback) {
46+
callback = callback || function () {}
4647
this.collectorApi.sendCustomMetrics({
4748
incrementMetrics: this.incrementMetrics,
4849
recordMetrics: this.recordMetrics
49-
})
50+
}, callback)
5051

5152
this.incrementMetrics = {}
5253
this.recordMetrics = {}
5354
}
5455

56+
CustomMetrics.prototype.stop = function (callback) {
57+
Agent.prototype.stop.call(this)
58+
this.sendMetrics(callback)
59+
}
60+
5561
function create (options) {
5662
return new CustomMetrics(options)
5763
}

lib/agent/metrics/externalEdge/index.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ ExternalEdgeMetrics.prototype.calculateTimes = function (items) {
7070
}
7171
}
7272

73-
ExternalEdgeMetrics.prototype.sendMetrics = function () {
73+
ExternalEdgeMetrics.prototype.sendMetrics = function (callback) {
7474
var _this = this
75+
callback = callback || function () {}
7576

7677
var metrics = flatMap(Object.keys(this.metrics), function (protocol) {
7778
return Object.keys(_this.metrics[protocol]).map(function (hostName) {
@@ -96,20 +97,25 @@ ExternalEdgeMetrics.prototype.sendMetrics = function () {
9697

9798
// if no metrics, don't send anything
9899
if (!metrics || !metrics.length) {
99-
return
100+
return callback()
100101
}
101102

102103
this.collectorApi.sendExternalEdgeMetrics({
103104
timestamp: (new Date()).toISOString(),
104105
data: metrics
105-
})
106+
}, callback)
106107
}
107108

108109
ExternalEdgeMetrics.prototype.EDGE_STATUS = {
109110
OK: 0,
110111
NOT_OK: 1
111112
}
112113

114+
ExternalEdgeMetrics.prototype.stop = function (callback) {
115+
Agent.prototype.stop.call(this)
116+
this.sendMetrics(callback)
117+
}
118+
113119
function create (options) {
114120
return new ExternalEdgeMetrics(options)
115121
}

lib/agent/metrics/incomingEdge/index.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ IncomingEdgeMetrics.prototype._calculateTimes = function (items) {
5959
}
6060
}
6161

62-
IncomingEdgeMetrics.prototype.sendMetrics = function () {
62+
IncomingEdgeMetrics.prototype.sendMetrics = function (callback) {
6363
var _this = this
64+
callback = callback || function () {}
6465

6566
var metrics = flatMap(Object.keys(this.metrics), function (protocol) {
6667
return Object.keys(_this.metrics[protocol]).map(function (serviceKey) {
@@ -89,17 +90,22 @@ IncomingEdgeMetrics.prototype.sendMetrics = function () {
8990

9091
// if no metrics, don't send anything
9192
if (!metrics || !metrics.length) {
92-
return
93+
return callback()
9394
}
9495

95-
this.collectorApi.sendIncomingEdgeMetrics(metrics)
96+
this.collectorApi.sendIncomingEdgeMetrics(metrics, callback)
9697
}
9798

9899
IncomingEdgeMetrics.prototype.EDGE_STATUS = {
99100
OK: 0,
100101
NOT_OK: 1
101102
}
102103

104+
IncomingEdgeMetrics.prototype.stop = function (callback) {
105+
Agent.prototype.stop.call(this)
106+
this.sendMetrics(callback)
107+
}
108+
103109
function create (options) {
104110
return new IncomingEdgeMetrics(options)
105111
}

0 commit comments

Comments
 (0)