Perintah dan konteks
Sebuah layanan Node.js menerima unggahan berukuran besar dan harus menjalankan dekompresi, pemindaian virus, validasi format, serta penulisan ke penyimpanan objek secara berurutan. Implementasi lama mendengarkan event data secara manual, terkadang mengalami lonjakan memori, terus berjalan setelah klien terputus, dan meninggalkan file sementara ketika langkah perantara gagal.
Gunakan stream.compose() atau pendekatan komposisi pipeline yang setara. Jelaskan bagaimana tahapan readable, writable, dan Transform terhubung, serta bagaimana backpressure, AbortSignal, propagasi error, dan pembersihan akhir menjaga satu tugas tetap terkendali.
Hal yang diuji oleh pewawancara
- Apakah Anda membedakan batasan dan siklus hidup dari
pipe,pipeline, dancompose. - Apakah Anda dapat menjelaskan bagaimana backpressure membatasi produksi alih-alih hanya meningkatkan batas antrean.
- Apakah pembatalan, eksepsi, dan pemutusan koneksi klien merambat ke seluruh pipeline.
- Apakah Anda menangani async generator, pelepasan sumber daya, penulisan idempoten, dan observabilitas.
Pertanyaan untuk klarifikasi
- Apakah input berasal dari permintaan HTTP, file, atau SDK penyimpanan objek? Apakah output multipart dan percobaan ulang (retry) didukung?
- Apakah setiap tahapan berupa Node stream, Web Stream, AsyncIterable, atau fungsi biasa?
- Apakah pemindaian dan validasi membuat child process, file sementara, atau status database?
- Dapatkah penyimpanan objek membatalkan unggahan multipart, dan apakah pekerjaan yang dibatalkan harus dilanjutkan kembali?
Jawaban 30 detik
Saya akan mendefinisikan setiap tahapan sebagai readable, writable, Transform, atau AsyncIterable dan menggabungkannya ke dalam sebuah Duplex dengan stream.compose(), lalu membiarkan pipeline menggerakkan destinasi akhir. Produsen hanya melanjutkan jika konsumen di hilir (downstream) dapat menerima data, sehingga backpressure membatasi penggunaan memori. Pemutusan permintaan, batas waktu (deadline), dan pembatalan bisnis berbagi satu AbortSignal yang diteruskan ke tahapan yang mendukung; error apa pun akan menggagalkan seluruh rangkaian. File sementara, child process, dan unggahan multipart dibersihkan di dalam handler finally atau abort, dan proses penulisan menggunakan kunci idempotensi. Metrik mencakup throughput, kedalaman antrean, puncak RSS, alasan pembatalan, dan hasil pembersihan.
Pembahasan mendalam langkah demi langkah
Tentukan kontrak setiap tahapan
Tentukan tipe input dan output, ukuran chunk, apakah null diizinkan, perilaku pemblokiran, dan kepemilikan sumber daya saat selesai. Async generator harus mengonsumsi sumbernya dan menghasilkan (yield) data sesuai permintaan; fungsi biasa tidak boleh membaca seluruh file ke dalam memori secara diam-diam.
Susun tahapan yang dapat digunakan kembali
compose menghubungkan stream, AsyncIterable, atau fungsi ke dalam Duplex baru dan menangani tahapan yang berdekatan dengan semantik pipeline. Contoh:
import { compose } from 'node:stream';
async function* validate(source) {
for await (const chunk of source) {
checkChunk(chunk);
yield chunk;
}
}
const processing = compose(decompress(), validate, scan());Implementasi sebenarnya harus menghubungkan pemrosesan ke writable tujuan serta memusatkan penyelesaian dan error alih-alih mengabaikannya di setiap tahapan.
Jadikan backpressure sebagai control plane default
Saat hilir belum siap, tahapan Readable dan Transform harus berhenti memproduksi data. Jangan melakukan push tanpa batas di dalam callback data atau menyembunyikan konsumen yang lambat dengan terus menaikkan batas high-water mark. Uji beban harus mencatat setiap antrean, throughput, dan RSS sehingga tahapan paling lambat yang menentukan kecepatan total.
Sebarkan pembatalan dan pemutusan koneksi
Gabungkan pemutusan permintaan, batas waktu, dan pembatalan manual ke dalam satu AbortController. Teruskan sinyalnya ke tahapan compose yang mendukung dan SDK eksternal; setelah dibatalkan, hentikan pembacaan, hancurkan (destroy) hilir, dan tunggu hingga tertutup. Log harus membedakan antara pembatalan normal, kegagalan bisnis, dan error jaringan.
Pusatkan penanganan error dan pembersihan
Satu pemilik harus memanggil pipeline/compose, menerima error pertama, dan menghancurkan rangkaian tersebut. File sementara, pemindai, soket, dan unggahan multipart harus dilepaskan saat sukses, gagal, maupun dibatalkan; kegagalan pembersihan memicu peringatan beserta ID pekerjaan tanpa menggantikan error asli.
Rancang idempotensi dan metrik
Hasilkan kunci idempotensi dari ID unggahan dan versi tahapan, lalu lakukan commit status bisnis hanya setelah penulisan objek selesai. Catat byte input dan output, durasi, puncak RSS, alasan pembatalan, tahapan yang gagal, dan waktu pembersihan. Lakukan percobaan ulang hanya dari batas yang dapat dipulihkan untuk menghindari penulisan ganda.
Jawaban model
Saya akan mendefinisikan unggahan, dekompresi, pemindaian, validasi, dan penyimpanan sebagai tahapan dengan input, output, dan kepemilikan yang eksplisit, menyusunnya ke dalam pipeline readable, writable, atau async-iterable, serta membiarkan satu pemilik pipeline mengendalikannya. Konsumsi di hilir mengatur backpressure; buffering tanpa batas di dalam handler data dilarang. Pemutusan koneksi, batas waktu, dan pembatalan manual berbagi AbortSignal yang diteruskan ke tahapan yang mendukung dan SDK penyimpanan. Pemilik menangani error pertama dan menghancurkan rangkaian; file sementara, pemindai, dan unggahan multipart dibersihkan pada kondisi sukses, gagal, maupun dibatalkan. Kunci idempotensi melindungi penulisan, metrik mencakup throughput, antrean, RSS, pembatalan, dan pembersihan, serta percobaan ulang hanya dimulai dari batas yang aman.
Kesalahan umum
- Mengumpulkan seluruh stream ke dalam Buffer tetapi tetap mengklaim menggunakan compose.
- Hanya mendengarkan
errordari writable akhir, sehingga melewatkan kegagalan async-generator atau child-process. - Terus membaca dan menulis setelah klien terputus.
- Mengatasi backpressure dengan terus meningkatkan high-water mark tanpa batas.
- Menghapus file sementara hanya saat sukses, mengabaikan pembatalan dan eksepsi.
- Mencoba ulang tanpa kunci idempotensi sehingga menduplikasi objek atau status bisnis.
Pertanyaan lanjutan
Apa perbedaan antara compose dan pipeline?
Compose membuat Duplex yang dapat digunakan kembali dari tahapan-tahapan yang ada; pipeline menggerakkan koneksi end-to-end, menyebarkan error, dan menunggu hingga ditutup. Keduanya dapat digabungkan, tetapi kepemilikan dan penanganan error harus dipusatkan.
Apa yang terjadi jika sebuah async generator melempar error?
Stream yang digabungkan harus gagal dan menghancurkan tahapan-tahapan di dekatnya. Penelepon tetap menunggu pembersihan pipeline dan mencatat tahapan yang gagal alih-alih hanya mengandalkan keluarnya proses (process exit).
Bagaimana cara memverifikasi bahwa backpressure berfungsi?
Lakukan uji beban dengan produsen yang lebih cepat daripada sink lambat yang terkontrol, amati kedalaman antrean dan RSS yang terbatas, dan pastikan throughput mengikuti tahapan paling lambat alih-alih melakukan buffering tanpa batas.
Bisakah langsung mencoba ulang setelah pembatalan?
Pertama, pastikan semua sumber daya telah ditutup dan status sementara dapat diidentifikasi. Coba lagi dari batas pemulihan yang idempoten; lakukan kompensasi atau tandai status yang tidak diketahui untuk penulisan eksternal yang tidak dapat diinterupsi.