Coder Social home page Coder Social logo

jeff-lewis / cls-hooked Goto Github PK

View Code? Open in Web Editor NEW

This project forked from othiym23/node-continuation-local-storage

751.0 19.0 90.0 531 KB

cls-hooked : CLS using AsynWrap or async_hooks instead of async-listener for node 4.7+

License: BSD 2-Clause "Simplified" License

JavaScript 100.00%
cls continuation-local-storage nodejs async-hooks asyncwrap cls-hooked asynchooks

cls-hooked's Introduction

NPM

Build Status

Continuation-Local Storage ( Hooked )

This is a fork of CLS using AsyncWrap OR async_hooks instead of async-listener.

When running Nodejs version < 8, this module uses AsyncWrap which is an unsupported Nodejs API, so please consider the risk before using it.

When running Nodejs version >= 8.2.1, this module uses the newer async_hooks API which is considered Experimental by Nodejs.

Thanks to @trevnorris for AsyncWrap, async_hooks and all the async work in Node and @AndreasMadsen for async-hook

A little history of "AsyncWrap/async_hooks" and its incarnations

  1. First implementation was called AsyncListener in node v0.11 but was removed from core prior to Nodejs v0.12
  2. Second implementation called AsyncWrap, async-wrap or async_wrap was included to Nodejs v0.12.
    • AsyncWrap is unofficial and undocumented but is currently in Nodejs versions 6 & 7
    • cls-hooked uses AsyncWrap when run in Node < 8.
  3. Third implementation and offically Node-eps accepted AsyncHooks (async_hooks) API was included in Nodejs v8. :) The latest version of cls-hooked uses async_hooks API when run in Node >= 8.2.1

Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads. The standard Node convention of functions calling functions is very similar to something called "continuation-passing style" in functional programming, and the name comes from the way this module allows you to set and get values that are scoped to the lifetime of these chains of function calls.

Suppose you're writing a module that fetches a user and adds it to a session before calling a function passed in by a user to continue execution:

// setup.js

var createNamespace = require('cls-hooked').createNamespace;
var session = createNamespace('my session');

var db = require('./lib/db.js');

function start(options, next) {
  db.fetchUserById(options.id, function (error, user) {
    if (error) return next(error);

    session.set('user', user);

    next();
  });
}

Later on in the process of turning that user's data into an HTML page, you call another function (maybe defined in another module entirely) that wants to fetch the value you set earlier:

// send_response.js

var getNamespace = require('cls-hooked').getNamespace;
var session = getNamespace('my session');

var render = require('./lib/render.js')

function finish(response) {
  var user = session.get('user');
  render({user: user}).pipe(response);
}

When you set values in continuation-local storage, those values are accessible until all functions called from the original function – synchronously or asynchronously – have finished executing. This includes callbacks passed to process.nextTick and the timer functions (setImmediate, setTimeout, and setInterval), as well as callbacks passed to asynchronous functions that call native functions (such as those exported from the fs, dns, zlib and crypto modules).

A simple rule of thumb is anywhere where you might have set a property on the request or response objects in an HTTP handler, you can (and should) now use continuation-local storage. This API is designed to allow you extend the scope of a variable across a sequence of function calls, but with values specific to each sequence of calls.

Values are grouped into namespaces, created with createNamespace(). Sets of function calls are grouped together by calling them within the function passed to .run() on the namespace object. Calls to .run() can be nested, and each nested context this creates has its own copy of the set of values from the parent context. When a function is making multiple asynchronous calls, this allows each child call to get, set, and pass along its own context without overwriting the parent's.

A simple, annotated example of how this nesting behaves:

var createNamespace = require('cls-hooked').createNamespace;

var writer = createNamespace('writer');
writer.run(function () {
  writer.set('value', 0);

  requestHandler();
});

function requestHandler() {
  writer.run(function(outer) {
    // writer.get('value') returns 0
    // outer.value is 0
    writer.set('value', 1);
    // writer.get('value') returns 1
    // outer.value is 1
    process.nextTick(function() {
      // writer.get('value') returns 1
      // outer.value is 1
      writer.run(function(inner) {
        // writer.get('value') returns 1
        // outer.value is 1
        // inner.value is 1
        writer.set('value', 2);
        // writer.get('value') returns 2
        // outer.value is 1
        // inner.value is 2
      });
    });
  });

  setTimeout(function() {
    // runs with the default context, because nested contexts have ended
    console.log(writer.get('value')); // prints 0
  }, 1000);
}

cls.createNamespace(name)

  • return: {Namespace}

Each application wanting to use continuation-local values should create its own namespace. Reading from (or, more significantly, writing to) namespaces that don't belong to you is a faux pas.

cls.getNamespace(name)

  • return: {Namespace}

Look up an existing namespace.

cls.destroyNamespace(name)

Dispose of an existing namespace. WARNING: be sure to dispose of any references to destroyed namespaces in your old code, as contexts associated with them will no longer be propagated.

cls.reset()

Completely reset all continuation-local storage namespaces. WARNING: while this will stop the propagation of values in any existing namespaces, if there are remaining references to those namespaces in code, the associated storage will still be reachable, even though the associated state is no longer being updated. Make sure you clean up any references to destroyed namespaces yourself.

process.namespaces

  • return: dictionary of {Namespace} objects

Continuation-local storage has a performance cost, and so it isn't enabled until the module is loaded for the first time. Once the module is loaded, the current set of namespaces is available in process.namespaces, so library code that wants to use continuation-local storage only when it's active should test for the existence of process.namespaces.

Class: Namespace

Application-specific namespaces group values local to the set of functions whose calls originate from a callback passed to namespace.run() or namespace.bind().

namespace.active

  • return: the currently active context on a namespace

namespace.set(key, value)

  • return: value

Set a value on the current continuation context. Must be set within an active continuation chain started with namespace.run() or namespace.bind().

namespace.get(key)

  • return: the requested value, or undefined

Look up a value on the current continuation context. Recursively searches from the innermost to outermost nested continuation context for a value associated with a given key. Must be set within an active continuation chain started with namespace.run() or namespace.bind().

namespace.run(callback)

  • return: the context associated with that callback

Create a new context on which values can be set or read. Run all the functions that are called (either directly, or indirectly through asynchronous functions that take callbacks themselves) from the provided callback within the scope of that namespace. The new context is passed as an argument to the callback when it's called.

namespace.runAndReturn(callback)

  • return: the return value of the callback

Create a new context on which values can be set or read. Run all the functions that are called (either directly, or indirectly through asynchronous functions that take callbacks themselves) from the provided callback within the scope of that namespace. The new context is passed as an argument to the callback when it's called.

Same as namespace.run() but returns the return value of the callback rather than the context.

namespace.bind(callback, [context])

  • return: a callback wrapped up in a context closure

Bind a function to the specified namespace. Works analogously to Function.bind() or domain.bind(). If context is omitted, it will default to the currently active context in the namespace, or create a new context if none is currently defined.

namespace.bindEmitter(emitter)

Bind an EventEmitter to a namespace. Operates similarly to domain.add, with a less generic name and the additional caveat that unlike domains, namespaces never implicitly bind EventEmitters to themselves when they're created within the context of an active namespace.

The most likely time you'd want to use this is when you're using Express or Connect and want to make sure your middleware execution plays nice with CLS, or are doing other things with HTTP listeners:

http.createServer(function (req, res) {
  writer.bindEmitter(req);
  writer.bindEmitter(res);

  // do other stuff, some of which is asynchronous
});

namespace.createContext()

  • return: a context cloned from the currently active context

Use this with namespace.bind(), if you want to have a fresh context at invocation time, as opposed to binding time:

function doSomething(p) {
  console.log("%s = %s", p, ns.get(p));
}

function bindLater(callback) {
  return writer.bind(callback, writer.createContext());
}

setInterval(function () {
  var bound = bindLater(doSomething);
  bound('test');
}, 100);

context

A context is a plain object created using the enclosing context as its prototype.

copyright & license

See LICENSE for the details of the BSD 2-clause "simplified" license used by continuation-local-storage. This package was developed in 2012-2013 (and is maintained now) by Forrest L Norvell, @othiym23, with considerable help from Timothy Caswell, @creationix, working for The Node Firm. This work was underwritten by New Relic for use in their Node.js instrumentation agent, so maybe give that a look if you have some Node.js performance-monitoring needs.

cls-hooked's People

Contributors

andreasmadsen avatar antonmos avatar creationix avatar enko avatar ericwaldheim avatar gergelyke avatar groundwater avatar jeff-lewis avatar jiaz avatar johncmcdonough avatar leedm777 avatar othiym23 avatar rnachmanyanyvision avatar strml avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cls-hooked's Issues

[Question] CLS for browser

Hi,

I really do not know where to ask this, so I'll try here ; I hope you do not mind.

Are you aware of any CLS implementation or async api/hooks that would work in the browser ? Not with a global shim but with nested context support.

Thank you.

Difference with async-local-storage

Can someone explain what is the difference between this library compared to https://github.com/vicanso/async-local-storage ? That library seems super simple.

I checked other solutions as we are having issues with destroying the namespace.
Is destroying the namespace necessary on every request?
Here is our middleware:

return async function(ctx, next) {
      const namespace = continuationLocalStorage.createNamespace('session');

      try{
        await new Promise(namespace.bind(function(resolve, reject) {
          namespace.set('request_id', ctx.request.header['x-request-id']);

          next().then(resolve).catch(reject);
        }));
      } catch(error) {
        throw error;
      } finally {
        continuationLocalStorage.destroyNamespace('session');
      }
    };

How to integrate with SocketIO

I'm trying to integrate the cls_hooked with SocketIO, using the Feathersjs framework:

const socketio = require('@feathersjs/socketio')
const tracer = require('../tracer')
const sync = require('feathers-sync')

module.exports = app => {
  // 'socket.feathers' is the same object as the connection
  // in a channel. 'socket.request' and 'socket.handshake' contains information
  // the HTTP request that initiated the connection.
  app.configure(socketio(io => {

    // https://docs.feathersjs.com/api/socketio.html#params
    io.use( (socket, next) => {
      tracer.run(() => {
        tracer.set('ip', socket.conn.remoteAddress)
        next()
      })
    })

  }))
}

The tracer.js:

const cls = require('cls-hooked')
const tracer = cls.createNamespace('app')
module.exports = tracer

... but the context is lost, it's not possible to get the 'ip' property from service functions.

Clean old context references

Hi there,

I was wondering whether the Map of contexts would be automatically cleaned at some point or should we use destroy to do that?

It could lead to performance issue as the Map is becoming bigger and bigger.

Thanks for your work!

CPU leakage

Hi, I've started using this library on our production servers. It's a great help and saves a lot of "passing down" from one function to the next. However, over time I see that it increasingly uses more and more CPU resources, specifically around things related to async-hooks. I took a CPU profile of the server and I see emitHook going from 7% to 30%. Inside emitHook, the processes that grow the most are destroy (1% to 8%), before grows from 2% to ~6%, after (~1% to 6$). Is this expected. Am I using it wrong? Please advise.
Below is my code snippet.

  const _NAMESPACE = 'FOO_LOGGING'; 
  const _LOGGER_CONTEXT = 'logger'; 

 class LoggingContext { 
 constructor(token, instance, level = 'debug') {
      this._loggerBuilder =
         aLogger()
           .withLogentriesToken(token)
           .withLevel(level);

      this._instance = instance;
   }

   runWithContext(aspects, fn, ...args) {
     try {
        const userSession = aspects['session'];
        const requestId = AspectsApi.getRequestId(aspects) || uuid();
        const context = {
            instance: this._instance || 'none',
            requestId,
            userId: userSession && userSession.userGuid
       };
         const logger = this._loggerBuilder.withContext(context);
          const session = createNamespace(_NAMESPACE);
          return session.runAndReturn(() => {
             session.set(_LOGGER_CONTEXT, logger);
             return fn(...args);
         });
         } catch (e) {
            this._loggerBuilder.build().error(e);
         }
         }

    static get() {
       try {
          const session = getNamespace(_NAMESPACE);
          const logger = session.get(_LOGGER_CONTEXT);
          if (logger) {
            return logger.build();
         }
      else {
         console.log('error retrieving logger in context');
      return console;
     }
   } catch (e) {
      if (global.infoLogger) {
        return global.infoLogger;
      } else {
         console.log('error retrieving logger in context:', e);
         return console;
     }
   }  }

context is lost on 'end' event of http request stream

Here is a sample snippet:

const http = require('http');
var createNamespace = require('cls-hooked').createNamespace;
var session = createNamespace('session-ns');


const server = http.createServer(function (request, response) {
    session.run(function (ctx) {
        session.set('key', 1)
        read(request, response, function (req, res) {
            process._rawDebug(`DONE - key: ${session.get('key')}`)
            res.end(`key: ${session.get('key')}\r\n`)
        })
    })
})

server.listen(8080)

function read(req, res, done) {
    let body = [];
    req
        .on('data', function (chunk) {
            body.push(chunk)
        })
        .on('end', function () {
            body = Buffer.concat(body).toString()
            process._rawDebug(`End - key: ${session.get('key')}`)
            done(req, res)
        });
}

As you can see I have created a http server and read the body by a stream. run the snippet then use curl.
curl http://localhost:8080
console output:

End - key: 1  
DONE - key: 1

for simple GET requests the context is correct, but when you send data in the body the context is lost.
curl http://localhost:8080 -X POST -d aaaaaaaaaa
console output:

End - key: undefined   
DONE - key: undefined

This issues is also related to this skonves/express-http-context#4. The problem is not the body-parser package, I have tracked it down and found that it is caused by the same issue as here, the callback of 'end' event looses context.

Node runtime: reproducible on all v8.14.0, v10.4.0, v10.3.0, and v10.14.1
OS: Ubuntu 18.04
cls-hooked: 4.2.2

Wrong metadata when a large number of requests take place

Hi, I have an app that connects to the MQTT, I want to publish 1200 devices with the id of each device as a metadata. the following is the code

"use-strict"
const RSVP = require('rsvp');
const Mqtt = require('mqtt');
const cls = require('cls-hooked');

const namespace = "firstName";
let clsNamespace;

let client = Mqtt.connect("alis://test.mosquitto.org");


if (!client) {
    logger.Error("Test", 'Init', 'No mqtt client provided');
    throw new extError('No mqtt client created');
}

client.on('connect', async () => {
    console.log("Connected");
    try {
        clsNamespace = cls.createNamespace(namespace);
        main();
    } catch (error) {
        console.log(error);
    }
});


function main() {
    var devices = [];
    for (var i = 0; i < 1200; i++) {
        devices.push({ "id": i });

    }
    RSVP.all(devices.map(async (item) => await updateDevice(item)));
}

async function updateDevice(device) {
    try {
        return await wrapContext(clsNamespace, async () => {
            setContext({ device: device.id });
            console.log("update " + device.id + " metadata =" + JSON.stringify(__getMetadata()));
            return publish("message", device.id);
        });
    } catch (error) {
        console.log(error);
    }

}

function setContext(context) {
    try {
        let ctxKeys = clsNamespace.get('contextKeys') ? clsNamespace.get('contextKeys') : [];
        for (const key in context) {
            clsNamespace.set(key, context[key]);
            if (ctxKeys.indexOf(key) === -1) {
                ctxKeys.push(key);
            }
        }
        clsNamespace.set('contextKeys', ctxKeys);
    } catch (error) {
        console.error(error);
        console.log('cannot set context', context);
        throw error;
    }
}

function publish(message, deviceId) {
    return new RSVP.Promise((resolve, reject) => {
        try {
            client.publish(message,
                deviceId,
                (error) => {
                    if (error) {
                        console.log("error")
                        reject(error);
                    } else {
                        console.log("publish " + deviceId + " metadata" + JSON.stringify(__getMetadata()));
                        resolve();
                    }
                });
        } catch (error) {
            console.log(error);
        }

    });
}

async function wrapContext(cls, callback) {
    let defer = RSVP.defer();
    let context = await cls.run(async (contextObj) => {
        try {
            let result = await callback(contextObj);
            defer.resolve(result);
        } catch (error) {
            defer.reject(error);
        }
    });
    return defer.promise;
};

function __getMetadata() {
    const metadata = {};
    let contextData = {};
    for (const key of clsNamespace.get('contextKeys') || []) {
        contextData[key] = clsNamespace.get(key);
    }

    for (const key in contextData) {
        metadata[key] = contextData[key];
    }
    return metadata;
}

the output is the following:

update 0 metadata ={"device":0}
publish 0 metadata{"device":0}
update 1 metadata ={"device":1}
publish 1 metadata{"device":1}
... (same thing for 1165 devices)
update 1166 metadata ={"device":1166}
update 1167 metadata ={"device":1167}
update 1168 metadata ={"device":1168}
update 1169 metadata ={"device":1169}
... (same thing until 1199)
update 1199 metadata ={"device":1199}
publish 1166 metadata{"device":1199}
publish 1167 metadata{"device":1199}
publish 1168 metadata{"device":1199}
... (same thing until 1199)

As you can see, the metadata is correct for the 1165 first publish log but once there's an interruption in the iteration and the function become asychnronous, the metadata of the first publish will be missmatched.

When debugging with DEBUG_CLS_HOOKED, I found out that after the 1169th update, the context becomes missing and then destroyed. thus, the currentUid changes and makes the 1166th publish gets the wrong context. (attached the debug logs)
TestDebugCls.log

Is there a way to fix this?

Context lost when awaiting on custom thenable

Not sure if this is an issue or expected behavior, but figured I'd ask. I've been tracking down a bug with mongoose and express-http-context, and found that if you use await with a custom then() function, context goes away. Here's an example:

const clsHooked = require('cls-hooked');

const session = clsHooked.createNamespace('mytest');

session.run(() => {
  run().catch(err => console.log(err));
});

async function run() {
  session.set('test', '42');

  const thenable = {
    then: (resolve, reject) => {
      console.log('Session', session.get('test')); // undefined
      return Promise.resolve('').then(resolve, reject);
    }
  };

  await thenable; // This is the problem, context gets lost in `then()`

  // This works fine
  await new Promise((resolve, reject) => {
    console.log('Session2', session.get('test')); // 42
  });
}

Any ideas as to what might be going on here?

Re: Automattic/mongoose#7292

Add node engine limit <8 for 4.x branch

  • Limit the current 4.x branch to node <8 that uses AsyncWrap.

  • cls-hooked version 5.beta will require node 8.1.2+

  • cls-hooked version 5+ will require node 8.2+

having issues on aws..

locally cls-hooked seems to be working fine. I have amiddleware:

``
app.use((req, res, next) => {

      //Create new CLS contect for each request
      //At this point CLS puts the new context in a map of contexts by current execution ID.
      session.run( () => {
        next();
      });
    })

I set some things further down in another middleware:

session.set("sub", req.auth.sub); if(req.headers['X-Amzn-Trace-Id']){ session.set('X-Amzn-Trace-Id', req.headers['X-Amzn-Trace-Id']); }

and I access it in a logging service:

const jsonFormatter = (logEntry) => {

  const session = getNamespace('session');
  
  const sub = session.get("sub");
  let json = (sub ) ? Object.assign({ sub: sub }, logEntry) : logEntry;
  
  const amazonTraceId = session.get('X-Amzn-Trace-Id')
  json = (amazonTraceId ) ? Object.assign({ 'X-Amzn-Trace-Id': amazonTraceId }, json) : json;

  logEntry[MESSAGE] = JSON.stringify(json); queotes etc
  return logEntry;

that works fine..

but on aws ecs/fargate I get an error on the session.get():

``
TypeError: Cannot read property 'get' of undefined

  | 2021-05-13T16:37:49.193+01:00 | at Format.jsonFormatter [as transform] (/usr/src/app/service/logging-service.js:25:23)
``

Share performance tests of using a async hooks?

Hi, great work you all done.
I am trying to figure out the performance implications of cls-hooks (which obviously depend on the stage of sync hooks in node) and before doing my own benchmark I wondered if anyone can share his\hers experience?

I’ve seen this benchmark (https://dzone.com/articles/beware-the-performance-cost-of-async-hooks-node-8) which was already mentioned in a different issue. Question is, are there any updates? Is it only relevant to bluebird or also native promises?

Would love to hear any feedback from anyone who tested or run it in production.

Thanks

Memory leak in Node 8.9.3

I'm using Node: 8.9.3, cls-hooked: ^4.2.2.

There's the memory snapshoot:
image

I found this:
image

async hooks promise not destroyed

Certainly,I update Node to version 8.15.0。No memory leak any more.

So, cls-hooked dependency, Node version should update to the right version instead of 8.2.1

Context lost in an unhandled exception (or rejection)

First of all - great module!

I was wondering if there's any workaround to get the context, in case of an unhandled exception (or rejection, in case of promises). Here's a sample code:

// cls.js
const cls = require('cls-hooked');
const clsNamespace = cls.createNamespace(process.getuid());

module.exports = {
  middleware: (req, res, next) => {
    run(() => {
      let someContextId = getTheIDSomehow();
      set('context-id', someContextId)
      next()
    }),
  ns: clsNamespace
}
// some-other-file.js
const ns = require('./cls').ns;

process.on('unhandledRejection', () => {
  // this will be undefined
  console.log(ns.get('context-id')); 
});

Context occasionally gets corrupted

Hello!

Trying to debug a pretty tricky issue. We use this library to thread a distributed traceId down to our logs and shared HTTP library across many many microservices. In rare cases, we're seeing logs and requests hop from one traceId to another. In essence, this code:

      const oldTrace = clsHooked.getNamespace('DEFAULT_NAMESPACE').get('context').traceId;
      const res = await httpClient.get(someUrl);
      const newTrace = clsHooked.getNamespace('DEFAULT_NAMESPACE').get('context').traceId;
      if(oldTrace !== newTrace) {
        console.log('~~ TRACE ID MISMATCH: ', oldTrace, newTrace);
        process.exit(1);
      }

Fails pretty consistently, and I'm trying to figure out why. I'm still grappling with how async_hooks and cls-hooked work in general, but my understanding is that things like older or non-standard promise libraries or custom thenables (as mentioned in #37) can cause this to happen.

Any advice on tracking down exactly what's happening?

Use AsyncHook API

Hi.

Are you planning to convert this module to use the new AsyncHook API in Node 8? If so, any guess as to when that might happen?

Many thanks!

async_hooks.executionAsyncId is not a function

I'm getting the following error when trying to use cls-hooked with Express 4 and Sequelize 4 on node 8.4.0:

TypeError: async_hooks.executionAsyncId is not a function
    at AsyncHook.init (/usr/src/app/node_modules/cls-hooked/context.js:291:32)
    at init (async_hooks.js:444:43)
    at emitInitS (async_hooks.js:314:3)
    at setupInit (internal/process/next_tick.js:225:7)
    at process.nextTick (internal/process/next_tick.js:247:5)
    at onwrite (_stream_writable.js:419:15)
    at Socket._writeGeneric (net.js:770:5)
    at Socket._write (net.js:780:8)
    at doWrite (_stream_writable.js:371:12)
    at writeOrBuffer (_stream_writable.js:357:5)

I have an express middleware that looks like this:

const { createNamespace } = require('cls-hooked')
const shardingNamespace = createNamespace('sharding')

// Patch bluebird to ensure cls context isn't lost
const clsBluebird = require('cls-bluebird')

const Promise = require('bluebird')
clsBluebird(shardingNamespace, Promise)

const sequelizePromise = require('sequelize/lib/promise')
clsBluebird(shardingNamespace, sequelizePromise)

module.exports = function (req, res, next) {
  shardingNamespace.bindEmitter(req)
  shardingNamespace.bindEmitter(res)

  shardingNamespace.run(function () {
    shardingNamespace.set('shard', 'default')
    next()
  })
}

One thing to note is that Sequelize creates its own instance of bluebird via const Promise = require('bluebird').getNewLibraryCopy();. I'm wondering if that's causing the issue. Putting a console.log(async_hooks.executionAsyncId) before context.js line 291 shows that most of the time it's a valid function, but occasionally it's undefined.

Any idea what might be causing this?

Decorate errors with non-enumerable properties

When the context is used, seems like all errors get a special error@context property with the current execution context attached.

See:

https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L101
https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L150
https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L175

One of the most common use cases for this module, is to expose the current http request object in the current execution context, so that the application can be traced.

However, if for some reason, an application prints the error object (e.g in an express error handler), with a method that inspects all enumerable properties, then the current request object may be printed, and that may be a problem for a couple of reasons:

  • Developers could be using JSON.stringify which by default throws when circular dependencies are found (request is usually an object with circular dependencies)
  • The request object may container sensitive information, (sessions identifiers, users identifiers, or other secret information that developers may attach to it)

Would it make sense to change those properties to be non enumerable by default?

Memory leak in `_contexts`

We upgraded to Node 8 yesterday, and are now seeing memory leaks. Taking some heap snapshots I can see the _contexts map growing all the time. We run 4.2.2, so it shouldn't be the memory leak fixed recently.

It's hard to follow the code, but I would guess this is related to how the promises are handled. At least it seems the contexts are not always removed from the map.

Migration from CLS to CLS-hooked time outs requests

I don't know how to describe this issue and hope that you'll give me some hints of what is going on. Any, really any hints are highly appreciated as I can't debug the app to give me any useful info whatsoever. (It seems like a limit in my skills. :)

Background

Since we are using async/await, in our logging lib we have migrated from CLS to CLS-hooked. It fixed the context and we could reach any values needed again. However, when running tests, they started to time-out and the overall app started to react strangely.

All the migration meant only replacement of const continuationStorage = require('continuation-local-storage') // v3.2.1 for const continuationStorage = require('cls-hooked') // v4.2.2.

Node Versions tested

We have tested against these versions:

  • Node 10.14.2
  • Node 11.2.0

How do we use CLS?

  1. This file takes care of context creation. An excerpt:
const continuationStorage = require('cls-hooked')

const createNamespace = continuationStorage.createNamespace
const getNamespace = continuationStorage.getNamespace

const NSP_REQUEST = 'logzio-node-debug-request'
const KEY_MDC = 'mdc'
const requestNamespace = createNamespace(NSP_REQUEST)

function createContext(next) {
  const mdc = getAll()
  const mdcCopy = Object.assign({}, mdc)
  requestNamespace.run(() => {
    requestNamespace.set(KEY_MDC, mdcCopy)
    next()
  })
}
  1. We create a new context for every incoming request. We're using socket.io
// MDC i
const MDC = require('logzio-node-debug').MDC 

socket.use((packet, next) => {
      MDC.createContext(() => {
        MDC.put('key', 'value')
        next()
      })
    })
  1. We also put other values during code execution:
const MDC = require('logzio-node-debug').MDC 
MDC.put('key2', 'value2')

Question

Is this a bug? Are we doing something wrong?

Not working in node v10.0.0

Test fails in node v10.0.0

Repro steps

  1. Install nodejs v10.0.0
  2. Run tests

context is being missed so tests breaks.

CLS Hooks with Node v10.0.0

I'm currently running into an issue where my previous method for handing-off information via CLS no longer works in Node v10.0.0 (although it did before):

const bindRequestContext = async (ctx, next) => new Promise(
    namespace.bind(async (resolve, reject) => {
        try {
            await next();
            resolve();
        } catch (err) {
            reject(err);
        }
    })
);

const appLoggingMiddleware = async (ctx, next) => bindRequestContext(ctx, async () => {
    const id = cuid();
    namespace.set("requestId", requestId);
    await next();
});

const userLoggingMiddleware = async (ctx, next) => bindRequestContext(ctx, async () => {
    const userId = ctx.getUserId();
    namespace.set("userId", userId);
    await next();
});

When I attach these to KOA routers, the second call fails saying that I need to run bind or run on the context first.

Not sure if this has anything to do with the async_hooks addition or not. I was unable to find a connection myself.

Any guidance would be appreciated.

Usage in prod - service logic should not rely on success of ns.run

Hi,
I want to use cls-hooked in my services that run in a prod env.
From what I saw in the Namespace.prototype.run source code, the callback I set to run when ns.run is finished will run only if the code in the try statement will not throw any errors.
This means that if by any chance there will be an error before my callback will be called, then my code won't run.

I am using this package in a way I believe many others are using it(I am running my service with express) - calling "ns.run(() =>{ next() })" in a middleware in the start of the chain and then use the namespace in later parts of the chain. I am using it to add a transaction id to all my logs.
If the call to ns.run would fail for any reason, I would of course prefer my code to run anyway and to just not have the transaction id in the logs of this specific session.
From what I understand from the code I read, if ns.run will fail then my code won't run for the specific api request.

I guess that if there will be a "ns.safeRun" or something of the kind that will act the same as ns.run but in the end will run my callback either way then this would solve my issue.
I believe that this is something that other people who will use this package will want.

Or, maybe there is a way to do what I wrote here and I just didn't figure it out.
What do you say?

Thank you!

Busy server hits RangeError

I've notice during some active payloads we are generating a large number of async promises which ends up filling up the context map here:

https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L316

This happens after the Map has 16777216 entries and it throws a RangeError when you try to insert another. I've confirmed the hooks are not leaking as they are in the node queue to be destroyed its just that garbage collection has not been called yet.....

Any thoughts on how to mitigate or more gracefully fall back?

Feature Request: Symbol support

I suggest cls-hooked starts supporting Symbols as namespace names and context keys.
It would have worked right now if there was no implicit conversion of Namespace.name and keys to string (https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L56 - ${this.name} or ${key}).
If we could wrap these calls in stringify

const strigify = (target: string | symbol) => typeof target === 'symbol' ? target.toString() : target

it would allow us using Symbols.

Maturity

For a developer who intends to use cls in production, how would you define the current maturity and reliability?

Why persist namespaces globally?

Hi, I'm struggling to understand the motivation for persisting namespaces globally (by setting process.namespaces) (covered by tests tests here), as opposed to keeping that state local to the library (say, by defining a namespaces object at the top of context.js).

Thanks,
Bruce

Memory Leak

It appears that the keys in the context map that are set, but never removed. In context.js, _contexts keys are set using namespace._contexts.set(asyncId, namespace.active); and deleted using namespace._contexts.delete(currentUid); where currentUid is the executionAsyncId, not the id passed into the async hook handler.

A sample of one of the crashes observed due to this issue is given below.

<--- Last few GCs --->

[1:0x5595a7ffd9c0] 19367966 ms: Scavenge 1154.7 (1414.3) -> 1141.3 (1414.3) MB, 17.0 / 0.5 ms  allocation failure 
[1:0x5595a7ffd9c0] 19370410 ms: Scavenge 1155.5 (1414.3) -> 1143.5 (1414.3) MB, 40.5 / 0.8 ms  allocation failure 
[1:0x5595a7ffd9c0] 19375363 ms: Scavenge 1157.7 (1414.3) -> 1145.2 (1414.3) MB, 19.9 / 0.5 ms  allocation failure 
[1:0x5595a7ffd9c0] 19375870 ms: Scavenge 1159.7 (1414.3) -> 1147.3 (1414.3) MB, 43.5 / 0.7 ms  allocation failure 


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x16617801bbd9 <JS Object>
    1: set [native collection.js:~247] [pc=0x661da70922b](this=0x4840a2843c1 <a Map with map 0x3dfb28a94319>,p=25597058,x=0x6c3ab1fb699 <an Object with map 0x7c2351f0fc1>)
    2: 0x1bf1c91c2ed9 <Symbol: init>(aka init) [/src/node_modules/cls-hooked/context.js:~290] [pc=0x661da7bfffd](this=0x1fd338a7e599 <an AsyncHook with map 0x3dfb28af8d91>,asyncId=25597058,type=0x166178074701 <String[7]: PROM...

FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory

fix: #10

[Question] Unit test an Express middleware using cls-hooked

Hello,

I'm trying to write a unit test to check if the value I set is well setted in my middleware.

A simplified version (the namespace 'mynamespace' is already created) of my middleware is:

module.exports = (req, res, next) => {
  ns.bindEmitter(req);
  ns.bindEmitter(res);

  ns.run(() => {
    const correlationId = uuidV4();
    getNamespace('mynamespace').set('correlationId', correlationId);
    next();
  });
};

My failing test is:

it.only('should set a correlationId in the uuid format in the request and the response', done => {
    const req = new Emitter();
    const res = new Emitter();
    const next = sinon.stub();

    correlationIdMiddleware(ns)(req, res, next);

    ns.run(() => {
        const correlationId = ns.get('correlationId');
        correlationId.should.satisfy(isUUID.v4);
        done();
    });
  });

It says that correlationId is undefined. My guess is that correlationId doesn't exist anymore when I check it in my unit test.

When I run my application I can see my middleware working.

Any idea? Thanks!

Doesn't work with Node 8.1.3

Node crashes with error:
../src/async-wrap.cc:349:void node::SetupHooks(const FunctionCallbackInfo<v8::Value> &): Assertion before_v->IsFunction()' failed.
1: node::Abort() [/usr/local/Cellar/node/8.1.3/bin/node]
2: node::MakeCallback(v8::Isolate*, v8::Localv8::Object, char const*, int, v8::Localv8::Value, double, double) [/usr/local/Cellar/node/8.1.3/bin/node]
3: node::SetupHooks(v8::FunctionCallbackInfov8::Value const&) [/usr/local/Cellar/node/8.1.3/bin/node]
4: v8::internal::FunctionCallbackArguments::Call(void (
)(v8::FunctionCallbackInfov8::Value const&)) [/usr/local/Cellar/node/8.1.3/bin/node]
5: v8::internal::MaybeHandlev8::internal::Object v8::internal::(anonymous namespace)::HandleApiCallHelper(v8::internal::Isolate*, v8::internal::Handlev8::internal::HeapObject, v8::internal::Handlev8::internal::HeapObject, v8::internal::Handlev8::internal::FunctionTemplateInfo, v8::internal::Handlev8::internal::Object, v8::internal::BuiltinArguments) [/usr/local/Cellar/node/8.1.3/bin/node]
6: v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) [/usr/local/Cellar/node/8.1.3/bin/node]
7: 0x3b4d1000437d
sh: line 1: 8851 Abort trap: 6 ts-node src/main
`

[Question] How do contexts get persisted?

Hello,

I've been trying to get this to work in combination with the aws-xray-sdk (for aws/aws-xray-sdk-node#12). It's my first time working with CLS and async hooks.

For some reason, my context is not present anymore when the destroy hook for that asyncId is called.

In this specific example, when entering the context, I have currentUid:146.

So, when setting a key in my namespace, for the context with the currentUid:146, that gets set in the active field of the namespace for that id (146).

Before exiting the context, another init hook gets called (150). At this point, namespace.active is still set to the context from 146, so we get into this case, where the current namespace.active with ID 146 gets saved in namespace._contexts with the asyncID of 150.

This context now gets deleted from namespace._contexts when 150 gets destroyed as per this, even though 146 has not been destroyed yet.

Here the debug log output (with some extra debug info I've added): log.txt

I'm fairly sure I'm just not understanding the code correctly, as this seems fairly obvious.

I have attempted a very basic patch in my fork here, to illustrate the above, but that is unlikely to be a fix (and comes with other issues..)

npm test failed on node 11

Here is the complete log:

[email protected] test /home/jo/.dev/cls-hooked
mocha test/.js & tap test/tap/.tap.js

cls simple async local context
✓ asynchronously propagating state with local-context

cls edges and regression testing
+
!
✓ minimized test case that caused #6011 patch to fail

cls with http Agent
when making two http requests
test/tap/async-context.tap.js ......................... 2/2
test/tap/async-no-run-queue-multiple.tap.js +
.!
.. ✓ should retain context during first (255ms)
✓ should retain context during second (65ms)

cls with http connections
client server
✓ server request event should be called
✓ server request event should receive data
✓ server request data event should be called
✓ server request data event should receive data
✓ client data event should be called
✓ client data event should receive data
✓ final context value should be 4919

multiple namespaces handles them correctly
Namespace {
name: 'ONE',
active: { _ns_name: 'ONE', id: 191 },
_set: [ null ],
id: 1,
_contexts:
Map {
191 => { _ns_name: 'ONE', id: 189, name: 'tom1' },
192 => { _ns_name: 'ONE', id: 191 } },
_indent: 0 } true
Namespace {
name: 'TWO',
active: { _ns_name: 'TWO', id: 189, name: 'paul2' },
_set: [ null ],
id: 1,
_contexts:
Map {
191 => { _ns_name: 'TWO', id: 189, name: 'paul2' },
192 => { _ns_name: 'TWO', id: 189, name: 'paul2' } },
_indent: 0 } true
✓ name tom1
✓ name paul2
✓ name bob
✓ name alice

cls namespace management
✓ name is required
✓ namespace is returned upon creation
✓ namespace lookup works
✓ allows resetting namespaces
✓ namespaces have been reset
✓ namespace is available from global
✓ destroying works
✓ namespace has been removed

cls with net connection
✓ value newContextValue
1) value newContextValue 2
✓ value MONKEY
✓ value MONKEY 2

cls with net connection 2
2) client server

Promise context convention
✓ convention should be 3

27 passing (381ms)
2 failing

  1. cls with net connection value newContextValue 2:
    AssertionError: expected undefined to exist
    at Assertion. (node_modules/chai/lib/chai/core/assertions.js:819:10)
    at Assertion.propertyGetter (node_modules/chai/lib/chai/utils/addProperty.js:62:29)
    at Object.get ()
    at Object.proxyGetter [as get] (node_modules/chai/lib/chai/utils/proxify.js:86:22)
    at Object.should.exist (node_modules/chai/lib/chai/interface/should.js:129:34)
    at Context.it (test/net-events.test.js:81:12)

  2. cls with net connection 2 client server:
    Uncaught AssertionError: state is still preserved: expected undefined to equal 4919
    at Proxy.assertEqual (node_modules/chai/lib/chai/core/assertions.js:1014:12)
    at Proxy.methodWrapper (node_modules/chai/lib/chai/utils/addMethod.js:57:25)
    at Socket.OnServerSocketData (test/net-events2.test.js:31:46)
    at Socket.clsBind (context.js:172:17)
    at Socket.emitted [as emit] (node_modules/emitter-listener/listener.js:122:21)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
    at Socket.Readable.push (_stream_readable.js:224:10)
    at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17)

test/tap/async-no-run-queue-multiple.tap.js ........... 3/3
test/tap/bind-emitter-multiple.tap.js ................. 6/6
test/tap/bind-emitter.tap.js ........................ 51/51
test/tap/bind.tap.js .................................. 6/6
test/tap/crypto.tap.js ................................ 3/3
test/tap/dns.tap.js ................................. 44/44
test/tap/error-handling.tap.js ...................... 17/17
test/tap/fs.tap.js .(node:9131) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
test/tap/fs.tap.js ................................ 174/175
continuation-local state with MakeCallback and fs module > fs.unlink
not ok Callback must be a function
stack: |
test/tap/fs.tap.js:471:22
at:
line: 133
column: 9
file: fs.js
function: maybeCallback
type: 'TypeError [ERR_INVALID_CALLBACK]'
test: fs.unlink

test/tap/interleave-contexts.tap.js ................. 10/10
test/tap/namespaces-multiple-values.tap.js ............ 4/4
test/tap/namespaces.tap.js ............................ 8/8
test/tap/nesting.tap.js ............................. 18/18
test/tap/net-events.tap.js ............................ 3/4
continuation-local state with net connection
not ok state is still preserved
+++ found
--- wanted
-"newContextValue"
+[null]
compare: ===
at:
line: 21
column: 13
file: test/tap/net-events.tap.js
type: Socket
stack: |
Socket. (test/tap/net-events.tap.js:21:13)
source: |
t.equal(namespace.get('test'), 'newContextValue', 'state is still preserved');

test/tap/promises.tap.js ............................ 16/16
test/tap/proper-exit.tap.js ........................... 0/1
Skipped: 1
proper exit on uncaughtException

test/tap/run-and-return.tap.js ........................ 7/7
test/tap/simple.tap.js ................................ 6/6
test/tap/timers.tap.js ................................ 8/8
test/tap/tracer-scenarios.tap.js .................... 71/71
test/tap/zlib.tap.js (node:9491) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
test/tap/zlib.tap.js .................................. 1/1
total ............................................. 458/461

458 passing (6s)
1 pending
2 failing

npm ERR! Test failed. See above for more details.

bindEmitter() does not work on certain EEs when there are other listeners

I found a bug in bindEmitter() which I eventually tracked down to the emitter-listener module. I've submitted a PR to fix it, but I'm unsure if it will ever get merged since the module seems unmaintained. (@othiym23, if you see this, say something).

I was wondering if you would consider importing the emitter-listener module (it's small) with the fix so that cls-hooked's bindEmitter() operates properly?

Thanks.

context is lost in async waterfall in hapi

i'm creating a package and use cls-hooked to keep the request in the context in hapi.js

1- i created the function to start context

let createNamespace = require('cls-hooked').createNamespace;
const ns = createNamespace('test');

bindCurrentNamespace(req, reply) {
    ns.bindEmitter(req);

    ns.run(() => {
        reply()
    });
}

2- another functions to set and get value

 setCurrentContext(req, reply) {
    ns.set('test', 'test');
    reply();
}

 getCurrentContext() {
    return ns.get('test');
 }

when i'm using these functions in async waterfall i lost the context in the second callback

async.waterfall(
    [
      function(cb) {
        getCurrentContext(); // return 'test'
        getTest(cb);
      },
      function(testData, cb1) {
       getCurrentContext(); // return undefined
       getTest2(testData, (err, data) => {
          callback(err, data);
        });
      }
    ],
    callback
  );

i tried to console the namespace to check if it keep the same namespace or not i checked it like that

let ns = getNamespace();
async.waterfall(
    [
      function(cb) {
        console.log(ns);
        getCurrentContext(); // return 'test'
        getTest(cb);
      },
      function(testData, cb1) {
       console.log(ns);
       getCurrentContext(); // return undefined
       getTest2(testData, (err, data) => {
          callback(err, data);
        });
      }
    ],
    callback
  );

what i found is:

  1. first log i got the ns like that
Namespace {
  name: 'request',
  active: { _ns_name: 'request', id: 1096, 'test': 'test' },
  _set: [ null ],
  id: -1,
  _contexts:
   Map {
     1120 => { _ns_name: 'request', id: 1096, 'test': 'test' },
     1121 => { _ns_name: 'request', id: 1096, 'test': 'test' }
   }
}
  1. second log i get the ns like that
Namespace {
  name: 'request',
  active: null,
  _set: [],
  id: -1,
  _contexts: Map {},
  _indent: 0 
}

i found namespace.exit(context); in line 412 in context.js file this line exit the context before the callback Done

but when i removed it all requests overwrite each other

anyone have idea how to fix this issue?

Can't get ctx when use await in async function.

image

Like the image above, when i tried to get the value in namespace after await one promise in a async function, i got undefined.
I guess it maybe due to the chain of function callback, in some aspect.
Can I reobtain values in the namespace after calling Await?

Context is preserved between two runPromise calls

Running the command in sequence preserves the context. I would expect that each call would create a clean context, like is the case with run

Is that normal behavior?

    await namespace.runPromise(async () => {
        set("foo", "bar");
        const foo = get("foo");
        console.log("VAELU", foo);
      })
     
    await namespace.runPromise(async () => {
      const foo = get("foo");
      console.log("Value", foo); // Here is prints "bar" instead of undefined
    });

[Memory leak] Context is not cleared if it references a Promise

Summary
If the context contains a key which references a Promise, then that context object is never cleared. Hence, the _contexts map keeps growing and hogs up all the heap memory.

Steps to reproduce

  1. Install express and cls-hooked.
  2. Run the script below, node --inspect index.js
const express = require('express')
const clsHooked = require('cls-hooked')
clsHooked.createNamespace('test')

const app = express()

app.get('/', (req, res, next) => {
    const namespace = clsHooked.getNamespace('test')
    
    console.log(namespace._contexts)

    namespace.run(() => {
        namespace.set('x', Promise.resolve())
        res.json({})
    })
})

app.listen(3030, () => console.log(('Listening')))
  1. Call the server api, curl localhost:3030
  2. [Optional] Manually trigger a garbage collection from the Chrome debugger.
  3. Repeat 3 and 4 and observe the logs. Notice that the _contexts keeps growing even after a manual garbage collection
node --inspect index.js 
Debugger listening on ws://127.0.0.1:9229/05303a92-c1ac-4cdb-816a-4b5e15de611a
For help, see: https://nodejs.org/en/docs/inspector
Listening
Debugger attached.
Map {}
Map {
  16 => { _ns_name: 'test', id: 11, x: Promise { undefined } },
  22 => { _ns_name: 'test', id: 11, x: Promise { undefined } } }
Map {
  16 => { _ns_name: 'test', id: 11, x: Promise { undefined } },
  39 => { _ns_name: 'test', id: 34, x: Promise { undefined } } }
Map {
  16 => { _ns_name: 'test', id: 11, x: Promise { undefined } },
  39 => { _ns_name: 'test', id: 34, x: Promise { undefined } },
  61 => { _ns_name: 'test', id: 56, x: Promise { undefined } } }
Map {
  16 => { _ns_name: 'test', id: 11, x: Promise { undefined } },
  39 => { _ns_name: 'test', id: 34, x: Promise { undefined } },
  61 => { _ns_name: 'test', id: 56, x: Promise { undefined } },
  84 => { _ns_name: 'test', id: 79, x: Promise { undefined } },
  90 => { _ns_name: 'test', id: 79, x: Promise { undefined } } }

If I change the set to namespace.set('x', '') (or any other primitive or object), context is cleared sometimes automatically after every request and always if a manual garbage collection is triggered.

Output with no manual GC.

node --inspect index.js 
Debugger listening on ws://127.0.0.1:9229/d39a47e8-72a2-4603-885a-ba0e3dbd8d53
For help, see: https://nodejs.org/en/docs/inspector
Listening
Map {}
Map { 21 => { _ns_name: 'test', id: 11, x: '' } }
Map {}
Map {}
Map {}

session lost when call promise-like function

Hi there,

I found that cls session was lost when execute promise-like(not really promise object, just have a then property) function ,see the below code example。

var createNamespace = require('cls-hooked').createNamespace;
var session = createNamespace('my session');
var async_hooks = require('async_hooks');

async function main () {
  const fake = new Fake()
  const result = await fake.query(233)    // not work, function end  session.get('user') is undefined
  // const result = await util.promisify(fake.query)(233) // work, session.get('user') is 'flag'
  // const result = await fake.query(233).then()  // work

  console.log('result ', result, '\n')
}

function Fake () {
  // empty
}

Fake.prototype.query = function (ct) {
  this.ct = ct
  console.log('session user :', session.get('user'))
  console.log('query asyncId :', async_hooks.executionAsyncId(), async_hooks.triggerAsyncId())
  console.log('set query ct', ct, '\n')
  return this
}

Fake.prototype.end = function (callback) {
  const self = this
  setTimeout(function () {
    console.log('session user :', session.get('user'))
    console.log('end asyncId :', async_hooks.executionAsyncId(), async_hooks.triggerAsyncId())
    console.log('do query ', self.ct, '\n')
    callback(self.ct)
  })
}

Fake.prototype.then = function then (resolve, reject) {
  const self = this
  let promise = new Promise(function (innerResolve) {
    self.end(innerResolve)
  })
  return promise.then(resolve, reject)
}

session.run(function () {
  session.set('user', 'flag')
  console.log('main asyncId :', async_hooks.executionAsyncId(), async_hooks.triggerAsyncId(), '\n')
  main().then()
})

change await fake.query(233) to await fake.query(233).then() seems can solve the problem,but I don't want to modify in all places, Is there other way to solve the problem?

superagent was support promise like the above code example, so when I try to trace seesion in superagent's http-client, I got this problem.
see https://github.com/visionmedia/superagent/blob/c2f65c665cf1738c5ed8f31c9d255f0a0afa70b2/lib/request-base.js#L231

No context available - how to use? Node 8.12.0 (and Typescript 3.3)

Hi,

I'm trying to get a simple example working, but keep getting an error No context available. ns.run() or ns.bind() must be called first. This is locally in unit tests and deployed.

I've written a test that fails:

const createNamespace = require('cls-hooked').createNamespace
const getNamespace = require('cls-hooked').getNamespace

const sessionName = 'cats'

const namespace = createNamespace(sessionName)
console.log('namespace', namespace)
namespace.set('a', 'value')

describe("logger", () => {
  it("should log values passed into init", () => {
    const session = getNamespace(sessionName)
    const baseObject = session.get('a')
  })
})

the error i'm getting is:

    No context available. ns.run() or ns.bind() must be called first.

       7 | const namespace = createNamespace(sessionName)
       8 | console.log('namespace', namespace)
    >  9 | namespace.set('a', 'value')
         |           ^

and the logged out namespace:

    namespace Namespace {
      name: 'cats',
      active: null,
      _set: [],
      id: -1,
      _contexts: Map {},
      _indent: 0 }

I thought that i was following the example :). I did have a look at context.js, but couldn't see what I was supposed to do - any help would be greatly appreciated.

Sam

Cls with library using bluebird (knex)

Hey guys,
thanks a lot for this lib, I managed to get it running on node 8.6.0 with express and async/await. However, I would like to ask if it is possible to use it with other library that is using Bluebird promises - knexjs.org. My ultimate goal is to wrap knex.transaction with my own function that is going to set the transaction to namespace so the transaction can be accessed from other places.

I tried something with cls-bluebird or using ns.run or ns.bind without any success. It fails on Unhandled rejection Error: No context available. ns.run() or ns.bind() must be called first.. Accessing the namespace out of knex.transaction() works fine.

Thanks for any kind of help!

Problem using Promises (Bluebird)

I have noticed that while using Promises (bluebird) that sometimes I loose the context and cannot get my stored variables. I also noticed that async-listener addressed something like this by adding its own monkey patch to native promises. Have you experienced any issues using this library with promises?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.