Question presented to candidate: "You need to hash a password with a slow, deliberately expensive algorithm like scrypt. Would using the synchronous version inside a request handler cause a real, measurable problem — and how would you actually prove it either way?"
What a strong answer should cover:
- Node's
cryptomodule offers both synchronous (pbkdf2Sync,scryptSync) and asynchronous (pbkdf2,scrypt) variants for its computationally expensive functions — the sync versions run directly on the main thread, blocking it for the full duration; the async versions are dispatched to libuv's thread pool (covered fully in its own dedicated question), letting the main thread continue running other work while the computation happens elsewhere. - 📌 Verified, not just described: a 10ms heartbeat timer, run alongside both, showed
pbkdf2Syncfreezing it completely (0 ticks) for the full duration of the computation, while the asyncpbkdf2doing the identical computation let the heartbeat tick 9 times during a comparable wall-clock duration — the exact same measurement technique used for blocking file I/O, applied here specifically to crypto. - This means the deliberate slowness that makes an algorithm like
scrypt/pbkdf2good for password hashing (covered in its own dedicated question) is not automatically a server-wide problem — using the async variant keeps that expensive computation from freezing every other concurrent request, because the actual work happens on a separate thread pool thread, not the main thread running the event loop. - The thread pool has a fixed, limited size (default 4, covered fully in its own dedicated question) — so async crypto is not free of contention either; enough concurrent expensive crypto calls will still queue behind that fixed pool, just without freezing the entire event loop the way the synchronous variant would.
- A precise answer names which crypto operations actually go through the thread pool (the deliberately slow, CPU-intensive ones —
pbkdf2/scrypt) versus lighter operations (hashing a small piece of data withcreateHash, HMAC) that are fast enough to run synchronously on the main thread with negligible blocking impact in practice, even though a synchronous API is used for them too. - The broader principle this demonstrates: "synchronous API" and "blocks the main thread" are the same fact stated twice for anything CPU-intensive — the fix is never a clever workaround, it is simply calling the asynchronous variant, which Node deliberately provides for exactly this reason.
Clarifying questions expected:
- "Is this specifically about the deliberately slow password-hashing functions, or crypto operations generally?" — the blocking concern is really about the CPU-intensive ones specifically.
- "Is the concurrency concern about one single slow operation, or many concurrent ones competing for the thread pool?" — decides whether async alone is sufficient or the thread pool's fixed size also needs consideration.
Code / implementation expected: Yes — the heartbeat-based measured comparison between pbkdf2Sync and pbkdf2 is the concrete, convincing proof, not a description of "async is non-blocking."