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