SlideShare a Scribd company logo
1 of 24
Domains!
BY @DOMENIC
Domenic Denicola
• http://domenic.me
• https://github.com/domenic
• https://npmjs.org/~domenic
• http://slideshare.net/domenicdenicola
Things 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)
@DOMENIC
IF 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
@DOMENIC
https://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 do
something 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 'error's
• 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 async
operation.You might as well keep track of things yourself.
• But what if we assigned a single domain to a whole bunch of async
operations?
• Say, every async operation involved in a single HTTP
request/response cycle?
• What if we had hooks into every asynchronous function and every
event emitter in Node.js, to automatically associate with an
“active” domain?
@DOMENIC
DEMOTIME
@DOMENIC
https://github.com/domenic/domains-romance/blob/master/serverWithDomains.js
But … how!?
To understand how domains hook in to all that, we’re going
to need to take a journey into the heart of Node.js.
@DOMENIC
MakeCallback
node.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 active
domain’s 'error' handler.
• Thus all the enter()/exit() wrapping was just establishing context for this
moment: deciding which domain gets the errors.
• If there is an active domain, this behavior replaces
'uncaughtException'.
@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 sufficient
real-world testing to be considered stable. Backwards-compatibility
will 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 domain
existed 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 sometimes
event emitters stick around for a long time.
• Any callbacks given to the event emitter “escape” the active
domain, which is replaced by whatever the active domain was
when the event emitter was created.
• Generally, anything involving a persistent connection or
connection pool will be in trouble, and not able to associate errors
with 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 a
transaction or with file handles open or who knows what.
• The recommendation is to gracefully return a 500, then shut down
your 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, by
giving you back return and throw semantics.
• In practice, this means wrapping every callback in try/catch, much
like 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 exceptions
are again dangerous.
@DOMENIC
IN CONCLUSION
• Domains work pretty well to tame
async errors in Node.js.
• They do so by hooking into Node.js at
the deepest level.
• They don’t work perfectly, and they
don’t absolve you of cleanup.
• But you should be able to get some
solid wins with minimal extra code.
@DOMENIC

More Related Content

What's hot

What's hot (20)

Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
JavaScript Promise
JavaScript PromiseJavaScript Promise
JavaScript Promise
 
Promises, Promises
Promises, PromisesPromises, Promises
Promises, Promises
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
A Gentle Introduction to Event Loops
A Gentle Introduction to Event LoopsA Gentle Introduction to Event Loops
A Gentle Introduction to Event Loops
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
Callbacks and control flow in Node js
Callbacks and control flow in Node jsCallbacks and control flow in Node js
Callbacks and control flow in Node js
 
Perl: Coro asynchronous
Perl: Coro asynchronous Perl: Coro asynchronous
Perl: Coro asynchronous
 
Javascript Promises/Q Library
Javascript Promises/Q LibraryJavascript Promises/Q Library
Javascript Promises/Q Library
 
Understanding Asynchronous JavaScript
Understanding Asynchronous JavaScriptUnderstanding Asynchronous JavaScript
Understanding Asynchronous JavaScript
 
Asynchronous programming patterns in Perl
Asynchronous programming patterns in PerlAsynchronous programming patterns in Perl
Asynchronous programming patterns in Perl
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is here
 
Avoiding callback hell with promises
Avoiding callback hell with promisesAvoiding callback hell with promises
Avoiding callback hell with promises
 
C to perl binding
C to perl bindingC to perl binding
C to perl binding
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
 
Any event intro
Any event introAny event intro
Any event intro
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 
Introduction to Ecmascript - ES6
Introduction to Ecmascript - ES6Introduction to Ecmascript - ES6
Introduction to Ecmascript - ES6
 

Similar to Domains!

Fast, concurrent ruby web applications with EventMachine and EM::Synchrony
Fast, concurrent ruby web applications with EventMachine and EM::SynchronyFast, concurrent ruby web applications with EventMachine and EM::Synchrony
Fast, concurrent ruby web applications with EventMachine and EM::Synchrony
Kyle Drake
 
Case Study of the Unexplained
Case Study of the UnexplainedCase Study of the Unexplained
Case Study of the Unexplained
shannomc
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to Go
Cloudflare
 

Similar to Domains! (20)

Unit Testing for Great Justice
Unit Testing for Great JusticeUnit Testing for Great Justice
Unit Testing for Great Justice
 
Understanding the Node.js Platform
Understanding the Node.js PlatformUnderstanding the Node.js Platform
Understanding the Node.js Platform
 
Fast, concurrent ruby web applications with EventMachine and EM::Synchrony
Fast, concurrent ruby web applications with EventMachine and EM::SynchronyFast, concurrent ruby web applications with EventMachine and EM::Synchrony
Fast, concurrent ruby web applications with EventMachine and EM::Synchrony
 
Klee and angr
Klee and angrKlee and angr
Klee and angr
 
Hands-on with the Symfony2 Framework
Hands-on with the Symfony2 FrameworkHands-on with the Symfony2 Framework
Hands-on with the Symfony2 Framework
 
LCA2014 - Introduction to Go
LCA2014 - Introduction to GoLCA2014 - Introduction to Go
LCA2014 - Introduction to Go
 
Ruby 1.9 Fibers
Ruby 1.9 FibersRuby 1.9 Fibers
Ruby 1.9 Fibers
 
Pilot Tech Talk #10 — Practical automation by Kamil Cholewiński
Pilot Tech Talk #10 — Practical automation by Kamil CholewińskiPilot Tech Talk #10 — Practical automation by Kamil Cholewiński
Pilot Tech Talk #10 — Practical automation by Kamil Cholewiński
 
No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010
 
Case Study of the Unexplained
Case Study of the UnexplainedCase Study of the Unexplained
Case Study of the Unexplained
 
Time for Comet?
Time for Comet?Time for Comet?
Time for Comet?
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to Go
 
Serverless, The Middy Way - Workshop
Serverless, The Middy Way - WorkshopServerless, The Middy Way - Workshop
Serverless, The Middy Way - Workshop
 
From Elixir to Akka (and back) - ElixirConf Mx 2017
From Elixir to Akka (and back) - ElixirConf Mx 2017From Elixir to Akka (and back) - ElixirConf Mx 2017
From Elixir to Akka (and back) - ElixirConf Mx 2017
 
Symfony 2.0
Symfony 2.0Symfony 2.0
Symfony 2.0
 
ES6 is Nigh
ES6 is NighES6 is Nigh
ES6 is Nigh
 
An opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathonAn opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathon
 
Original slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talkOriginal slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talk
 
Crm saturday madrid 2017 jordi montaña - test automation
Crm saturday madrid 2017   jordi montaña - test automationCrm saturday madrid 2017   jordi montaña - test automation
Crm saturday madrid 2017 jordi montaña - test automation
 
Crm Saturday Madrid - Test Automation for Dynamics 365
Crm Saturday Madrid  - Test Automation for Dynamics 365Crm Saturday Madrid  - Test Automation for Dynamics 365
Crm Saturday Madrid - Test Automation for Dynamics 365
 

More from Domenic Denicola

The Final Frontier
The Final FrontierThe Final Frontier
The Final Frontier
Domenic Denicola
 
How to Win Friends and Influence Standards Bodies
How to Win Friends and Influence Standards BodiesHow to Win Friends and Influence Standards Bodies
How to Win Friends and Influence Standards Bodies
Domenic Denicola
 

More from Domenic Denicola (14)

The State of JavaScript (2015)
The State of JavaScript (2015)The State of JavaScript (2015)
The State of JavaScript (2015)
 
The jsdom
The jsdomThe jsdom
The jsdom
 
The Final Frontier
The Final FrontierThe Final Frontier
The Final Frontier
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
Streams for the Web
Streams for the WebStreams for the Web
Streams for the Web
 
After Return of the Jedi
After Return of the JediAfter Return of the Jedi
After Return of the Jedi
 
The State of JavaScript
The State of JavaScriptThe State of JavaScript
The State of JavaScript
 
How to Win Friends and Influence Standards Bodies
How to Win Friends and Influence Standards BodiesHow to Win Friends and Influence Standards Bodies
How to Win Friends and Influence Standards Bodies
 
The Extensible Web
The Extensible WebThe Extensible Web
The Extensible Web
 
Client-Side Packages
Client-Side PackagesClient-Side Packages
Client-Side Packages
 
Creating Truly RESTful APIs
Creating Truly RESTful APIsCreating Truly RESTful APIs
Creating Truly RESTful APIs
 
JavaScript on the Desktop
JavaScript on the DesktopJavaScript on the Desktop
JavaScript on the Desktop
 
Real World Windows 8 Apps in JavaScript
Real World Windows 8 Apps in JavaScriptReal World Windows 8 Apps in JavaScript
Real World Windows 8 Apps in JavaScript
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 

Recently uploaded

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Recently uploaded (20)

Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 

Domains!

  • 2. Domenic Denicola • http://domenic.me • https://github.com/domenic • https://npmjs.org/~domenic • http://slideshare.net/domenicdenicola Things I’m doing: • @esdiscuss onTwitter • The Promises/A+ spec • Client-Side Packages talk @DOMENIC
  • 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. @DOMENIC IF AN ERROR IS THROWN, BUT NOBODY CATCHES IT, DOES IT MAKE A SOUND?
  • 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. Before domains: process.on('uncaughtException', …) • Instead of crashing, you could listen for “about to crash,” and do something 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. 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. Build your own domains: unheard 'error's • 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. 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. The key feature: automatic aggregation • This is pretty useless if you have to create a domain for every async operation.You might as well keep track of things yourself. • But what if we assigned a single domain to a whole bunch of async operations? • Say, every async operation involved in a single HTTP request/response cycle? • What if we had hooks into every asynchronous function and every event emitter in Node.js, to automatically associate with an “active” domain? @DOMENIC
  • 13. But … how!? To understand how domains hook in to all that, we’re going to need to take a journey into the heart of Node.js. @DOMENIC
  • 14. MakeCallback node.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. 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. 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. 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 active domain’s 'error' handler. • Thus all the enter()/exit() wrapping was just establishing context for this moment: deciding which domain gets the errors. • If there is an active domain, this behavior replaces 'uncaughtException'. @DOMENIC
  • 18. That’s cool. Now what? We know how domains work. But do we truly know how to use them? @DOMENIC
  • 19. Stability 2: Unstable “TheAPI is in the process of settling, but has not yet had sufficient real-world testing to be considered stable. Backwards-compatibility will be maintained if reasonable.” (source) • Don’t use d.dispose(). @DOMENIC
  • 20. APIs to know • domain.create(), obviously • d.run(), to enter/exit the domain • d.add(), for adding event emitters created before the domain existed into the domain. • d.bind(), for manually wiring up a callback to a domain. • d.intercept(), that callback helper from before. @DOMENIC
  • 21. EventEmitter pitfalls • Event emitters are bound to a domain on creation. But sometimes event emitters stick around for a long time. • Any callbacks given to the event emitter “escape” the active domain, which is replaced by whatever the active domain was when the event emitter was created. • Generally, anything involving a persistent connection or connection pool will be in trouble, and not able to associate errors with the currently-active domain. • https://github.com/domenic/domains-tragedy @DOMENIC
  • 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 a transaction or with file handles open or who knows what. • The recommendation is to gracefully return a 500, then shut down your worker and restart while others in the cluster take over. @DOMENIC
  • 23. Versus promises • Domains have grown … organically. • Promises attempt to solve error handling from first principles, by giving you back return and throw semantics. • In practice, this means wrapping every callback in try/catch, much like 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 exceptions are again dangerous. @DOMENIC
  • 24. IN CONCLUSION • Domains work pretty well to tame async errors in Node.js. • They do so by hooking into Node.js at the deepest level. • They don’t work perfectly, and they don’t absolve you of cleanup. • But you should be able to get some solid wins with minimal extra code. @DOMENIC

Editor's Notes

  1. Opening story: GothamJS, diving into AngularJS code.
  2. I work here at Lab49!
  3. 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.
  4. (On switch back) How can we fix this??
  5. Let’s build our own “domains” to track error sources!
  6. I dug into the source code, and figured out how domains work. This is basically it.
  7. I dug into the source code, and figured out how domains work. This is basically it.
  8. Since we’ve got this working for everything else, might as well try to make callbacks a bit easier to use.
  9. 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.
  10. To understand how this gives us the power we need, let’s take a step back and see how the domain object themselves work.
  11. 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: