Domains!BY @DOMENIC
Domenic Denicola• http://domenic.me• https://github.com/domenic• https://npmjs.org/~domenic• http://slideshare.net/domenic...
Let’s talk about errors in@DOMENIC• Errors passed to a callback: beAsync(function (err, result) { … })• Errors emitted fro...
@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 em...
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 dosometh...
Build your own domains: uncaught throws• Before initiating some asynchronous work:• process.pseudoDomain = new EventEmitte...
Build your own domains: unheard errors• When creating an event emitter:• ee.pseudoDomain = new EventEmitter();• ee.on(erro...
Build your own domains: callback err params• function bindToDomain(domain, cb) {return function (err, result) {if (err) re...
The key feature: automatic aggregation• This is pretty useless if you have to create a domain for every asyncoperation.You...
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.@...
MakeCallbacknode.cc line 1001:Handle<Value> MakeCallback(const Handle<Object> object,const Handle<Function> callback,int a...
Domain basics• var d = require(domain).create();• There’s a globally active current domain:• process.domain === require(do...
Effects of having an active domain• MakeCallback is most directly influenced• process.domain.enter() and process.domain.ex...
Uncaught exceptions• Remember FatalException(try_catch)?• That calls from C++ to process._fatalException (node.js line 222...
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 conside...
APIs to know• domain.create(), obviously• d.run(), to enter/exit the domain• d.add(), for adding event emitters created be...
EventEmitter pitfalls• Event emitters are bound to a domain on creation. But sometimesevent emitters stick around for a lo...
Error recovery• Domains don’t give you try/catch back.• By the time you see an error on the domain, it escaped any close-t...
Versus promises• Domains have grown … organically.• Promises attempt to solve error handling from first principles, bygivi...
IN CONCLUSION• Domains work pretty well to tameasync errors in Node.js.• They do so by hooking into Node.js atthe deepest ...
Upcoming SlideShare
Loading in...5
×

Domains!

4,629

Published on

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.

Published in: Technology
2 Comments
10 Likes
Statistics
Notes
No Downloads
Views
Total Views
4,629
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
26
Comments
2
Likes
10
Embeds 0
No embeds

No notes for slide
  • 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!

    1. 1. Domains!BY @DOMENIC
    2. 2. 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
    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)
    4. 4. @DOMENICIF AN ERROR IS THROWN,BUT NOBODY CATCHES IT,DOES IT MAKE A SOUND?
    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
    6. 6. DEMOTIME@DOMENIChttps://github.com/domenic/domains-romance/blob/master/server.js
    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
    12. 12. DEMOTIME@DOMENIChttps://github.com/domenic/domains-romance/blob/master/serverWithDomains.js
    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. 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
    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.• d.run(func) 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• 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
    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.• https://github.com/domenic/domains-tragedy@DOMENIC
    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
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×