1# Domain 2<!-- YAML 3deprecated: v1.4.2 4changes: 5 - version: v8.8.0 6 pr-url: https://github.com/nodejs/node/pull/15695 7 description: Any `Promise`s created in VM contexts no longer have a 8 `.domain` property. Their handlers are still executed in the 9 proper domain, however, and `Promise`s created in the main 10 context still possess a `.domain` property. 11 - version: v8.0.0 12 pr-url: https://github.com/nodejs/node/pull/12489 13 description: Handlers for `Promise`s are now invoked in the domain in which 14 the first promise of a chain was created. 15--> 16 17<!--introduced_in=v0.10.0--> 18 19> Stability: 0 - Deprecated 20 21<!-- source_link=lib/domain.js --> 22 23**This module is pending deprecation**. Once a replacement API has been 24finalized, this module will be fully deprecated. Most developers should 25**not** have cause to use this module. Users who absolutely must have 26the functionality that domains provide may rely on it for the time being 27but should expect to have to migrate to a different solution 28in the future. 29 30Domains provide a way to handle multiple different IO operations as a 31single group. If any of the event emitters or callbacks registered to a 32domain emit an `'error'` event, or throw an error, then the domain object 33will be notified, rather than losing the context of the error in the 34`process.on('uncaughtException')` handler, or causing the program to 35exit immediately with an error code. 36 37## Warning: Don't ignore errors! 38 39<!-- type=misc --> 40 41Domain error handlers are not a substitute for closing down a 42process when an error occurs. 43 44By the very nature of how [`throw`][] works in JavaScript, there is almost 45never any way to safely "pick up where it left off", without leaking 46references, or creating some other sort of undefined brittle state. 47 48The safest way to respond to a thrown error is to shut down the 49process. Of course, in a normal web server, there may be many 50open connections, and it is not reasonable to abruptly shut those down 51because an error was triggered by someone else. 52 53The better approach is to send an error response to the request that 54triggered the error, while letting the others finish in their normal 55time, and stop listening for new requests in that worker. 56 57In this way, `domain` usage goes hand-in-hand with the cluster module, 58since the master process can fork a new worker when a worker 59encounters an error. For Node.js programs that scale to multiple 60machines, the terminating proxy or service registry can take note of 61the failure, and react accordingly. 62 63For example, this is not a good idea: 64 65```js 66// XXX WARNING! BAD IDEA! 67 68const d = require('domain').create(); 69d.on('error', (er) => { 70 // The error won't crash the process, but what it does is worse! 71 // Though we've prevented abrupt process restarting, we are leaking 72 // a lot of resources if this ever happens. 73 // This is no better than process.on('uncaughtException')! 74 console.log(`error, but oh well ${er.message}`); 75}); 76d.run(() => { 77 require('http').createServer((req, res) => { 78 handleRequest(req, res); 79 }).listen(PORT); 80}); 81``` 82 83By using the context of a domain, and the resilience of separating our 84program into multiple worker processes, we can react more 85appropriately, and handle errors with much greater safety. 86 87```js 88// Much better! 89 90const cluster = require('cluster'); 91const PORT = +process.env.PORT || 1337; 92 93if (cluster.isMaster) { 94 // A more realistic scenario would have more than 2 workers, 95 // and perhaps not put the master and worker in the same file. 96 // 97 // It is also possible to get a bit fancier about logging, and 98 // implement whatever custom logic is needed to prevent DoS 99 // attacks and other bad behavior. 100 // 101 // See the options in the cluster documentation. 102 // 103 // The important thing is that the master does very little, 104 // increasing our resilience to unexpected errors. 105 106 cluster.fork(); 107 cluster.fork(); 108 109 cluster.on('disconnect', (worker) => { 110 console.error('disconnect!'); 111 cluster.fork(); 112 }); 113 114} else { 115 // the worker 116 // 117 // This is where we put our bugs! 118 119 const domain = require('domain'); 120 121 // See the cluster documentation for more details about using 122 // worker processes to serve requests. How it works, caveats, etc. 123 124 const server = require('http').createServer((req, res) => { 125 const d = domain.create(); 126 d.on('error', (er) => { 127 console.error(`error ${er.stack}`); 128 129 // We're in dangerous territory! 130 // By definition, something unexpected occurred, 131 // which we probably didn't want. 132 // Anything can happen now! Be very careful! 133 134 try { 135 // Make sure we close down within 30 seconds 136 const killtimer = setTimeout(() => { 137 process.exit(1); 138 }, 30000); 139 // But don't keep the process open just for that! 140 killtimer.unref(); 141 142 // Stop taking new requests. 143 server.close(); 144 145 // Let the master know we're dead. This will trigger a 146 // 'disconnect' in the cluster master, and then it will fork 147 // a new worker. 148 cluster.worker.disconnect(); 149 150 // Try to send an error to the request that triggered the problem 151 res.statusCode = 500; 152 res.setHeader('content-type', 'text/plain'); 153 res.end('Oops, there was a problem!\n'); 154 } catch (er2) { 155 // Oh well, not much we can do at this point. 156 console.error(`Error sending 500! ${er2.stack}`); 157 } 158 }); 159 160 // Because req and res were created before this domain existed, 161 // we need to explicitly add them. 162 // See the explanation of implicit vs explicit binding below. 163 d.add(req); 164 d.add(res); 165 166 // Now run the handler function in the domain. 167 d.run(() => { 168 handleRequest(req, res); 169 }); 170 }); 171 server.listen(PORT); 172} 173 174// This part is not important. Just an example routing thing. 175// Put fancy application logic here. 176function handleRequest(req, res) { 177 switch (req.url) { 178 case '/error': 179 // We do some async stuff, and then... 180 setTimeout(() => { 181 // Whoops! 182 flerb.bark(); 183 }, timeout); 184 break; 185 default: 186 res.end('ok'); 187 } 188} 189``` 190 191## Additions to `Error` objects 192 193<!-- type=misc --> 194 195Any time an `Error` object is routed through a domain, a few extra fields 196are added to it. 197 198* `error.domain` The domain that first handled the error. 199* `error.domainEmitter` The event emitter that emitted an `'error'` event 200 with the error object. 201* `error.domainBound` The callback function which was bound to the 202 domain, and passed an error as its first argument. 203* `error.domainThrown` A boolean indicating whether the error was 204 thrown, emitted, or passed to a bound callback function. 205 206## Implicit binding 207 208<!--type=misc--> 209 210If domains are in use, then all **new** `EventEmitter` objects (including 211Stream objects, requests, responses, etc.) will be implicitly bound to 212the active domain at the time of their creation. 213 214Additionally, callbacks passed to lowlevel event loop requests (such as 215to `fs.open()`, or other callback-taking methods) will automatically be 216bound to the active domain. If they throw, then the domain will catch 217the error. 218 219In order to prevent excessive memory usage, `Domain` objects themselves 220are not implicitly added as children of the active domain. If they 221were, then it would be too easy to prevent request and response objects 222from being properly garbage collected. 223 224To nest `Domain` objects as children of a parent `Domain` they must be 225explicitly added. 226 227Implicit binding routes thrown errors and `'error'` events to the 228`Domain`'s `'error'` event, but does not register the `EventEmitter` on the 229`Domain`. 230Implicit binding only takes care of thrown errors and `'error'` events. 231 232## Explicit binding 233 234<!--type=misc--> 235 236Sometimes, the domain in use is not the one that ought to be used for a 237specific event emitter. Or, the event emitter could have been created 238in the context of one domain, but ought to instead be bound to some 239other domain. 240 241For example, there could be one domain in use for an HTTP server, but 242perhaps we would like to have a separate domain to use for each request. 243 244That is possible via explicit binding. 245 246```js 247// Create a top-level domain for the server 248const domain = require('domain'); 249const http = require('http'); 250const serverDomain = domain.create(); 251 252serverDomain.run(() => { 253 // Server is created in the scope of serverDomain 254 http.createServer((req, res) => { 255 // Req and res are also created in the scope of serverDomain 256 // however, we'd prefer to have a separate domain for each request. 257 // create it first thing, and add req and res to it. 258 const reqd = domain.create(); 259 reqd.add(req); 260 reqd.add(res); 261 reqd.on('error', (er) => { 262 console.error('Error', er, req.url); 263 try { 264 res.writeHead(500); 265 res.end('Error occurred, sorry.'); 266 } catch (er2) { 267 console.error('Error sending 500', er2, req.url); 268 } 269 }); 270 }).listen(1337); 271}); 272``` 273 274## `domain.create()` 275 276* Returns: {Domain} 277 278## Class: `Domain` 279 280* Extends: {EventEmitter} 281 282The `Domain` class encapsulates the functionality of routing errors and 283uncaught exceptions to the active `Domain` object. 284 285To handle the errors that it catches, listen to its `'error'` event. 286 287### `domain.members` 288 289* {Array} 290 291An array of timers and event emitters that have been explicitly added 292to the domain. 293 294### `domain.add(emitter)` 295 296* `emitter` {EventEmitter|Timer} emitter or timer to be added to the domain 297 298Explicitly adds an emitter to the domain. If any event handlers called by 299the emitter throw an error, or if the emitter emits an `'error'` event, it 300will be routed to the domain's `'error'` event, just like with implicit 301binding. 302 303This also works with timers that are returned from [`setInterval()`][] and 304[`setTimeout()`][]. If their callback function throws, it will be caught by 305the domain `'error'` handler. 306 307If the Timer or `EventEmitter` was already bound to a domain, it is removed 308from that one, and bound to this one instead. 309 310### `domain.bind(callback)` 311 312* `callback` {Function} The callback function 313* Returns: {Function} The bound function 314 315The returned function will be a wrapper around the supplied callback 316function. When the returned function is called, any errors that are 317thrown will be routed to the domain's `'error'` event. 318 319```js 320const d = domain.create(); 321 322function readSomeFile(filename, cb) { 323 fs.readFile(filename, 'utf8', d.bind((er, data) => { 324 // If this throws, it will also be passed to the domain. 325 return cb(er, data ? JSON.parse(data) : null); 326 })); 327} 328 329d.on('error', (er) => { 330 // An error occurred somewhere. If we throw it now, it will crash the program 331 // with the normal line number and stack message. 332}); 333``` 334 335### `domain.enter()` 336 337The `enter()` method is plumbing used by the `run()`, `bind()`, and 338`intercept()` methods to set the active domain. It sets `domain.active` and 339`process.domain` to the domain, and implicitly pushes the domain onto the domain 340stack managed by the domain module (see [`domain.exit()`][] for details on the 341domain stack). The call to `enter()` delimits the beginning of a chain of 342asynchronous calls and I/O operations bound to a domain. 343 344Calling `enter()` changes only the active domain, and does not alter the domain 345itself. `enter()` and `exit()` can be called an arbitrary number of times on a 346single domain. 347 348### `domain.exit()` 349 350The `exit()` method exits the current domain, popping it off the domain stack. 351Any time execution is going to switch to the context of a different chain of 352asynchronous calls, it's important to ensure that the current domain is exited. 353The call to `exit()` delimits either the end of or an interruption to the chain 354of asynchronous calls and I/O operations bound to a domain. 355 356If there are multiple, nested domains bound to the current execution context, 357`exit()` will exit any domains nested within this domain. 358 359Calling `exit()` changes only the active domain, and does not alter the domain 360itself. `enter()` and `exit()` can be called an arbitrary number of times on a 361single domain. 362 363### `domain.intercept(callback)` 364 365* `callback` {Function} The callback function 366* Returns: {Function} The intercepted function 367 368This method is almost identical to [`domain.bind(callback)`][]. However, in 369addition to catching thrown errors, it will also intercept [`Error`][] 370objects sent as the first argument to the function. 371 372In this way, the common `if (err) return callback(err);` pattern can be replaced 373with a single error handler in a single place. 374 375```js 376const d = domain.create(); 377 378function readSomeFile(filename, cb) { 379 fs.readFile(filename, 'utf8', d.intercept((data) => { 380 // Note, the first argument is never passed to the 381 // callback since it is assumed to be the 'Error' argument 382 // and thus intercepted by the domain. 383 384 // If this throws, it will also be passed to the domain 385 // so the error-handling logic can be moved to the 'error' 386 // event on the domain instead of being repeated throughout 387 // the program. 388 return cb(null, JSON.parse(data)); 389 })); 390} 391 392d.on('error', (er) => { 393 // An error occurred somewhere. If we throw it now, it will crash the program 394 // with the normal line number and stack message. 395}); 396``` 397 398### `domain.remove(emitter)` 399 400* `emitter` {EventEmitter|Timer} emitter or timer to be removed from the domain 401 402The opposite of [`domain.add(emitter)`][]. Removes domain handling from the 403specified emitter. 404 405### `domain.run(fn[, ...args])` 406 407* `fn` {Function} 408* `...args` {any} 409 410Run the supplied function in the context of the domain, implicitly 411binding all event emitters, timers, and lowlevel requests that are 412created in that context. Optionally, arguments can be passed to 413the function. 414 415This is the most basic way to use a domain. 416 417```js 418const domain = require('domain'); 419const fs = require('fs'); 420const d = domain.create(); 421d.on('error', (er) => { 422 console.error('Caught error!', er); 423}); 424d.run(() => { 425 process.nextTick(() => { 426 setTimeout(() => { // Simulating some various async stuff 427 fs.open('non-existent file', 'r', (er, fd) => { 428 if (er) throw er; 429 // proceed... 430 }); 431 }, 100); 432 }); 433}); 434``` 435 436In this example, the `d.on('error')` handler will be triggered, rather 437than crashing the program. 438 439## Domains and promises 440 441As of Node.js 8.0.0, the handlers of promises are run inside the domain in 442which the call to `.then()` or `.catch()` itself was made: 443 444```js 445const d1 = domain.create(); 446const d2 = domain.create(); 447 448let p; 449d1.run(() => { 450 p = Promise.resolve(42); 451}); 452 453d2.run(() => { 454 p.then((v) => { 455 // running in d2 456 }); 457}); 458``` 459 460A callback may be bound to a specific domain using [`domain.bind(callback)`][]: 461 462```js 463const d1 = domain.create(); 464const d2 = domain.create(); 465 466let p; 467d1.run(() => { 468 p = Promise.resolve(42); 469}); 470 471d2.run(() => { 472 p.then(p.domain.bind((v) => { 473 // running in d1 474 })); 475}); 476``` 477 478Domains will not interfere with the error handling mechanisms for 479promises. In other words, no `'error'` event will be emitted for unhandled 480`Promise` rejections. 481 482[`Error`]: errors.md#errors_class_error 483[`domain.add(emitter)`]: #domain_domain_add_emitter 484[`domain.bind(callback)`]: #domain_domain_bind_callback 485[`domain.exit()`]: #domain_domain_exit 486[`setInterval()`]: timers.md#timers_setinterval_callback_delay_args 487[`setTimeout()`]: timers.md#timers_settimeout_callback_delay_args 488[`throw`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw 489