Domains!
Upcoming SlideShare
Loading in...5
×
 

Domains!

on

  • 4,267 views

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 ...

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.

Statistics

Views

Total Views
4,267
Views on SlideShare
4,013
Embed Views
254

Actions

Likes
7
Downloads
16
Comments
2

5 Embeds 254

https://twitter.com 229
http://eventifier.co 17
http://storify.com 4
http://192.168.0.108 3
https://storify.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Opening story: GothamJS, diving into AngularJS code.
  • I work here at Lab49!
  • The first of these, you handle yourself.The second, by design, re-throws the error if there are no listeners for it.The third, since we’re almost always inside a callback, can’t bubble up the call stack; callbacks reset the call stack, so they hit the top of that call stack pretty quickly.—If we were in a browser, those would hit window.onerror.
  • (On switch back) How can we fix this??
  • Let’s build our own “domains” to track error sources!
  • I dug into the source code, and figured out how domains work. This is basically it.
  • I dug into the source code, and figured out how domains work. This is basically it.
  • Since we’ve got this working for everything else, might as well try to make callbacks a bit easier to use.
  • All callbacks you give to Node.js’s code pass through here.Either directly, from C++ code which directly calls MakeCallback to execute your callbackOr indirectly, via a convoluted chain inside src/node.js that ends up in process._makeCallback.
  • To understand how this gives us the power we need, let’s take a step back and see how the domain object themselves work.
  • The moment you start using domains all this gets switched out from under you. process._setupDomainUse()BUT: wrapping in enter()/exit() is just the setup. enter() and exit() do literally nothing besides setting the active domain. The magic takes place in our next chapter:

Domains! Domains! Presentation Transcript

  • Domains!BY @DOMENIC
  • Domenic Denicola• http://domenic.me• https://github.com/domenic• https://npmjs.org/~domenic• http://slideshare.net/domenicdenicolaThings I’m doing:• @esdiscuss onTwitter• The Promises/A+ spec• Client-Side Packages talk@DOMENIC
  • 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)
  • @DOMENICIF AN ERROR IS THROWN,BUT NOBODY CATCHES IT,DOES IT MAKE A SOUND?
  • 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
  • DEMOTIME@DOMENIChttps://github.com/domenic/domains-romance/blob/master/server.js
  • 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
  • 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
  • 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
  • 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
  • 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
  • DEMOTIME@DOMENIChttps://github.com/domenic/domains-romance/blob/master/serverWithDomains.js
  • 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
  • MakeCallbacknode.cc 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
  • 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.• d.run(func) is shorthand for d.enter(); func(); d.exit().@DOMENIC
  • 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…)
  • 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
  • That’s cool. Now what?We know how domains work.But do we truly know how to use them?@DOMENIC
  • 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
  • APIs to know• domain.create(), obviously• d.run(), 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
  • 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.• https://github.com/domenic/domains-tragedy@DOMENIC
  • 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
  • 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
  • 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