Topik wawancara representatif

Wawancara Backend: Bagaimana Anda Merancang Bounded Worker Pool?

BackendSedang
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Rancang bounded worker pool untuk layanan backend. Jelaskan kapasitas antrean, penerimaan atau penolakan, pembatalan, kepemilikan retry, korelasi hasil, metrik, dan graceful shutdown.

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.

text
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 OVERLOADED

Langkah 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.

Sumber publik

Pertanyaan terkait