Skip to content

Commit b5f7ddc

Browse files
author
Martin Kuba
committed
instrumentation: fixed issue with pg "native" getter instrumentation
1 parent 055e873 commit b5f7ddc

File tree

2 files changed

+121
-8
lines changed

2 files changed

+121
-8
lines changed

lib/instrumentation/pg.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ function initializeSegment(tracer, segment, config) {
3636
}
3737

3838
module.exports = function initialize(agent, pgsql) {
39+
if (!pgsql) return
40+
3941
var tracer = agent.tracer
4042

4143
// allows for native wrapping to not happen if not necessary
@@ -45,15 +47,20 @@ module.exports = function initialize(agent, pgsql) {
4547
return instrumentPGNative('pg', pgsql)
4648
}
4749

48-
// using ('pg').native in their require
50+
// The pg module defines "native" getter which sets up the native client lazily
51+
// (only when called). We replace the getter, so that we can instrument the native
52+
// client. The original getter replaces itself with the instance of the native
53+
// client, so only instrument if the getter exists (otherwise assume already
54+
// instrumented).
4955
var origGetter = pgsql.__lookupGetter__('native')
50-
delete pgsql.native
51-
pgsql.__defineGetter__('native', function getNative() {
52-
var temp = origGetter()
53-
instrumentPGNative('pg.native', pgsql.native)
54-
return temp
55-
})
56-
56+
if (origGetter) {
57+
delete pgsql.native
58+
pgsql.__defineGetter__('native', function getNative() {
59+
var temp = origGetter()
60+
instrumentPGNative('pg.native', temp)
61+
return temp
62+
})
63+
}
5764

5865
// wrapping for native
5966
function instrumentPGNative(eng, pg) {

test/unit/instrumentation/postgresql.test.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,112 @@
11
'use strict'
22

3+
var path = require('path')
4+
, chai = require('chai')
5+
, expect = chai.expect
6+
, helper = require('../../lib/agent_helper')
7+
38
describe("agent instrumentation of PostgreSQL", function () {
9+
var agent
10+
, initialize
11+
12+
before(function () {
13+
agent = helper.loadMockedAgent()
14+
initialize = require('../../../lib/instrumentation/pg')
15+
})
16+
17+
after(function () {
18+
helper.unloadAgent(agent)
19+
})
20+
21+
describe("shouldn't cause bootstrapping to fail", function () {
22+
it("when passed no module", function () {
23+
expect(function () { initialize(agent); }).not.throws()
24+
})
25+
26+
it("when passed an empty module", function () {
27+
expect(function () { initialize(agent, {}); }).not.throws()
28+
})
29+
})
30+
31+
describe("lazy loading of native PG client", function() {
32+
33+
function getMockModule() {
34+
function PG(clientConstructor) {
35+
this.Client = clientConstructor
36+
}
37+
38+
function DefaultClient() {}
39+
DefaultClient.prototype.query = function() {}
40+
function NativeClient() {}
41+
NativeClient.prototype.query = function() {}
42+
43+
var mockPg = new PG(DefaultClient)
44+
mockPg.__defineGetter__("native", function() {
45+
delete mockPg.native;
46+
mockPg.native = new PG(NativeClient);
47+
return mockPg.native;
48+
})
49+
return mockPg
50+
}
51+
52+
it("instruments when native getter is called", function() {
53+
var mockPg = getMockModule()
54+
55+
initialize(agent, mockPg)
56+
57+
var pg = mockPg.native
58+
expect(pg.Client['__NR_original'].name).equal('NativeClient')
59+
60+
var pg = mockPg
61+
expect(pg.Client.name).equal('DefaultClient')
62+
})
63+
64+
it("does not fail when getter is called multiple times", function() {
65+
var mockPg = getMockModule()
66+
67+
initialize(agent, mockPg)
68+
var pg1 = mockPg.native
69+
70+
initialize(agent, mockPg)
71+
var pg2 = mockPg.native
72+
73+
expect(pg1).equal(pg2)
74+
})
75+
76+
it("does not interfere with non-native instrumentation", function() {
77+
var mockPg = getMockModule()
78+
79+
initialize(agent, mockPg)
80+
var nativeClient = mockPg.native
81+
expect(nativeClient.Client['__NR_original'].name).equal('NativeClient')
82+
var defaultClient = mockPg
83+
expect(defaultClient.Client.name).equal('DefaultClient')
84+
85+
initialize(agent, mockPg)
86+
var nativeClient = mockPg.native
87+
expect(nativeClient.Client['__NR_original'].name).equal('NativeClient')
88+
var defaultClient = mockPg
89+
expect(defaultClient.Client.name).equal('DefaultClient')
90+
})
91+
92+
it("when pg modules is refreshed in cache", function() {
93+
var mockPg = getMockModule()
94+
95+
// instrument once
96+
initialize(agent, mockPg)
97+
var pg1 = mockPg.native
98+
expect(pg1.Client['__NR_original'].name).equal('NativeClient')
99+
100+
// simulate deleting from module cache
101+
mockPg = getMockModule()
102+
initialize(agent, mockPg)
103+
var pg2 = mockPg.native
104+
expect(pg2.Client['__NR_original'].name).equal('NativeClient')
105+
106+
expect(pg1).not.equal(pg2)
107+
})
108+
})
109+
4110
describe("for each operation", function () {
5111
it("should update the global database aggregate statistics")
6112
it("should also update the global web aggregate statistics")

0 commit comments

Comments
 (0)