Created
May 20, 2021 16:21
-
-
Save Mr0grog/f6f3d97a234087ebf247b8faf73822b1 to your computer and use it in GitHub Desktop.
Test out scenarios with Node.js async hooks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Test how async hooks works in various scenarios. | |
* | |
* Call `hook.enable()` to start tracking async resources and `hook.disable()` | |
* to stop or pause tracking. Then use `logAndResetHooks()` to print a log of | |
* all the creation and destruction of async hooks. | |
*/ | |
const asyncHooks = require("async_hooks"); | |
const http = require("http"); | |
const util = require("util"); | |
// TOOLING FOR TESTING ASYNC HOOKS ----------------------------------------- | |
let logs = []; | |
function log(...args) { | |
logs.push(`${util.format(...args)}`); | |
} | |
class ErrorWithStack extends Error { | |
constructor(message, callsite) { | |
// Ensure we have a large stack length so we get full details. | |
const stackLimit = Error.stackTraceLimit; | |
Error.stackTraceLimit = Math.max(100, stackLimit || 10); | |
super(message); | |
if (Error.captureStackTrace) { | |
Error.captureStackTrace(this, callsite); | |
} | |
Error.stackTraceLimit = stackLimit; | |
} | |
} | |
const noStack = new Set(["TickObject", "Immediate"]) | |
const handles = new Map(); | |
const hook = asyncHooks.createHook({ | |
init: function initHook (asyncId, type, triggerAsyncId, resource) { | |
const error = new ErrorWithStack(type, initHook); | |
log(`init ( asyncId: ${asyncId}, type: "${type}", triggerAsyncId: ${triggerAsyncId} )`); | |
if (!noStack.has(type)) log(" Stack:", error.stack); | |
handles.set(asyncId, { asyncId, type, triggerAsyncId }); | |
}, | |
destroy(asyncId) { | |
log(`destroy ( asyncId: ${asyncId} )`); | |
handles.delete(asyncId); | |
}, | |
}); | |
function logAndResetHooks() { | |
console.log(logs.join("\n")); | |
handles.clear(); | |
logs = []; | |
} | |
// ACTUAL TEST SCENARIOS -------------------------------------------------- | |
const respondWithOk = (_, response) => response.end("ok"); | |
// 1. `server.listen` with just a port creates TCPSERVERWRAP directly. | |
function userCode1 (done) { | |
hook.enable(); | |
const server = http.createServer(respondWithOk); | |
server.listen(0, () => { | |
// Wait until after the server.listen callback is completed to stop hooks. | |
setImmediate(() => { | |
// Disable before closing the server to simulate a server left running | |
hook.disable(); | |
server.close(); | |
logAndResetHooks(); | |
if (done) done(); | |
}); | |
}); | |
} | |
// 2. `server.listen` with a host creates GETADDRINFOREQWRAP directly and | |
// TCPSERVERWRAP indirectly. | |
function userCode2 (done) { | |
hook.enable(); | |
const server = http.createServer(respondWithOk); | |
server.listen({host: "localhost", port: 0}, () => { | |
// Wait until after the server.listen callback is completed to stop hooks. | |
setImmediate(() => { | |
// Disable before closing the server to simulate a server left running | |
hook.disable(); | |
server.close(); | |
logAndResetHooks(); | |
if (done) done(); | |
}); | |
}); | |
} | |
// Run these! | |
console.log("TEST 1: `server.listen` with just a port"); | |
console.log("----------------------------------------") | |
userCode1(() => { | |
console.log("\n\n\n"); | |
console.log("TEST 2: `server.listen` with a host option"); | |
console.log("------------------------------------------") | |
userCode2(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment