Skip to content

Commit 3b53d55

Browse files
committed
fix(collector): add expiry to locks to prevent memory leak in incomplete instrumentations
1 parent 012d136 commit 3b53d55

2 files changed

Lines changed: 59 additions & 17 deletions

File tree

lib/agent/tracer/collector.js

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function Collector (options) {
4444
// init required variables
4545
this._noStack = options.noStack
4646
this._eventTtl = options.eventTtl || 1
47+
this._lockExpriryMs = 240000
4748
this._eventBuffers = { }
4849
this._sampler = reservoirSampler.create(this.samplerLimit)
4950
}
@@ -374,6 +375,7 @@ Collector.prototype._addLockToCache = function (cacheId) {
374375
cacheId = cacheId || 'root'
375376
if (this._eventBuffers[cacheId]) {
376377
++this._eventBuffers[cacheId].locks
378+
this._eventBuffers[cacheId].lastLocked = Date.now()
377379
}
378380
}
379381

@@ -418,26 +420,28 @@ Collector.prototype.collect = function () {
418420
var lockedCount = 0
419421
Object.keys(this._eventBuffers).forEach(function (key) {
420422
if (self._eventBuffers[key].locks > 0) {
421-
++lockedCount
422-
return
423-
} else {
424-
if (!self._eventBuffers[key].skipped &&
425-
some(self._eventBuffers[key].severityBuf.elements(), function (severity) {
426-
return levels.gte(severity, self.mustCollectSeverity)
427-
})) {
428-
var events = self._eventBuffers[key].eventBuf.flush()
429-
if (events.length) {
430-
self._sampler.add(events)
431-
}
423+
var deltaSinceLastLocked = Date.now() - self._eventBuffers[key].lastLocked
424+
if (deltaSinceLastLocked < self._lockExpriryMs) {
425+
++lockedCount
426+
return
432427
}
433-
self._eventBuffers[key].eventBuf.expire()
434-
self._eventBuffers[key].severityBuf.expire()
435-
436-
if (self._eventBuffers[key].skipped ||
437-
self._eventBuffers[key].severityBuf.isEmpty()) {
438-
delete self._eventBuffers[key]
428+
}
429+
if (!self._eventBuffers[key].skipped &&
430+
some(self._eventBuffers[key].severityBuf.elements(), function (severity) {
431+
return levels.gte(severity, self.mustCollectSeverity)
432+
})) {
433+
var events = self._eventBuffers[key].eventBuf.flush()
434+
if (events.length) {
435+
self._sampler.add(events)
439436
}
440437
}
438+
self._eventBuffers[key].eventBuf.expire()
439+
self._eventBuffers[key].severityBuf.expire()
440+
441+
if (self._eventBuffers[key].skipped ||
442+
self._eventBuffers[key].severityBuf.isEmpty()) {
443+
delete self._eventBuffers[key]
444+
}
441445
})
442446
debug('#collect', lockedCount + ' buffers locked')
443447
return this._sampler.flush().reduce(function (acc, x) {

lib/agent/tracer/collector.spec.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ var options = {
1212
}
1313

1414
describe('Collector', function () {
15+
var clock
16+
beforeEach(function () {
17+
clock = this.sandbox.useFakeTimers()
18+
})
19+
afterEach(function () {
20+
clock.restore()
21+
})
1522
describe('SR', function () {
1623
var payload = {
1724
protocol: 'http',
@@ -399,6 +406,37 @@ describe('Collector', function () {
399406
tracer.clientSend(payload, briefcase)
400407
expect(tracer.collect().length).to.eql(0)
401408
})
409+
410+
it('is forcefully collected after some time (lock expired)', function () {
411+
var tracer = new Collector(options)
412+
var payload = {
413+
protocol: 'http',
414+
action: 'action',
415+
resource: 'resource',
416+
host: 'host',
417+
data: { },
418+
severity: tracer.mustCollectSeverity
419+
}
420+
var briefcase = {
421+
communication: {
422+
id: 'communicationId',
423+
transactionId: 'transactionId'
424+
}
425+
}
426+
var id = 'uuid'
427+
428+
this.sandbox.stub(uuid, 'v4', function () {
429+
return id
430+
})
431+
432+
this.sandbox.stub(microtime, 'now', function () {
433+
return 2
434+
})
435+
tracer.clientSend(payload, briefcase)
436+
clock.tick(tracer._lockExpriryMs)
437+
expect(tracer.collect().length).to.eql(1)
438+
expect(tracer.collect().length).to.eql(0)
439+
})
402440
it('is not collected (in itself) when not in a transaction', function () {
403441
var tracer = new Collector(options)
404442
var payload = { }

0 commit comments

Comments
 (0)