| 'use strict'; |
| |
| const { promisify } = require('util'); |
| const { readFile } = require('fs'); |
| const sleep = promisify(setTimeout); |
| const read = promisify(readFile); |
| const common = require('../common.js'); |
| const { |
| createHook, |
| executionAsyncResource, |
| executionAsyncId, |
| AsyncLocalStorage, |
| } = require('async_hooks'); |
| const { createServer } = require('http'); |
| |
| const bench = common.createBenchmark(main, { |
| type: ['async-resource', 'destroy', 'async-local-storage'], |
| asyncMethod: ['callbacks', 'async'], |
| path: '/', |
| connections: 500, |
| duration: 5, |
| n: [1e6], |
| }); |
| |
| function buildCurrentResource(getServe) { |
| const server = createServer(getServe(getCLS, setCLS)); |
| const hook = createHook({ init }); |
| const cls = Symbol('cls'); |
| hook.enable(); |
| |
| return { |
| server, |
| close, |
| }; |
| |
| function getCLS() { |
| const resource = executionAsyncResource(); |
| if (!resource[cls]) { |
| return null; |
| } |
| return resource[cls].state; |
| } |
| |
| function setCLS(state) { |
| const resource = executionAsyncResource(); |
| if (!resource[cls]) { |
| resource[cls] = { state }; |
| } else { |
| resource[cls].state = state; |
| } |
| } |
| |
| function init(asyncId, type, triggerAsyncId, resource) { |
| const cr = executionAsyncResource(); |
| if (cr !== null) { |
| resource[cls] = cr[cls]; |
| } |
| } |
| |
| function close() { |
| hook.disable(); |
| server.close(); |
| } |
| } |
| |
| function buildDestroy(getServe) { |
| const transactions = new Map(); |
| const server = createServer(getServe(getCLS, setCLS)); |
| const hook = createHook({ init, destroy }); |
| hook.enable(); |
| |
| return { |
| server, |
| close, |
| }; |
| |
| function getCLS() { |
| const asyncId = executionAsyncId(); |
| return transactions.has(asyncId) ? transactions.get(asyncId) : null; |
| } |
| |
| function setCLS(value) { |
| const asyncId = executionAsyncId(); |
| transactions.set(asyncId, value); |
| } |
| |
| function init(asyncId, type, triggerAsyncId, resource) { |
| transactions.set(asyncId, getCLS()); |
| } |
| |
| function destroy(asyncId) { |
| transactions.delete(asyncId); |
| } |
| |
| function close() { |
| hook.disable(); |
| server.close(); |
| } |
| } |
| |
| function buildAsyncLocalStorage(getServe) { |
| const asyncLocalStorage = new AsyncLocalStorage(); |
| const server = createServer((req, res) => { |
| asyncLocalStorage.run({}, () => { |
| getServe(getCLS, setCLS)(req, res); |
| }); |
| }); |
| |
| return { |
| server, |
| close, |
| }; |
| |
| function getCLS() { |
| const store = asyncLocalStorage.getStore(); |
| if (store === undefined) { |
| return null; |
| } |
| return store.state; |
| } |
| |
| function setCLS(state) { |
| const store = asyncLocalStorage.getStore(); |
| if (store === undefined) { |
| return; |
| } |
| store.state = state; |
| } |
| |
| function close() { |
| asyncLocalStorage.disable(); |
| server.close(); |
| } |
| } |
| |
| function getServeAwait(getCLS, setCLS) { |
| return async function serve(req, res) { |
| setCLS(Math.random()); |
| await sleep(10); |
| await read(__filename); |
| if (res.destroyed) return; |
| res.setHeader('content-type', 'application/json'); |
| res.end(JSON.stringify({ cls: getCLS() })); |
| }; |
| } |
| |
| function getServeCallbacks(getCLS, setCLS) { |
| return function serve(req, res) { |
| setCLS(Math.random()); |
| setTimeout(() => { |
| readFile(__filename, () => { |
| if (res.destroyed) return; |
| res.setHeader('content-type', 'application/json'); |
| res.end(JSON.stringify({ cls: getCLS() })); |
| }); |
| }, 10); |
| }; |
| } |
| |
| const types = { |
| 'async-resource': buildCurrentResource, |
| 'destroy': buildDestroy, |
| 'async-local-storage': buildAsyncLocalStorage, |
| }; |
| |
| const asyncMethods = { |
| 'callbacks': getServeCallbacks, |
| 'async': getServeAwait, |
| }; |
| |
| function main({ type, asyncMethod, connections, duration, path }) { |
| const { server, close } = types[type](asyncMethods[asyncMethod]); |
| |
| server |
| .listen(common.PORT) |
| .on('listening', () => { |
| |
| bench.http({ |
| path, |
| connections, |
| duration, |
| }, () => { |
| close(); |
| }); |
| }); |
| } |