• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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