• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const { promisify } = require('util');
4const { readFile } = require('fs');
5const sleep = promisify(setTimeout);
6const read = promisify(readFile);
7const common = require('../common.js');
8const {
9  createHook,
10  executionAsyncResource,
11  executionAsyncId,
12  AsyncLocalStorage
13} = require('async_hooks');
14const { createServer } = require('http');
15
16const bench = common.createBenchmark(main, {
17  type: ['async-resource', 'destroy', 'async-local-storage'],
18  asyncMethod: ['callbacks', 'async'],
19  path: '/',
20  connections: 500,
21  duration: 5,
22  n: [1e6]
23});
24
25function buildCurrentResource(getServe) {
26  const server = createServer(getServe(getCLS, setCLS));
27  const hook = createHook({ init });
28  const cls = Symbol('cls');
29  hook.enable();
30
31  return {
32    server,
33    close
34  };
35
36  function getCLS() {
37    const resource = executionAsyncResource();
38    if (!resource[cls]) {
39      return null;
40    }
41    return resource[cls].state;
42  }
43
44  function setCLS(state) {
45    const resource = executionAsyncResource();
46    if (!resource[cls]) {
47      resource[cls] = { state };
48    } else {
49      resource[cls].state = state;
50    }
51  }
52
53  function init(asyncId, type, triggerAsyncId, resource) {
54    const cr = executionAsyncResource();
55    if (cr !== null) {
56      resource[cls] = cr[cls];
57    }
58  }
59
60  function close() {
61    hook.disable();
62    server.close();
63  }
64}
65
66function buildDestroy(getServe) {
67  const transactions = new Map();
68  const server = createServer(getServe(getCLS, setCLS));
69  const hook = createHook({ init, destroy });
70  hook.enable();
71
72  return {
73    server,
74    close
75  };
76
77  function getCLS() {
78    const asyncId = executionAsyncId();
79    return transactions.has(asyncId) ? transactions.get(asyncId) : null;
80  }
81
82  function setCLS(value) {
83    const asyncId = executionAsyncId();
84    transactions.set(asyncId, value);
85  }
86
87  function init(asyncId, type, triggerAsyncId, resource) {
88    transactions.set(asyncId, getCLS());
89  }
90
91  function destroy(asyncId) {
92    transactions.delete(asyncId);
93  }
94
95  function close() {
96    hook.disable();
97    server.close();
98  }
99}
100
101function buildAsyncLocalStorage(getServe) {
102  const asyncLocalStorage = new AsyncLocalStorage();
103  const server = createServer((req, res) => {
104    asyncLocalStorage.run({}, () => {
105      getServe(getCLS, setCLS)(req, res);
106    });
107  });
108
109  return {
110    server,
111    close
112  };
113
114  function getCLS() {
115    const store = asyncLocalStorage.getStore();
116    if (store === undefined) {
117      return null;
118    }
119    return store.state;
120  }
121
122  function setCLS(state) {
123    const store = asyncLocalStorage.getStore();
124    if (store === undefined) {
125      return;
126    }
127    store.state = state;
128  }
129
130  function close() {
131    asyncLocalStorage.disable();
132    server.close();
133  }
134}
135
136function getServeAwait(getCLS, setCLS) {
137  return async function serve(req, res) {
138    setCLS(Math.random());
139    await sleep(10);
140    await read(__filename);
141    if (res.destroyed) return;
142    res.setHeader('content-type', 'application/json');
143    res.end(JSON.stringify({ cls: getCLS() }));
144  };
145}
146
147function getServeCallbacks(getCLS, setCLS) {
148  return function serve(req, res) {
149    setCLS(Math.random());
150    setTimeout(() => {
151      readFile(__filename, () => {
152        if (res.destroyed) return;
153        res.setHeader('content-type', 'application/json');
154        res.end(JSON.stringify({ cls: getCLS() }));
155      });
156    }, 10);
157  };
158}
159
160const types = {
161  'async-resource': buildCurrentResource,
162  'destroy': buildDestroy,
163  'async-local-storage': buildAsyncLocalStorage
164};
165
166const asyncMethods = {
167  'callbacks': getServeCallbacks,
168  'async': getServeAwait
169};
170
171function main({ type, asyncMethod, connections, duration, path }) {
172  const { server, close } = types[type](asyncMethods[asyncMethod]);
173
174  server
175    .listen(common.PORT)
176    .on('listening', () => {
177
178      bench.http({
179        path,
180        connections,
181        duration
182      }, () => {
183        close();
184      });
185    });
186}
187