Konteks dan cakupan
Rancang worker pool untuk layanan backend yang menerima tugas (job), menjalankan paling banyak sejumlah tugas tetap secara bersamaan (konkuren), dan mati (shut down) tanpa membuang pekerjaan yang telah diterima secara diam-diam. Jelaskan kapasitas antrean, penerimaan atau penolakan, pembatalan, kepemilikan retry, korelasi hasil, metrik, dan graceful shutdown.
Asumsikan pekerjaan bersifat independen dan dapat memanggil API downstream. Pool ini adalah komponen in-process; antrean tahan lama (durable queue) termasuk dalam rancangan terpisah ketika pekerjaan harus tetap bertahan saat terjadi kegagalan proses.
Apa yang diuji oleh pewawancara
Pewawancara ingin melihat batas konkurensi yang jelas, model memori yang terbatas (bounded), dan kebijakan untuk kelebihan beban (overload). Mereka juga menguji apakah pembatalan sampai ke worker, apakah retry dapat melipatgandakan beban, dan apakah shutdown membedakan antara pekerjaan yang diantrekan, berjalan, selesai, dan ditolak.
Pertanyaan untuk diklarifikasi sebelum menjawab
- Apakah kehilangan pekerjaan saat proses crash dapat diterima? Jika tidak, tempatkan pekerjaan di durable broker terlebih dahulu.
- Apakah pekerjaan bersifat idempoten, dan dapatkah dicoba ulang dengan aman?
- Berapa batas laju (rate limit) downstream, rata-rata waktu eksekusi, dan target tail latency?
- Apakah pengiriman (submit) harus memblokir sementara, mengembalikan
429, atau membuang (shed) pekerjaan berprioritas rendah saat antrean penuh? - Apakah pembatalan berarti "berhenti sebelum mulai" saja, atau pekerjaan dapat menginterupsi I/O secara kooperatif?
Kerangka jawaban 30 detik
"Saya akan menyediakan API submit terbatas yang didukung oleh jumlah worker tetap dan antrean terbatas. Penerimaan mengembalikan hasil overload yang jelas ketika kapasitas habis, sementara konteks atau token pembatalan memungkinkan pekerjaan yang diantrekan keluar sebelum dieksekusi dan memungkinkan pekerjaan yang sedang berjalan berhenti secara kooperatif. Setiap pekerjaan yang diterima memiliki ID dan status terminal; retry dibatasi, diberi jitter, dan dimiliki oleh satu lapisan saja. Metrik mencakup kedalaman antrean, usia antrean, worker aktif, penolakan, waktu eksekusi, dan pembatalan. Shutdown menghentikan penerimaan baru, membatalkan pekerjaan dalam antrean sesuai kebijakan, menguras (drain) pekerjaan yang diterima hingga batas waktu (deadline), dan melaporkan apa pun yang tidak dapat diselesaikan."
Pembahasan mendalam langkah demi langkah
Langkah 1: Definisikan state machine
Gunakan submitted → queued → running → succeeded|failed|cancelled. Antrean yang penuh menghasilkan rejected daripada menunggu tanpa kepastian. Menyimpan status-status ini di luar proses diperlukan ketika pemanggil membutuhkan pemulihan setelah crash.
Langkah 2: Batasi worker dan antrean
Pilih W worker dan kapasitas antrean Q; memori dibatasi oleh Q muatan (payload) pekerjaan ditambah stack worker. Jangan membuat satu thread atau goroutine per request. ThreadPoolExecutor milik Python menyediakan max_workers, tetapi kebijakan penerimaan terbatas tingkat aplikasi tetap diperlukan saat volume pengiriman tidak terbatas.
Langkah 3: Pilih penerimaan dan backpressure
Tawarkan submit non-blocking, penantian terbatas, atau penolakan berbasis prioritas. Kembalikan kode overload yang stabil dan Retry-After hanya jika mencoba ulang aman dilakukan. Produsen yang menunggu selamanya pada antrean penuh dapat menyebabkan deadlock pada jalur request, sementara antrean tanpa batas mengubah overload menjadi peningkatan latensi dan memori.
submit(job, deadline):
if stopping or deadline expired: return REJECTED
if queue.try_push(job): return ACCEPTED(job.id)
if policy == WAIT and wait_until(deadline) and queue.try_push(job):
return ACCEPTED(job.id)
return OVERLOADEDLangkah 4: Jadikan pembatalan kooperatif
Teruskan konteks pembatalan ke setiap pekerjaan. Hapus pekerjaan yang dibatalkan yang belum dimulai; pekerjaan yang sedang berjalan harus memeriksa pembatalan pada titik-titik aman dan meneruskannya ke klien downstream. Pool tidak dapat mematikan thread sembarangan secara aman, jadi dokumentasikan arti 'dibatalkan' untuk I/O yang tidak dapat diinterupsi.
Langkah 5: Jaga kepemilikan retry pada satu lapisan
Pilih antara pool, job handler, atau durable queue untuk menjadwalkan retry. Batasi jumlah percobaan, tambahkan exponential backoff dengan jitter, dan klasifikasikan error menjadi error yang dapat dicoba ulang (retryable) dan permanen. Jika tidak, timeout pada tiga lapisan dapat menciptakan badai retry (retry storm) terhadap dependensi yang sama.
Langkah 6: Korelasikan hasil dan error
Kembalikan job ID atau future, jangan pernah membagikan slot hasil mutable bersama. Catat error asli, jumlah percobaan, dan status terminal. Jika pemanggil melakukan polling, buat retensi dan otorisasi penyimpanan hasil menjadi eksplisit; jika pemanggil menunggu (await), tentukan bagaimana pemutusan koneksi memengaruhi pekerjaan.
Langkah 7: Rancang graceful shutdown
Saat shutdown, hentikan penerimaan terlebih dahulu, lalu tandai pekerjaan dalam antrean sesuai kebijakan, dan biarkan pekerjaan yang sedang berjalan menguras tugasnya hingga batas waktu. Panduan pipeline Go menyebarkan pembatalan melalui sinyal done; prinsip yang sama berlaku di sini. Setelah batas waktu tercapai, laporkan pekerjaan yang belum selesai untuk diputar ulang (replay) atau tandai sebagai ditinggalkan hanya dengan kontrak yang eksplisit.
Langkah 8: Pantau bottleneck dengan instrumentasi
Lacak kedalaman dan usia antrean, worker aktif, utilisasi, jumlah diterima/ditolak/dibatalkan, durasi eksekusi, jumlah retry, dan error downstream. Pasang peringatan (alert) pada usia antrean dan penolakan yang berkelanjutan, bukan hanya CPU. Tentukan ukuran W dari bottleneck downstream: connection pool, rate limit eksternal, atau kapasitas CPU.
Pertukaran (Trade-offs) dan batasan
Pertukaran 1: Worker tetap atau penskalaan dinamis
Worker tetap membuat konkurensi dapat diprediksi dan melindungi dependensi. Penskalaan dinamis dapat meningkatkan throughput tetapi harus menerapkan batas global yang ketat dan memperhitungkan setiap replika; jika tidak, setiap instance akan melakukan penskalaan secara independen dan membebani dependensi secara berlebihan.
Pertukaran 2: Tolak atau tunggu saat penuh
Menolak memberikan umpan balik yang cepat kepada pemanggil dan menjaga latensi. Menunggu dapat meredam lonjakan beban (burst) singkat, tetapi penantian harus memiliki tenggat waktu dan tidak boleh menahan thread request yang langka tanpa batas waktu.
Pertukaran 3: Antrean in-process atau durable
Pool in-process memiliki latensi rendah dan sederhana. Durable broker menambahkan pemulihan, replay, dan biaya operasional. Gunakan opsi durable saat pekerjaan yang diterima harus bertahan melewati deploy, crash, atau perutean multi-instance.
Latihan kegagalan dan rencana evolusi
Latihan 1: Pemadaman downstream
Buat setiap pekerjaan gagal secara lambat dan verifikasi usia antrean, penolakan, batas waktu, dan retry yang dibatasi. Pastikan pool tidak menambah konkurensi saat dependensi sedang tidak sehat.
Latihan 2: Badai pembatalan
Kirim 10.000 pekerjaan, batalkan setengahnya sebelum dieksekusi, dan hentikan layanan selama proses pengurasan (drain). Verifikasi bahwa pembatalan dalam antrean tidak dijalankan dan pekerjaan yang diterima serta sedang berjalan mencapai status terminal yang dilaporkan.
Latihan 3: Peluncuran replika
Jalankan dua versi selama rolling deploy. Periksa apakah setiap instance menerapkan batas lokalnya dan apakah durable queue atau pembatas global menerapkan batas di seluruh sistem saat diperlukan.
Kesalahan umum dan tindak lanjut
Kesalahan 1: Buffering tanpa batas
Antrean tanpa batas menyembunyikan kelebihan beban hingga memori atau latensi runtuh. Buat kapasitas dan kebijakan penolakan dapat diobservasi.
Kesalahan 2: Retry duplikat
Jika klien HTTP dan job handler sama-sama melakukan retry, upaya percobaan akan berlipat ganda. Tetapkan satu pemilik retry dan teruskan metadata percobaan.
Kesalahan 3: Memperlakukan pembatalan sebagai pembunuhan thread
Sebagian besar runtime tidak dapat mematikan pekerjaan sembarangan secara aman. Gunakan pemeriksaan kooperatif, I/O yang dapat dibatalkan, dan kebijakan pekerjaan yang ditinggalkan secara jelas.
Kesalahan 4: Menguras tanpa menghentikan penerimaan
Pekerjaan baru dapat membuat antrean tidak pernah kosong. Tutup penerimaan sebelum menunggu batas waktu pengurasan (drain deadline).
Kesalahan 5: Melewatkan batas per-replika
Pool berisi 20 worker pada 10 replika berarti 200 panggilan konkuren. Nyatakan apakah batas tersebut bersifat lokal, di-shard, atau dikoordinasikan secara global.
Kesalahan 6: Tidak ada kebijakan retensi hasil
Future dan catatan polling memerlukan kedaluwarsa, otorisasi, dan jalur kegagalan. Jika tidak, pekerjaan yang diterima akan menjadi penyimpanan tak terbatas.