6

My use case requires node.js domains to share information across server files at a request level.

Sample Implementation in express.js

domain = require('domain');

app.use(function(req, res, next) {
    var reqDomain = domain.create();
    reqDomain.add(req);
    reqDomain.add(res);
    reqDomain.run(next);
});

More explanation at Nodejs Domains Explicit Binding

In controller / service - process.domain will provide you above created domain And you can easily bind values to this domain. For eg:

process.domain.obj = {};

This explanation is sufficient to understand the usage of Domains.

Questions

  1. Is it safe to use domains for multiple requests ?

  2. How to ensure process.domain is different for different requests and not the same ?

I would also like to know how such issues are handled in continuation local storage

keshav
  • 734
  • 6
  • 19

1 Answers1

5

A warning

First of all - domains are deprecated and will be removed in an upcoming release of NodeJS. I would not write new code using them.

How domains work?

Second of all - it's important to understand domains are not magic. They're really a simple concept. Basically, they:

  • Wrap every async call in the platform (the entire NodeJS APIs).
  • Set a "global" variable for the duration of the call.
  • If another async call is being made during that call - keep note to set the global variable to the same value when you enter it.
  • That's it.

Here's how one can implement domains, let's only implement it for setTimeout for simplicity.

const oldTimeout = setTimeout;
setTimeout = function(fn, ms) { // also ...args, but let's ignore that
    var trackedDomain = domain;
    oldTimeout(function() { 
      var oldDomain = domain; // preserve old context
      domain = trackedDomain; // restore context to "global" variable
      fn(); // call the function itself
      domain = oldDomain; // restore old context
    }, ms); // that's it!
};

Something like express can just do domain = new RequestContext at the start and then all methods called in the request would work since they're all wrapped like in the example above (since again, it's baked into node itself).

That sounds swell, why remove them?

They're being removed because of the implementation complexity they add and the fact they're leaky and error recovery has edge cases where it doesn't work.

So what do I do?

You have alternatives, for example bluebird promises have .bind which brings promise chains context which is a less leaky way to do this.

That said, I would just avoid implicit global context altogether. It makes refactoring hard, it makes dependencies implicit and it makes code harder to reason about. I'd just pass relevant context around to objects when I create them (dependency injection, in a nutshell) rather than set a global variable.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Thanks for this wonderful explanation, I m aware that domains will be deprecated soon but my use case requires some thread local like structure. Anyways i will try exploring bluebird bind to fit in my use case. Hope to see better alternative to domains in upcoming node.js releases – keshav Dec 19 '15 at 15:06
  • 2
    @keshav to be 100% frank, I don't really see a viable alternative emerging soon. – Benjamin Gruenbaum Dec 19 '15 at 15:11
  • Oh! what option do i left with then. Lets work with bluebird bind, thanks – keshav Dec 19 '15 at 15:13
  • @keshav passing things explicitly, in the long run - it pays off since code is read a lot more than it is written. Implicit global (or thread) state makes refactoring harder. I wish I had better news, but honestly I've read all the discussions and nothing really good has emerged. "Purity" and code cleanness aside - I've found domains to be buggy and introduce nontrivial bugs - that's the #1 reason I avoid them. – Benjamin Gruenbaum Dec 19 '15 at 15:15
  • Using Domains in Production in 2018: https://medium.com/@the1mills/using-node-js-domains-in-production-797105a4c302 – Alexander Mills Mar 27 '18 at 22:30
  • 1
    @AlexanderMills please don't do that :) – Benjamin Gruenbaum Mar 28 '18 at 08:03
  • I knew that might torture you lol sorry, I know you love promises and hate domains :) – Alexander Mills Mar 28 '18 at 08:52
  • 1
    @AlexanderMills I don't hate domains, but they _are_ subtly broken in a way that can break your code unexpectedly. Core _does not guarantee_ that Node recovers from caught domain errors (but we guarantee it for async/await and promises). This means your code can break in subtle ways leading to data corruption, leaked connections etc. If you do this all bets are off in terms of what we guarantee - which is why I advise against it. I also recommend reading trevnorris's excellent domains postmortem. – Benjamin Gruenbaum Mar 28 '18 at 12:40
  • Yeah I think the way I am using domains, there's is no "recovery", you either pin the error to a request/response or you don't. The only risk is pinning an error to the wrong request/response, which would happen if there is a bug (inherent problem) with domains. But in my testing I havent seen that happen yet. – Alexander Mills Mar 28 '18 at 17:23
  • @BenjaminGruenbaum say you were too create a domain for each request in a node server, if it is 'thread state' how come that state isn't leaked between to simultaneous async requests? Is it creating a new thread? – C Deuter Feb 23 '19 at 02:16