Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.


Domains were added to Node.js in 0.8, but their use and workings have been a relative mystery. In short, domains are a structured way of reacting to uncaught exceptions; for example, when creating an HTTP server, you can use domains to send 500 errors when exceptions occur instead of crashing your server. This talk will go over what domains are, how to use them, and some of the subtleties behind how they work.

  • Login to see the comments


  1. 1. Domains!BY @DOMENIC
  2. 2. Domenic Denicola•••• I’m doing:• @esdiscuss onTwitter• The Promises/A+ spec• Client-Side Packages talk@DOMENIC
  3. 3. Let’s talk about errors in@DOMENIC• Errors passed to a callback: beAsync(function (err, result) { … })• Errors emitted from an event emitter: emitter.emit(error, err)• Errors thrown “intentionally”: throw err• Errors thrown “accidentally”: JSON.parse(badUserInput)
  5. 5. Answer: yes!!@DOMENIC• Errors passed to a callback: beAsync(function (err, result) { … })• Errors emitted from an event emitter: emitter.emit(error, err)• Errors thrown “intentionally”: throw err• Errors thrown “accidentally”: JSON.parse(badUserInput)SERVER GO CRASH
  7. 7. Before domains: process.on(uncaughtException, …)• Instead of crashing, you could listen for “about to crash,” and dosomething about it.• But: how do you keep track of what caused the crash?• Concretely: how can you return a 500 error to the right request?• Domains are supposed to help with this… somehow.@DOMENIC
  8. 8. Build your own domains: uncaught throws• Before initiating some asynchronous work:• process.pseudoDomain = new EventEmitter();• After initiating the async work:• process.pseudoDomain = null;• Inside your uncaughtException handler:• if (process.pseudoDomain) process.pseudoDomain.emit(error, err);• Now, in the pseudo-domain’s error handler, you know the cause!@DOMENIC
  9. 9. Build your own domains: unheard errors• When creating an event emitter:• ee.pseudoDomain = new EventEmitter();• ee.on(error, function (err) {if (EventEmitter.listenerCount(this, error) === 1) {this.pseudoDomain.emit(error, err);}});• Now, in the pseudo-domain’s error handler, you know the cause!@DOMENIC
  10. 10. Build your own domains: callback err params• function bindToDomain(domain, cb) {return function (err, result) {if (err) return domain.emit(error, err);cb(null, result);};}• Every time you use a callback:• var pseudoDomain = new EventEmitter();• doAsyncThing(bindToDomain(pseudoDomain, function (result) { … });• Now, in the pseudo-domain’s error handler, you know the cause!@DOMENIC
  11. 11. The key feature: automatic aggregation• This is pretty useless if you have to create a domain for every asyncoperation.You might as well keep track of things yourself.• But what if we assigned a single domain to a whole bunch of asyncoperations?• Say, every async operation involved in a single HTTPrequest/response cycle?• What if we had hooks into every asynchronous function and everyevent emitter in Node.js, to automatically associate with an“active” domain?@DOMENIC
  13. 13. But … how!?To understand how domains hook in to all that, we’re goingto need to take a journey into the heart of Node.js.@DOMENIC
  14. 14. line 1001:Handle<Value> MakeCallback(const Handle<Object> object,const Handle<Function> callback,int argc, Handle<value> argv[]) {⋮TryCatch try_catch;Local<Value> ret = callback->Call(object, argc, argv);if (try_catch.HasCaught()) {FatalException(try_catch);⋮@DOMENIC
  15. 15. Domain basics• var d = require(domain).create();• There’s a globally active current domain:• process.domain === require(domain).active• (There’s actually a stack, so you can recursively enter domains, but whatevs.)• d.enter() makes d the current domain.• d.exit() makes d inactive.• is shorthand for d.enter(); func(); d.exit().@DOMENIC
  16. 16. Effects of having an active domain• MakeCallback is most directly influenced• process.domain.enter() and process.domain.exit() wrap the previous code• setImmediate, setTimeout, setInterval, process.nextTick• record the active domain and attach it to the callback (1, 2, 3, 4)• when the callback is triggered, wrap it with enter() and exit() (1, 2/3, 4)• new EventEmitter()• records the active domain and attach it to the emitter• when any events are emitted, wraps with enter() and exit()• when an error event is emitted and not handled, gives it to the domain@DOMENIC(mmm, yummy global state…)
  17. 17. Uncaught exceptions• Remember FatalException(try_catch)?• That calls from C++ to process._fatalException (node.js line 222)• Much like in our pseudo-domains, it hands them off to the activedomain’s error handler.• Thus all the enter()/exit() wrapping was just establishing context for thismoment: deciding which domain gets the errors.• If there is an active domain, this behavior replacesuncaughtException.@DOMENIC
  18. 18. That’s cool. Now what?We know how domains work.But do we truly know how to use them?@DOMENIC
  19. 19. Stability 2: Unstable“TheAPI is in the process of settling, but has not yet had sufficientreal-world testing to be considered stable. Backwards-compatibilitywill be maintained if reasonable.” (source)• Don’t use d.dispose().@DOMENIC
  20. 20. APIs to know• domain.create(), obviously•, to enter/exit the domain• d.add(), for adding event emitters created before the domainexisted into the domain.• d.bind(), for manually wiring up a callback to a domain.• d.intercept(), that callback helper from before.@DOMENIC
  21. 21. EventEmitter pitfalls• Event emitters are bound to a domain on creation. But sometimesevent emitters stick around for a long time.• Any callbacks given to the event emitter “escape” the activedomain, which is replaced by whatever the active domain waswhen the event emitter was created.• Generally, anything involving a persistent connection orconnection pool will be in trouble, and not able to associate errorswith the currently-active domain.•
  22. 22. Error recovery• Domains don’t give you try/catch back.• By the time you see an error on the domain, it escaped any close-to-the-source error handling; nobody handled it explicitly.• You’re probably in an inconsistent state, halfway through atransaction or with file handles open or who knows what.• The recommendation is to gracefully return a 500, then shut downyour worker and restart while others in the cluster take over.@DOMENIC
  23. 23. Versus promises• Domains have grown … organically.• Promises attempt to solve error handling from first principles, bygiving you back return and throw semantics.• In practice, this means wrapping every callback in try/catch, muchlike MakeCallback, but less pervasively and more intelligently.• But, they won’t save you completely in Node.js:• error events are still fatal• If you step outside of promise-land, e.g. setTimeout, uncaught exceptionsare again dangerous.@DOMENIC
  24. 24. IN CONCLUSION• Domains work pretty well to tameasync errors in Node.js.• They do so by hooking into Node.js atthe deepest level.• They don’t work perfectly, and theydon’t absolve you of cleanup.• But you should be able to get somesolid wins with minimal extra code.@DOMENIC