You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We have an issue (JIRA NR-353533) wherein a customer is attempting to instrument a long running action. This results in a large amount of system memory being used, and the customer thinking that the agent is causing a memory leak. Memory is not being leaked. The agent operates in a transactional manner, collecting information on all activity during a "request," and only releasing memory when the request has ended. Since the request the customer is instrumenting is effectively a websocket (a "server send event" scenario), the result is a large amount of memory usage.
While investigating this issue, we realized that it could be beneficial if the agent provided a mechanism to disable instrumentation for individual request handlers. We have support for ignoring a transaction, but that doesn't stop the information from being collected in the process. It merely prevents the collected data from being shipped to the New Relic collector.
I took some time to determine if we could provide an API that would solve this problem. I discovered that we could do it, but we would need to review all of our instrumentations to ensure that they all flow through a single hook point. In particular, our core module instrumentations seem to skip the "shim" we utilize for most other instrumentations. Below is a summary of the discovery I performed, and should serve as a jumping off point for solving this issue.
First, I updated shim.record to recognize decorated entities that should be omitted from instrumentation.
My modification (notice the new if block with the Symbol.for):
functionrecord(nodule,properties,recordNamer){if(this.isFunction(properties)){recordNamer=propertiesproperties=null}if(nodule[Symbol.for('newrelic-skip-instrumentation')]){return}returnthis.wrap(nodule,properties,functionmakeWrapper(shim,fn,name){// Can't record things that aren't functions.if(!shim.isFunction(fn)){shim.logger.debug('Not recording non-function "%s".',name)returnfn}shim.logger.trace('Wrapping "%s" with metric recording.',name)returnrecordWrapper({ shim, fn, name, recordNamer })})}
test('skips recording transaction if handler is decorated',asynct=>{const{ agent, app, address, port }=t.nrconstplan=tsplan(t,{plan: 10})letfinishedCount=0agent.on('transactionFinished',tx=>{finishedCount+=1if(finishedCount!==2){return}assertSegments(tx.trace,tx.trace.root,['WebTransaction/Expressjs/GET//data',['Expressjs/Route Path: /data',['Nodejs/Middleware/Expressjs/<anonymous>']]],{exact: true},{assert: plan})})app.get('/data',(req,res)=>{res.send('ok')res.end()})handler[Symbol.for('newrelic-skip-instrumentation')]=trueapp.get('/test',handler)consturl=`http://localhost:${port}/test`helper.makeGetRequest(url,{json: true},(error,res,body)=>{plan.ifError(error)plan.equal(res.statusCode,200)plan.deepEqual(body,{status: 'ok'})})awaitplan.completedfunctionhandler(req,res){plan.ok('handler invoked')http.get(`http://${address}:${port}/data`,response=>{letdata=''response.on('data',d=>{data+=d.toString()})response.on('end',()=>{res.send({status: data})res.end()})})}})
With some modifications to utils.js:
asyncfunctionsetup(ctx,config={}){ctx.nr={}ctx.nr.agent=helper.instrumentMockedAgent(config)ctx.nr.isExpress5=isExpress5()ctx.nr.express=require('express')ctx.nr.app=ctx.nr.express()const{ promise, resolve }=promiseResolvers()constserver=require('http').createServer(ctx.nr.app)server.listen(0,TEST_HOST,resolve)awaitpromisectx.nr.server=server// Added the address to the context:constaddress=server.address().addressctx.nr.address=address.startsWith('::') ? `[${address}]` : addressctx.nr.port=server.address().port}
Notice that the Nodejs/Middleware/Expressjs/handler segment is omitted from the second trace, but the child trace (the outgoing HTTP request) is still included in the second trace. This is not what we want. We want the handler and all child operations to be omitted. But the child operations are being included because the http_outbound instrumentation does not utilize the shim method we patched.
The text was updated successfully, but these errors were encountered:
We have an issue (JIRA NR-353533) wherein a customer is attempting to instrument a long running action. This results in a large amount of system memory being used, and the customer thinking that the agent is causing a memory leak. Memory is not being leaked. The agent operates in a transactional manner, collecting information on all activity during a "request," and only releasing memory when the request has ended. Since the request the customer is instrumenting is effectively a websocket (a "server send event" scenario), the result is a large amount of memory usage.
While investigating this issue, we realized that it could be beneficial if the agent provided a mechanism to disable instrumentation for individual request handlers. We have support for ignoring a transaction, but that doesn't stop the information from being collected in the process. It merely prevents the collected data from being shipped to the New Relic collector.
I took some time to determine if we could provide an API that would solve this problem. I discovered that we could do it, but we would need to review all of our instrumentations to ensure that they all flow through a single hook point. In particular, our core module instrumentations seem to skip the "shim" we utilize for most other instrumentations. Below is a summary of the discovery I performed, and should serve as a jumping off point for solving this issue.
First, I updated
shim.record
to recognize decorated entities that should be omitted from instrumentation.shim.record
node-newrelic/lib/shim/shim.js
Lines 638 to 654 in 7dceae9
My modification (notice the new
if
block with theSymbol.for
):Second, I added a new test to verify it works:
bare-router.test.js
https://github.com/newrelic/node-newrelic/blob/7dceae94f564573df4093a5ceb4792ed6d6af1ba/test/versioned/express/bare-router.test.js
With some modifications to
utils.js
:The result are the following traces:
Regular trace that does not ignore anything.
Trace that is supposed to ignore the handler
Notice that the
Nodejs/Middleware/Expressjs/handler
segment is omitted from the second trace, but the child trace (the outgoing HTTP request) is still included in the second trace. This is not what we want. We want the handler and all child operations to be omitted. But the child operations are being included because thehttp_outbound
instrumentation does not utilize the shim method we patched.The text was updated successfully, but these errors were encountered: