Skip to content

Commit 62a6e61

Browse files
committed
fix(mysql): instrument pool.getConnection properly
1 parent b463c22 commit 62a6e61

File tree

2 files changed

+111
-34
lines changed

2 files changed

+111
-34
lines changed

lib/instrumentations/trace-instrumentation-mysql.js

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,53 @@ module.exports = function (mysql, agent, pkg) {
2323
var callback = args[last]
2424

2525
if (name === 'query') {
26-
var config = moduleName === 'pool'
27-
? this.config.connectionConfig
28-
: this.config
26+
wrapQuery.call(this, moduleName, original, args, callback)
27+
} else if (name === 'getConnection') {
28+
wrapGetConnection.call(this, original, callback)
29+
} else {
30+
return original.apply(this, args)
31+
}
32+
}
33+
}
2934

30-
var user = config.user || 'unknown'
31-
var host = config.host || 'unknown'
32-
var port = config.port ? ':' + config.port : ''
33-
var database = config.database ? '/' + config.database : ''
35+
function wrapQuery (moduleName, original, args, callback) {
36+
var config = moduleName === 'pool'
37+
? this.config.connectionConfig
38+
: this.config
3439

35-
var connectionURI = 'mysql://' +
36-
user + '@' +
37-
host +
38-
port +
39-
database
40+
var user = config.user || 'unknown'
41+
var host = config.host || 'unknown'
42+
var port = config.port ? ':' + config.port : ''
43+
var database = config.database ? '/' + config.database : ''
4044

41-
var hostString = host +
42-
port +
43-
database
45+
var connectionURI = 'mysql://' +
46+
user + '@' +
47+
host +
48+
port +
49+
database
4450

45-
return utils.wrapQuery.call(this,
46-
original,
47-
args,
48-
agent, {
49-
continuationMethod: typeof callback === 'function' ? 'callback' : 'eventEmitter',
50-
protocol: consts.PROTOCOLS.MYSQL,
51-
url: connectionURI,
52-
host: hostString,
53-
method: utils.tryParseSql(args[0]) || 'unknown'
54-
})
55-
} else {
56-
return original.apply(this, args)
57-
}
51+
var hostString = host +
52+
port +
53+
database
54+
55+
return utils.wrapQuery.call(this,
56+
original,
57+
args,
58+
agent, {
59+
continuationMethod: typeof callback === 'function' ? 'callback' : 'eventEmitter',
60+
protocol: consts.PROTOCOLS.MYSQL,
61+
url: connectionURI,
62+
host: hostString,
63+
method: utils.tryParseSql(args[0]) || 'unknown'
64+
})
65+
}
66+
67+
function wrapGetConnection (original, callback) {
68+
var wrappedCallback = function (err, connection) {
69+
Shimmer.wrap(connection, CONNECTION_OPERATIONS, wrapOperations.bind(this, 'connection'))
70+
return callback.call(this, err, connection)
5871
}
72+
return original.call(this, wrappedCallback)
5973
}
6074

6175
Shimmer.wrap(mysql, 'createConnection', function (original) {

test/instrumentations/mysql/mysql.spec.js

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var wrapper = require('../../../lib/instrumentations/trace-instrumentation-mysql
55
var expect = require('chai').expect
66
var Shimmer = require('../../../lib/utils/shimmer')
77
var utils = require('../../../lib/instrumentations/utils')
8+
var sinon = require('sinon')
89

910
describe('The mysql wrapper', function () {
1011
var CONNECTION_OPERATIONS = [
@@ -19,6 +20,44 @@ describe('The mysql wrapper', function () {
1920
'destroy'
2021
]
2122

23+
var sandbox = sinon.sandbox.create()
24+
var fakeAgent = {
25+
incomingEdgeMetrics: {
26+
report: sandbox.spy()
27+
},
28+
tracer: {
29+
collector: {
30+
mustCollectSeverity: 9,
31+
defaultSeverity: 0,
32+
clientRecv: sandbox.spy(),
33+
clientSend: sandbox.stub().returns({
34+
event: {
35+
p: 'dfasdfs'
36+
},
37+
duffelBag: {
38+
timestamp: 0
39+
}
40+
}),
41+
systemError: sandbox.spy()
42+
},
43+
send: sandbox.spy()
44+
},
45+
storage: {
46+
get: sandbox.spy()
47+
},
48+
externalEdgeMetrics: {
49+
report: sandbox.spy(),
50+
EDGE_STATUS: {
51+
OK: 1,
52+
NOT_OK: 0
53+
}
54+
}
55+
}
56+
57+
afterEach(function () {
58+
sandbox.restore()
59+
})
60+
2261
describe('Connection', function () {
2362
it('should wrap var connection = mysql.createConnection and var connection = mysql.createPool', function () {
2463
var shimmerWrapStub = this.sandbox.stub(Shimmer, 'wrap')
@@ -44,7 +83,6 @@ describe('The mysql wrapper', function () {
4483
it('should use wrapQuery with expected arguments to wrap connection.query', function (done) {
4584
var sandbox = this.sandbox
4685
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
47-
var fakeAgent = { clearly: 'a mock' }
4886

4987
var mysql = require('mysql')
5088

@@ -87,7 +125,6 @@ describe('The mysql wrapper', function () {
87125
it('should use wrapQuery with expected arguments to wrap pool.query', function (done) {
88126
var sandbox = this.sandbox
89127
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
90-
var fakeAgent = { clearly: 'a mock' }
91128

92129
var mysql = require('mysql')
93130

@@ -124,10 +161,39 @@ describe('The mysql wrapper', function () {
124161
expect(shimmerWrapStub).to.have.been.called
125162
})
126163

164+
it('should wrap connection returned by pool.getConnection properly', function (done) {
165+
require.cache = {}
166+
var mysql = require('mysql')
167+
168+
wrapper(mysql, fakeAgent)
169+
170+
// This is tricky. We have to stub exactly after wrapping, and before
171+
// createConnection to catch the wrapping of the query operation
172+
173+
var pool = mysql.createPool({
174+
host: process.env.MYSQL_HOST || 'localhost',
175+
user: 'root',
176+
password: '',
177+
database: 'information_schema'
178+
})
179+
180+
pool.getConnection(function (err, conn) {
181+
if (err) throw err
182+
conn.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
183+
if (error) throw error
184+
expect(fakeAgent.tracer.collector.clientSend).to.have.been.calledOnce
185+
expect(fakeAgent.tracer.collector.clientRecv).to.have.been.calledWith({
186+
protocol: 'mysql',
187+
status: 'ok'
188+
})
189+
done()
190+
})
191+
})
192+
})
193+
127194
it('should not leak credentials', function (done) {
128195
var sandbox = this.sandbox
129196
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
130-
var fakeAgent = { clearly: 'a mock' }
131197

132198
var mysql = require('mysql')
133199

@@ -171,7 +237,6 @@ describe('The mysql wrapper', function () {
171237
it('should use wrapQuery with expected arguments to wrap connection.query', function (done) {
172238
var sandbox = this.sandbox
173239
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
174-
var fakeAgent = { clearly: 'a mock' }
175240

176241
var mysql = require('mysql')
177242

@@ -215,7 +280,6 @@ describe('The mysql wrapper', function () {
215280
it('should use wrapQuery with expected arguments to wrap pool.query', function (done) {
216281
var sandbox = this.sandbox
217282
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
218-
var fakeAgent = { clearly: 'a mock' }
219283

220284
var mysql = require('mysql')
221285

@@ -255,7 +319,6 @@ describe('The mysql wrapper', function () {
255319
it('should not leak credentials', function (done) {
256320
var sandbox = this.sandbox
257321
var fakeWrapQuery = sandbox.stub(utils, 'wrapQuery')
258-
var fakeAgent = { clearly: 'a mock' }
259322

260323
var mysql = require('mysql')
261324

0 commit comments

Comments
 (0)