'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(); }); }); }