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