Skip to content

Commit e1fd042

Browse files
authored
add feature to disable mongo heartbeats spans (#5526)
* add feature to disable mongodb heartbeat tracing
1 parent 17e03a7 commit e1fd042

2 files changed

Lines changed: 109 additions & 0 deletions

File tree

packages/datadog-plugin-mongodb-core/src/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
'use strict'
22

33
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
4+
const coalesce = require('koalas')
45

56
class MongodbCorePlugin extends DatabasePlugin {
67
static get id () { return 'mongodb-core' }
78
static get component () { return 'mongodb' }
89
// avoid using db.name for peer.service since it includes the collection name
910
// should be removed if one day this will be fixed
1011
static get peerServicePrecursors () { return [] }
12+
13+
configure (config) {
14+
super.configure(config)
15+
this.config.heartbeatEnabled = coalesce(
16+
config.heartbeatEnabled,
17+
process.env.DD_TRACE_MONGODB_HEARTBEAT_ENABLED,
18+
true
19+
)
20+
}
21+
1122
start ({ ns, ops, options = {}, name }) {
23+
// heartbeat commands can be disabled if this.config.heartbeatEnabled is false
24+
if (!this.config.heartbeatEnabled && isHeartbeat(ops, this.config)) {
25+
return
26+
}
1227
const query = getQuery(ops)
1328
const resource = truncate(getResource(this, ns, query, name))
1429
const service = this.serviceName({ pluginConfig: this.config })
@@ -153,4 +168,9 @@ function isBinary (val) {
153168
return val && val._bsontype === 'Binary'
154169
}
155170

171+
function isHeartbeat (ops, config) {
172+
// Check if it's a heartbeat command hello: 1 or helloOk: 1
173+
return ops && typeof ops === 'object' && (ops.hello === 1 || ops.helloOk === true)
174+
}
175+
156176
module.exports = MongodbCorePlugin

packages/datadog-plugin-mongodb-core/test/mongodb.spec.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,95 @@ describe('Plugin', () => {
459459
}).toArray()
460460
})
461461
})
462+
463+
describe('with heartbeatEnabled configuration', () => {
464+
describe('when heartbeat tracing is disabled', () => {
465+
before(() => {
466+
return agent.load('mongodb-core', {
467+
heartbeatEnabled: false
468+
})
469+
})
470+
471+
after(() => {
472+
return agent.close({ ritmReset: false })
473+
})
474+
475+
beforeEach(async () => {
476+
client = await createClient()
477+
db = client.db('test')
478+
})
479+
480+
it('should NOT create a span for heartbeat commands', (done) => {
481+
const parentSpan = tracer.startSpan('test.parent')
482+
483+
agent
484+
.use(traces => {
485+
// Should only receive the trace for the parent span
486+
expect(traces[0]).to.have.length(1)
487+
const span = traces[0][0]
488+
expect(span.name).to.equal('test.parent')
489+
})
490+
.then(done)
491+
492+
// Activate parent span scope and trigger heartbeat command
493+
tracer.scope().activate(parentSpan, async () => {
494+
// Admin connect should be all that is needed to trigger heartbeat command for newer versions of mongo
495+
client = await createClient()
496+
db = client.db('test')
497+
498+
// but we should send a test heartbeat command since older versions of mongo don't auto-send heartbeats
499+
db.command({ hello: 1 })
500+
setTimeout(() => parentSpan.finish(), 50)
501+
})
502+
})
503+
})
504+
505+
describe('when heartbeat tracing is enabled (default)', () => {
506+
before(() => {
507+
return agent.load('mongodb-core', {
508+
heartbeatEnabled: true
509+
})
510+
})
511+
512+
after(() => {
513+
return agent.close({ ritmReset: false })
514+
})
515+
516+
beforeEach(async () => {
517+
})
518+
519+
it('should create a child span for heartbeat commands', (done) => {
520+
const parentSpan = tracer.startSpan('test.parent')
521+
522+
agent
523+
.use(traces => {
524+
expect(traces[0]).to.have.length.at.least(2)
525+
const rootSpan = traces[0][0]
526+
527+
expect(rootSpan.name).to.equal('test.parent')
528+
529+
// assert that some child spans were created, these are the heartbeat spans
530+
// don't assert on exact number of spans because it's dynamic
531+
for (const childSpan of traces[0].slice(1)) {
532+
expect(childSpan.name).to.equal(expectedSchema.outbound.opName)
533+
expect(childSpan.parent_id.toString()).to.equal(rootSpan.span_id.toString()) // Verify parent-child
534+
}
535+
})
536+
.then(done)
537+
538+
// Activate parent span scope and trigger heartbeat command
539+
tracer.scope().activate(parentSpan, async () => {
540+
// Admin connect should be all that is needed to trigger heartbeat command for newer versions of mongo
541+
client = await createClient()
542+
db = client.db('test')
543+
544+
// but we should send a test heartbeat command since older versions of mongo don't auto-send heartbeats
545+
db.command({ hello: 1 })
546+
setTimeout(() => parentSpan.finish(), 200)
547+
})
548+
})
549+
})
550+
})
462551
})
463552
})
464553
})

0 commit comments

Comments
 (0)