Perintah dan konteks
Sebuah layanan Node.js menyimpan requestId, tenant, dan data audit di AsyncLocalStorage. Entri HTTP dapat membaca store tersebut, tetapi beberapa log kehilangan konteks setelah callback driver database, event emitter, objek mirip-Promise kustom, atau batas worker. Tim baru saja meningkatkan dari Node.js 22 ke 24 dan ingin mengetahui apakah perubahan implementasi default ada hubungannya. Rancang rencana diagnosis, perbaikan, regresi, dan fallback.
Catatan rilis Node.js 24 mencatat bahwa AsyncLocalStorage kini menggunakan AsyncContextFrame secara default. Dokumentasi resmi masih menyatakan bahwa API berbasis callback atau thenable kustom mungkin memerlukan AsyncResource untuk mengaitkan pekerjaan asinkron dengan konteks eksekusi yang benar. Wawancara ini menguji pemahaman batas asinkron, observabilitas, dan migrasi versi daripada langsung menyalahkan runtime atas setiap kehilangan konteks.
Apa yang dinilai oleh pewawancara
Pewawancara mencari pemahaman pembeda antara konteks yang dibuat oleh run(), masa pakai sumber daya asinkron, batas lintas-thread, dan kode bisnis yang menimpa store. Jawaban yang kuat menggunakan reproduksi minimal untuk menemukan pemutusan pertama, menghindari penggunaan enterWith() secara membabi buta, dan mendefinisikan verifikasi untuk callback pihak ketiga, jalur error, diagnostik tersampel, dan versi Node.
Pertanyaan klarifikasi yang perlu diajukan
- Apakah kehilangan terjadi di dalam event loop yang sama, worker, child process, atau batas jaringan?
- Apakah library menggunakan native promise, callback, event emitter, atau thenable kustom?
- Apakah store tidak sengaja tertimpa oleh
run()lain,enterWith(), atau tugas asinkron yang digunakan kembali? - Apakah kehilangan requestId memengaruhi kebenaran audit dan penagihan, atau hanya korelasi log?
- Apakah opsi startup, versi dependensi, dan switch eksperimental identik di Node.js 22 dan 24?
Jawaban 30 detik
"Saya akan merekam snapshot store yang tidak dapat diubah (immutable) pada entri, setiap batas asinkron, dan log akhir, lalu menggunakan reproduksi minimal untuk menemukan kehilangan pertama alih-alih menyalahkan Node 24. Rantai promise bawaan harus menggunakan run() pada batas permintaan. Jika callback pihak ketiga atau thenable kustom gagal mempropagasi konteks, saya akan menggunakan AsyncResource di mana tugas dibuat dan di mana callback-nya dijalankan. Saya akan menghindari kontaminasi enterWith() global dan melakukan uji regresi Node 22/24, error, batas waktu (timeout), dan worker secara terpisah. Perbaikan dibuktikan oleh kelengkapan requestId dan kebenaran bisnis."
Jawaban mendalam langkah demi langkah
1. Definisikan kontrak konteks
Tentukan field store, masa pakai, dan ketetapan (immutability). Buat store baru per permintaan; kode downstream dapat membaca atau memperoleh nilai tetapi tidak boleh berbagi objek yang dapat diubah (mutable) lintas permintaan. Apakah hilangnya requestId hanya masalah logging atau memengaruhi otorisasi dan isolasi tenant menentukan kondisi penghentian dan prioritas perbaikan.
2. Temukan kehilangan pertama dengan snapshot
Ambil ringkasan field dari getStore() pada entri, sebelum dan sesudah panggilan database, di event listener, callback promise, timeout, error handler, dan logging akhir. Catat hanya hash atau ID permintaan pendek, jangan pernah data sensitif tenant. Beri label pada setiap batas asinkron dan temukan transisi pertama dari nilai menjadi undefined daripada hanya memeriksa error terakhir.
import { AsyncLocalStorage } from 'node:async_hooks';
const requestContext = new AsyncLocalStorage();
function contextSnapshot(label) {
const store = requestContext.getStore();
return { label, requestId: store?.requestId ?? null };
}3. Pisahkan run(), enterWith(), dan asosiasi sumber daya
run(store, callback) menyediakan store di dalam callback dan pekerjaan asinkron yang dibuatnya, menjadikannya batas permintaan yang baik. enterWith() memperluas konteks ke penanganan event berikutnya dalam eksekusi sinkron yang sama dan dapat mengontaminasi listener, sehingga tidak boleh dijadikan perbaikan umum tanpa batas yang terbukti. Library callback yang tidak membuat sumber daya asinkron dengan benar memerlukan wrapper AsyncResource.
4. Bungkus batas callback pihak ketiga
Pertama, periksa apakah library sudah menggunakan sumber daya asinkron Node dengan benar. Jika tidak, buat satu AsyncResource per tugas, panggil callback dengan runInAsyncScope, dan hancurkan sumber daya saat tugas selesai. Jangan menggunakan kembali satu sumber daya di berbagai permintaan atau hanya menambal logger dengan requestId; itu menyembunyikan pemutusan konteks yang sebenarnya.
import { AsyncResource } from 'node:async_hooks';
function bindCallback(callback) {
const resource = new AsyncResource('third-party-callback');
return (...args) => resource.runInAsyncScope(callback, null, ...args);
}5. Uji thenable, event, dan worker secara terpisah
Thenable kustom mungkin tidak mengikuti propagasi konteks Promise bawaan. Event emitter dapat memanggil listener pada tick berikutnya. Worker atau child process memiliki konteks eksekusi independen dan tidak dapat membagikan store secara implisit. Tulis pengujian minimal untuk setiap batas, mendokumentasikan field mana yang melintas melalui pesan eksplisit dan mana yang dibuat ulang pada batas permintaan baru.
6. Uji regresi Node 22/24 dan amati hasilnya
Sertakan perubahan implementasi default Node 24 dalam matriks peningkatan, tetapi jangan mengganti analisis akar masalah dengan perbandingan versi semata. Pastikan flag startup, versi dependensi, dan mode eksekusi sama persis, lalu bandingkan kelengkapan requestId, pemutusan batas, tingkat error, latensi, dan throughput. Pertahankan diagnostik batas berkecepatan rendah setelah rilis; jika field otorisasi atau tenant hilang, hentikan canary dan kembali ke versi stabil.
Contoh jawaban berkualitas tinggi
Saya akan mendefinisikan kontrak store dan mencatat snapshot non-sensitif pada entri, batas asinkron utama, dan log akhir untuk menemukan transisi pertama dari nilai ke kosong. Rantai native Promise menggunakan run() pada batas permintaan. Jika callback pihak ketiga atau thenable kustom gagal mempropagasi konteks, wrapper membuat satu AsyncResource per tugas dan memanggil callback dengan runInAsyncScope, lalu menghancurkannya. Saya tidak akan menutupi masalah dengan enterWith() global. Uji event emitter, timeout, error, worker, dan Node 22/24 secara terpisah, membandingkan kelengkapan requestId dan field bisnis. AsyncContextFrame Node 24 adalah variabel versi dalam matriks pengujian, bukan satu-satunya penjelasan.
Kesalahan umum
- Menyalahkan Node 24 atas setiap kehilangan → Batas pihak ketiga mungkin merupakan titik putusnya → Gunakan reproduksi minimal dan snapshot batas terlebih dahulu.
- Memanggil
enterWith()di mana-mana → Event listener dapat saling mengontaminasi → Pilihrun()dengan cakupan permintaan. - Menambahkan requestId hanya di logger → Konteks tenant atau audit tetap hilang → Perbaiki asosiasi sumber daya asinkron.
- Menggunakan kembali satu
AsyncResourceuntuk semua permintaan → Konteks saling mengontaminasi → Buat dan hancurkan satu per tugas. - Mengasumsikan worker mewarisi store → Konteks lintas-thread tidak bersifat implisit → Kirim field yang diperlukan secara eksplisit dalam pesan.
Pertanyaan lanjutan dan respons
Bagaimana Anda memilih antara run() dan enterWith()?
Pilih run() untuk batas permintaan atau tugas karena cakupannya mengikuti callback dan pekerjaan asinkronnya. enterWith() memengaruhi penanganan event berikutnya dalam eksekusi sinkron saat ini dan hanya boleh digunakan ketika batasnya eksplisit dan kontaminasi listener telah dikesampingkan.
Kapan AsyncResource diperlukan?
Gunakan ketika API callback, pembungkus event, atau thenable kustom gagal menghubungkan operasi asinkronnya ke grafik sumber daya asinkron Node, sehingga callback dapat berjalan dalam konteks tempat tugas tersebut dibuat.
Bagaimana cara mempertahankan requestId di worker?
Worker memiliki konteks eksekusi independen, jadi teruskan requestId atau pengenal tenant minimum dalam pesan, buat store AsyncLocalStorage baru di entri worker, dan hindari mengirim objek sensitif.
Akankah AsyncContextFrame di Node 24 memperbaiki setiap kehilangan?
Tidak. Ini mengubah implementasi default, tetapi callback pihak ketiga, enterWith() yang salah, thenable kustom, dan batas lintas-thread masih memerlukan pengujian terpisah.
Bagaimana Anda menunjukkan bahwa perbaikan tersebut tidak menambah overhead?
Bandingkan latensi, throughput, CPU, memori, dan kelengkapan requestId di bawah lalu lintas dan versi dependensi yang identik, dan amati jumlah sumber daya yang dibuat untuk callback terikat daripada hanya mengandalkan satu tolok ukur (benchmark).