New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecate domains #66

Closed
jonathanong opened this Issue Dec 4, 2014 · 91 comments

Comments

Projects
None yet
@jonathanong
Copy link
Contributor

jonathanong commented Dec 4, 2014

what's the status on domains? can we deprecate it before v1?

@juliangruber

This comment has been minimized.

Copy link
Contributor

juliangruber commented Dec 4, 2014

afaik no breaking changes will happen until v1

@jonathanong

This comment has been minimized.

Copy link
Contributor

jonathanong commented Dec 4, 2014

just labeling something as deprecated is not a breaking change

@juliangruber

This comment has been minimized.

Copy link
Contributor

juliangruber commented Dec 4, 2014

gotcha, 👍 from me, domains have only meant pain to me personally

@rvagg rvagg added the tc-agenda label Dec 4, 2014

@rvagg

This comment has been minimized.

Copy link
Member

rvagg commented Dec 4, 2014

I'm on board, I bet a bunch of TC members would be too, marking as tc-agenda

@indutny

This comment has been minimized.

Copy link
Member

indutny commented Dec 4, 2014

+1

@Marak

This comment has been minimized.

Copy link

Marak commented Dec 4, 2014

I never understood why domains were added to core.

It seemed wrong at the time, and still seems equally as wrong.

For backwards compatibility, will it be possible to put this functionality into an npm module as a custom add-on? Does this already exist?

@Fishrock123

This comment has been minimized.

Copy link
Member

Fishrock123 commented Dec 4, 2014

+1 domains are confusing and I hear nothing but incompatibilities and edge cases

@othiym23

This comment has been minimized.

Copy link
Contributor

othiym23 commented Dec 4, 2014

+1 sad 🎺

@Marak

I never understood why domains were added to core.

There's a place for unified sync & async error handling for Node, and due to the way that state propagates through core, there at least need to be hooks exposed by core to facilitate this. uncaughtException is too gross-grained, not everybody uses (or likes) promises, try/catch + stream error listeners + callback convention is complicated to teach and learn and harder to master, etc. Probably the best effort at this so far is @piscisaureus and co's zones, but that solution has limitations that are going to be tough to resolve outside core.

I'm hopeful that @trevnorris's AsyncWrap can provide the foundation for a better / lower-impact way of building something useful along these lines in the future, and I consider domains a very useful failure that helped point to what AsyncWrap / AsyncListener should do.

@bnoordhuis

This comment has been minimized.

Copy link
Member

bnoordhuis commented Dec 4, 2014

It would have to go through the TC but I don't think there is much opposition to deprecating domains. Who volunteers to follow up with a pull request?

@tellnes

This comment has been minimized.

Copy link
Member

tellnes commented Dec 4, 2014

@bnoordhuis A deprecation warning if you call domain.create()? Or when you require it since it has side effects (acording to code comments).

@bnoordhuis

This comment has been minimized.

Copy link
Member

bnoordhuis commented Dec 4, 2014

@tellnes I'll leave that to your own judgment. Doing it at require() time would get the message out most effectively but it might end up being extremely annoying.

If you do do it at require() time, you should also update lib/repl.js and make it load the domain module lazily.

@tellnes

This comment has been minimized.

Copy link
Member

tellnes commented Dec 5, 2014

lib/repl.js needs to be changed anyway because it is calling domain.create().

@rlidwka

This comment has been minimized.

Copy link
Contributor

rlidwka commented Dec 5, 2014

Is there a suitable replacement that could be used? Repl is a valid use-case for example.

@tellnes tellnes referenced this issue Dec 5, 2014

Closed

Deprecate domains #75

@tellnes

This comment has been minimized.

Copy link
Member

tellnes commented Dec 5, 2014

@rlidwka Yes, the new tracing module in core. Please see my implementation in #75.

@jodytate

This comment has been minimized.

Copy link

jodytate commented Dec 5, 2014

+1 what @Marak said and +1 @Fishrock123 said, so +2

@jmcbee

This comment has been minimized.

Copy link

jmcbee commented Dec 6, 2014

What is TC? Only TechCrunch is going in my mind.

@SomeoneWeird

This comment has been minimized.

Copy link
Member

SomeoneWeird commented Dec 6, 2014

@benjamingr

This comment has been minimized.

Copy link
Member

benjamingr commented Dec 6, 2014

+1 domains have only brought me pain and suffering in my code and were not good enough in practice. I'd love to see them removed in v1 and deprecated today.

@caineio caineio added needs info and removed tc-agenda labels Dec 8, 2014

@indutny indutny added tc-agenda and removed need info labels Dec 8, 2014

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented May 4, 2016

100% agree with that assessment. As a test framework developer where user
code calls the framework api, domains are an imperative. In the case of
test framework, errors/exceptions are supposed to happen and be handled
without explicit use of try/catch.

On May 4, 2016 6:13 AM, "Jonathan Gros-Dubois" notifications@github.com
wrote:

Domains are useful for frameworks - They're a good way to separate 'user
logic' from internal 'framework logic' for the purposes of failure handling
and recovery.

Note that 'failure handling' is different from 'error handling' - A
failure is a side effect to an Error - The user of a framework should be
allowed to handle errors however they like, but sometimes you want the
framework to decide how to handle the subsequent failure which resulted
from that error.

For example, the framework itself might invoke a user-defined function
(for example, middleware) which might branch off (in unpredictable ways)
and invoke various functions synchronously and/or asynchronously - In such
a situation, if the user's code throws an Error, you may not want to just
kill the whole server (especially when it comes to WebSockets because then
you would end up killing tens of thousands of perfectly good connections
along with it) - You just want to shut down the single socket which was
related to the specific action which caused the problem.

Basically, as a framework developer, domains offer you the ability to
control the failure handling (and recovery) aspects related to code written
by a third party.

A big practical problem for example is if there is a logic error in user
code - For example, if they try to invoke a function which doesn't exist -
You DO NOT want the server to crash (and take down all other sockets) - You
just want to kill a single socket. (That would classify as a security
vulnerability).


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#66 (comment)

@jondubois

This comment has been minimized.

Copy link

jondubois commented May 7, 2016

The other really nice feature which domains offer is the ability to listen to the 'error' event on an EventEmitter without explicitly binding a listener to its 'error' event. E.g. using domain.add(emitter).

This relates to a common use-case for frameworks - You don't want to allow the user to accidentally unbind 'error' handlers which were setup by the framework for its own internal use (e.g. if the user calls eventEmitter.removeAllListeners()) - Because that would mess everything up internally (the user should not be able to unbind error handlers which they did not attach themselves).

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented May 13, 2016

does anyone know if there is indeed an effort to patch native ES6 promises to work with domains? Will that land in a Node version soon?

@trevnorris

This comment has been minimized.

Copy link
Contributor

trevnorris commented May 13, 2016

@ORESoftware Either we would need to overwrite native Promise, or use a V8 provided API to propagate the information. The later is slow going and won't happen soon, and doubt the CTC would be willing to patch Promise directly.

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented May 13, 2016

@trevnorris ok thanks for that info.

@jondubois

This comment has been minimized.

Copy link

jondubois commented Jul 21, 2016

@trevnorris

Instead of messing with native Promises, couldn't we just rewrite the domain module itself to leverage Promises instead? It looks like doing this would allow it to automatically work with nested Promises and also with async operations like setInterval, nextTick etc...

So for example, the domain.run(fn) method could just be:

Domain.prototype.run = function (callback) {
  var self = this;

  (new Promise(function (resolve, reject) {
    var args = Array.prototype.slice.call(arguments, 1);
    callback.apply(self, args);
  })).catch((err) => {
    this.emit('error', err);
  });
};

I tried this in a number of async scenarios and it seems to behave the same as with the Node.js domain module's run(fn) method - It's actually better because unlike the domain module, this doesn't pollute Error objects with error-domain-related properties... The promises do everything magically and capture any async errors.

I guess it doesn't capture emitted 'error' events though...

@sosz

This comment has been minimized.

Copy link

sosz commented Jul 23, 2016

I haven't read the whole thread, so sorry if I'm repeating things.

I want to say that I've recently been using domains to capture all calls to console.log, console.warn, etc. It is not a simple matter of monkey-patching these methods, since I also need to separate each call to its source (which is a unique context defined by my application).

So, I'm not using domains for handling errors at all. I don't even register an error event listener on my domains. I'm using domains as a way of execution context management.

This feature is necessary for my application to work, and so I hope that any replacement API to Domain will be able to cover my use-case...

BTW, I would be glad to know if there's a better way to do what I'm trying to do, other than abusing domains.

EDIT
I just wanted to add that it's not possible for me to improve my application to keep track of context manually, as my application keeps track of third-party user-code.


N.B.
I've also encountered the issues with native Promises. In my case, I fixed it by monkey-patching Promise (even though it might have a big performance hit):

function deferResult(fn, ...args) {
    let hasError = false;
    let error;
    let result;

    try {
        result = fn(...args);
    } catch (err) {
        hasError = true;
        error = err;
    }

    return () => {
        if (hasError) { throw error; }
        return result;
    };
}

const originalThen = Promise.prototype.then;
Promise.prototype.then = function (onFulfilled, onRejected) {
    const activeDomain = domain.active;

    let onF = onFulfilled;
    if (activeDomain && typeof onFulfilled === 'function') {
        onF = function (value) {
            activeDomain.enter();
            const deferredResult = deferResult(onFulfilled, value);
            activeDomain.exit();
            return deferredResult();
        };
    }

    let onR = onRejected;
    if (activeDomain && typeof onRejected === 'function') {
        onR = function (reason) {
            activeDomain.enter();
            const deferredResult = deferResult(onRejected, reason);
            activeDomain.exit();
            return deferredResult();
        };
    }

    Reflect.apply(originalThen, this, [onF, onR]);
};
@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented Aug 4, 2017

@sosz please search and read the whole thread before, I patched promises to use domains myself, in a much simpler way

Your code is a little bit of a horror show. What about this instead?

const then = Promise.prototype.then;
Promise.prototype.then = function (fn1, fn2) {

  if (process.domain) {
    fn1 = fn1 && process.domain.bind(fn1);
    fn2 = fn2 && process.domain.bind(fn2);
  }

  return then.call(this, fn1, fn2);
};

Note that you do not need to patch the Promise constructor, because the executors run synchronously, but you can patch Promise.prototype.catch to work in the same way as the above patch on Promise.prototype.then. That being said, pretty certain Node version 8 has allowed domains to work with Promises, so the above patch may only be useful for Node.js versions < 8.

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented Aug 4, 2017

I just want to add my 2 cents to the long run discussion. I have a job. I want to keep my job. I really prefer to not have my server(s) crash, even if we use forever or supervisor to restart upon crashes.

Using the following allows us to avoid many crashes:

let domain = require('domain');

app.use(function(req, res, next){

  let d = domain.create(); // create a new domain for this request

  res.once('finish', function(){
    d.exit();
    d.removeAllListeners();
  });

  d.once('error', function(e){
    // log the issue right here
    if(!res.headersSent){
        res.json({
          error: e.stack || e
       });
    }
  });

  d.run(next);   // we invoke the next middleware
});

we use domains in production. Hopefully no memory leaks though 😂

@evanlucas

This comment has been minimized.

Copy link
Member

evanlucas commented Aug 8, 2017

@ORESoftware I've moderated your previous comment. I found it to be inappropriate for this issue tracker. We intend on keeping this issue tracker professional and would appreciate if everyone who posts here does the same.

@nickmccurdy

This comment has been minimized.

Copy link

nickmccurdy commented Aug 8, 2017

Domains are still a hack, letting the process crash is often the better option especially with forever/supervisor.

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented Aug 8, 2017

@evanlucas censorship always appreciated, but I guess you're right, best to keep politics off of Github lol...@nickmmcurdy - you'd have to back up that statement - domains will fail in their intention if an error is raised for the wrong path of execution, and if it's easy to misuse them than that's also bad. But I don't think they are a hack perse, just perhaps imperfectly implemented.

@addaleax

This comment has been minimized.

Copy link
Member

addaleax commented Aug 8, 2017

@ORESoftware We have a Code of Conduct that asks you to be respectful of other people’s viewpoints and refrain from using language that “could reasonably be considered inappropriate in a professional setting”.

If you don’t feel comfortable with that, it might be easier to take a step back; Node’s project leadership is standing behind the choice to apply that CoC to our spaces.

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented Aug 8, 2017

Imma read the CoC right now to see if my microaggression actually violated it.

@ORESoftware

This comment has been minimized.

Copy link
Contributor

ORESoftware commented Aug 8, 2017

Yep, looks like I violated point 1

  • Using welcoming and inclusive language

whoops, my bad fam

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment