Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Implicit asynchronous context #3733

Closed
eladb opened this issue Jul 18, 2012 · 22 comments
Closed

Implicit asynchronous context #3733

eladb opened this issue Jul 18, 2012 · 22 comments

Comments

@eladb
Copy link

eladb commented Jul 18, 2012

In continuation to https://groups.google.com/d/topic/nodejs-dev/gBpJeQr0fWM/discussion

Most environments provide some form of ability to push implicit information into the execution context. In multithreaded environments, it is usually some form of storage associated with the current thread and which can be accessed by code anywhere in the program. See Thread Local Storage for details.

This mechanism enables use cases where it is impossible to pass along data across a call chain from one layer to another in the execution flow (consider the use of many libraries throughout the chain). Common examples are instrumentation and debugging tools like logging and performance counters that need to be associated with the current flow but are usually implemented in a central location within the codebase. For example, one might want to automatically associate a request ID with each emitted log line. The request ID will be added to the execution context when the request comes in and the logging library will extract it and emit it with the log line.

I think that node should enable those scenarios by providing a mechanism to associate data implicitly into the current execution context and extract them along the way. Because of the asynchronous nature of node, this data should traverse async hops in a similar fashion to Domains.

@isaacs
Copy link

isaacs commented Jul 18, 2012

One way to address this would be to bless the use of process.domain as the active domain object, and maybe give it a "data" object member that is a general-purpose bag-o-stuff for you to use.

@stonecobra
Copy link

data object member bag would be the most flexible, and usable, especially if users of the data member utilized their own name as a high-level key to 'their' own data bag. Example, process.domain.data.express, process.domain.data.haraka, or process.domain.data.passport

@eladb
Copy link
Author

eladb commented Jul 19, 2012

A caveat to consider is that domains can be arbitrarily nested. process.domain points to the current domain, which is not necessarily the domain that contains the context that you are looking for:

var domain = require('domain');

var d1 = domain.create();
d1.c1 = 'hello';

d1.run(function() {
  var d2 = domain.create();
  d2.c2 = 'world';
  d2.run(function() {
    console.log('c1:', process.domain.c1);
    console.log('c2:', process.domain.c2);
  });
});

Output:

c1: undefined
c2: world

A possible solution could be to automatically link data to the parent domain's data upon creation:

var domain = require('domain');

var _create = domain.create;

domain.create = function() {
  var d = _create.apply(domain, arguments);
  var parent_data = (process.domain && process.domain.data) || {};
  d.data = Object.create(parent_data);
  return d;
};

var d1 = domain.create();
d1.data.c1 = 'hello';

d1.run(function() {
  var d2 = domain.create();
  d2.data.c2 = 'world';

  d2.run(function() {
    console.log('c1:', process.domain.data.c1);
    console.log('c2:', process.domain.data.c2);
  });
});

Output:

c1: hello
c2: world

An added value of this approach is that overriding a data attribute of a child domain does not affect code that runs within the parent domain. Love javascript!

@eladb
Copy link
Author

eladb commented Sep 2, 2012

@isaacs What do you think about the above suggestion?

@rlidwka
Copy link

rlidwka commented Oct 14, 2012

So, is it guaranteed somehow that using process.domain.data in userland won't conflict with future node.js versions?

By the way, what is the preferred way to access current active domain? Is it process.domain or require('domain').active? Node.js core uses both ways: process.domain in timers.js and domain.active in events.js. Documentation don't mention neither one.

@langpavel
Copy link

So, is it guaranteed somehow that using process.domain.data in userland won't conflict with future node.js versions?

I thing no at the present time: Stability: 1 - Experimental

By the way, what is the preferred way to access current active domain? Is it process.domain or require('domain').active? Node.js core uses both ways: process.domain in timers.js and domain.active in events.js. Documentation don't mention neither one.

Both are fundamentals, this is question for Isaacs or @bnoordhuis, but:

What about prototype chaining? Can be efficiently used here (nesting execution context)?

@chuggins
Copy link

+1...support for this would be a huge win for any large scale Node application. A few features we desperately need on my team but can't provide:

  1. A logging feature that includes a unique request ID for all log statements, for easy debugging
  2. Instrumentation--for instance, for a given request how many SQL statements were executed? How long did it spend waiting on the database? On Facebook?

Without having some sort of request-scoped context, you end up having to pass the request or some sort of context object around to every single piece of code you write in order to support something like this.

@othiym23
Copy link

👍

For obvious reasons, this would be hugely useful for New Relic as well. FWIW, my work with domains leads me to believe that they're an excellent means for handling errors in end-user applications, but a poor means for creating context (and not that hot for weaving error tracing into module code, either).

In particular, the requirements that error-handling imposes on domains makes them inappropriate as a means of propagating transactional state. It's very difficult to write instrumentation that uses domains without monkeypatching the domains module and some of its clients if you want user code to run the same whether or not it's using the instrumentation. This can lead to lots of deoptimization and / or slowing down some of the hottest code paths in Node. Something that either generalized domains or provided an alternate path through Node core for transactional state would be a huge win.

@evadnoob
Copy link

Another +1 for this. I've been attempting to use process.domain for
context, and I have just had so much trouble getting process.domain to act
like "thread local storage" that I've given up on using it in favor of
"passing request scoped data" through to every object/method/function call.

In my opinion, having come from several languages that support thread local
storage, I miss it. There are use cases, like distributed transaction
coordination, that require quietly injecting some state that can be
retrieved later. Logging is another good example, having something like
"nested diagnostic context" is helpful.

-Dave

On Wed, Mar 13, 2013 at 9:02 PM, Forrest L Norvell <notifications@github.com

wrote:

[image: 👍]

For obvious reasons, this would be hugely useful for New Relic as well.
FWIW, my work with domains leads me to believe that they're an excellent
means for handling errors in end-user applications, but a poor means for
creating context (and not that hot for weaving error tracing into module
code, either).

In particular, the requirements that error-handling imposes on domains
makes them inappropriate as a means of propagating transactional state.
It's very difficult to write instrumentation that uses domains without
monkeypatching the domains module and some of its clients if you want user
code to run the same whether or not it's using the instrumentation. This
can lead to lots of deoptimization and / or slowing down some of the
hottest code paths in Node. Something that either generalized domains or
provided an alternate path through Node core for transactional state would
be a huge win.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3733#issuecomment-14881025
.

@alaendle
Copy link

alaendle commented Apr 5, 2013

Another +1. A "context" is definitely something that is missing - and even if it doesn't fit 100% into the node.js concept - real world is dirty and not all 3rd-party code allows us to retrieve something like a context objects in the callbacks. Also I'm not sure if domain is really the right place to implement this feature...

@gsilk
Copy link

gsilk commented Jul 8, 2013

@eladb In a multi-threaded program, thread-local storage provides a mechanism for storing context per thread. How would you define an implicit execution context in a single-threaded node app, without support from v8 ...?

@eladb
Copy link
Author

eladb commented Jul 8, 2013

@gsilk You need some low level support in order to implement something like that. Node already supports domains which are a form of implicit context and this is an example for using it in order to implement async context for async chains. Read above - there is an interesting discussion with several pull requests on the topic.

@manuelsantillan
Copy link

Absolute +1 for this. Real-world deployments need it: logging, transactions, instrumentation, security audits, ... It's not about deployment size, any non-trivial customer-facing app would benefit from it.

@vkurchatkin
Copy link

@manuelsantillan it's already possible with async listeners. Check https://github.com/othiym23/node-continuation-local-storage out

@manuelsantillan
Copy link

Thnx!! I was looking at it right now, gonna try it :-D

2013/12/15 Vladimir Kurchatkin notifications@github.com

@manuelsantillan https://github.com/manuelsantillan it's already
possible with async listeners. Check
https://github.com/othiym23/node-continuation-local-storage out


Reply to this email directly or view it on GitHubhttps://github.com//issues/3733#issuecomment-30605810
.

Manuel Santillán
Socio - Director de Operaciones
www.solvver.com

@trevnorris
Copy link

Just a quick warning. My first implementation missed a few things that seem
to be more necessary than I expected. Creating a completely generic way of
working with asynchronous transactions hasn't been easy. So, I'm in the
process of rewriting it now. The API will have a slight modification and
will allow more fine grain control over what you track. Right now it's an
all or nothing (including Node core level transactions).

@jasnell
Copy link
Member

jasnell commented Jun 24, 2015

@trevnorris ... any further thoughts or action on this one?

@trevnorris
Copy link

Nope. A large part of that implementation was removed, and there is now a cls shim in npm.

@jasnell
Copy link
Member

jasnell commented Jun 24, 2015

Ok. I'm leaning towards just closing this one then. If someone wishes to pursue, we can reopen if necessary.

@robertleeplummerjr
Copy link

Can we get a reference to this "cls shim in npm"? Is this the answer for this issue?

@ghost
Copy link

ghost commented Aug 10, 2015

@jasnell
Copy link
Member

jasnell commented Aug 26, 2015

given the current state of this, closing here. If someone wishes to pursue this further, it would need to be done in http://github.com/nodejs/node

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests