Event loop giỏi I/O non-block. CPU hash lớn, image resize, driver chỉ có API sync → ôm loop = chết concurrency.
Offload: chạy việc đó trên thread/process pool; loop chỉ await “khi xong”.
Offload cái gì
- CPU-bound: nén, crypto nặng, parse khổng lồ, ML infer nhỏ.
- Sync I/O bắt buộc: legacy SDK block.
- Không offload: I/O đã async gọn — chỉ thêm hop và contention pool.
Pool size = ngân sách
Pool N threads
= tối đa N blocking in-flight
= inventory (xem inflight-inventory)
N quá lớn → thrash, contest DB
N quá nhỏ → queue offload dài, p99 nổ
Align với DB pool và CPU cores (CPU-bound ~ cores; I/O-block pool có thể > cores).
Fake async
// Vẫn kẹt loop — heavy chạy TRƯỚC khi await nhường
await Promise.resolve(heavySync())
// Node worker_threads (ý)
const { Worker } = require('worker_threads')
function offload(workerFile, data) {
return new Promise((resolve, reject) => {
const w = new Worker(workerFile, { workerData: data })
w.on('message', resolve)
w.on('error', reject)
})
}
await offload('./heavy.js', payload)
// hoặc pool worker tái sử dụng — đừng new Worker mỗi request không bound
Offload ≠ parallel magic
Đưa sang 1 worker = khỏi kẹt loop, chưa chắc nhanh hơn wall-clock nếu một việc. Nhiều core: data/task parallel.
Một câu
Offload cứu loop; pool size vẫn là ngân sách blocking
in-flight.
await không biến sync thành non-block nếu heavy chạy trên
loop.