Search code examples
javascriptnode.jsasynchronousnewrelic

New Relic breaking continuation-local-storage context


Having an issue with New Relic in Node.js. It appears to be interfering with my continuation-local-storage implementation, and by this I mean it appears to be killing my cls context.

So now, everything works perfectly without issue with my current setup. It's only when I add the line require("newrelic"); to the top of app.js, as they recommend, that the issue occurs.

Below is the particular function where the issue occurs.

function getProfile( req, res, next ) {
  var logger = new log_handler( { "script": __filename, "function": getProfile.name } );
  var session = cls.getNamespace( 'session' );
  var auth = req.headers.authorization.split( " " );

  var existingProfile = session.get( "profile" );
  if ( typeof existingProfile === "object" && existingProfile.hasOwnProperty( "federatedId" ) ) {
    return next( );
  }

  query_auth0.postTokenInfo( auth[1], function ( err, profile ) {
    if ( err ) {
      logger.warn( "query_auth0 encountered an error", { error: err.message } );
      return next( err );
    }

    session.set( "profile", profile );
    next( err );
  } );
}

The session.get("profile") statement before I call query_auth0 works and does give me back the session profile (if there is one set earlier in the stack). However, the session.set( "profile", profile); line gives me the error.

The query_auth0.postTokenInfo() function call is where it seems to break. There are a few layers there and it might be simpler to give you a summary of what its doing:

The function simply builds the request to auth0 and is then sent through our own request_handler module. This is simply built on the third party "request" npm module. The request_handler interacts with cls to pull out request_ids, but doesn't set anything.

The issue seems to occur after the request is made (and succeeds without issue), however, when I attempt to add the profile that's returned I get the following exception being thrown:

An Uncaught Exception Occurred!
Error: No context available. ns.run() or ns.bind() must be called first.
    at Namespace.set (E:\sitesroot\0\node_modules\continuation-local-storage\context.js:27:11)
    at E:\sitesroot\0\shared\middleware\get_profile.js:45:35
    at E:\sitesroot\0\shared\query_auth0.js:51:12
    at E:\sitesroot\0\shared\request_handler.js:51:12
    at E:\sitesroot\0\node_modules\async\lib\async.js:52:16
    at Immediate.<anonymous> (E:\sitesroot\0\node_modules\async\lib\async.js:1206:34)
    at Immediate.wrapped [as _onImmediate] (E:\sitesroot\0\node_modules\newrelic\lib\transaction\tracer\index.js:155:28)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

Again, this error only occurs when I'm using the newrelic module. The code works fine otherwise. Also, the issue seems to happen when the request completes and calls back.

Has anyone come across and similar issues when using New Relic in node.js that might be at least able to point me in the right direction?


Solution

  • For anyone that comes across this and is interested.

    It seems after requiring newrelic it would somehow affect the way async and continuation-local-storage would interact for some reason. There were no issues before requiring newrelic.

    However, after further searching online I found some topic related to issues with async and cls, however these issues only arose for me when newrelic was required.

    In short, the solution I found was to require cls before requiring newrelic in app.js. I'm not sure why this solved the issues but it did. If anyone has any additional feedback on the causes it would be appreciated.