Topik wawancara representatif

Wawancara coding: Bagaimana asyncio seharusnya mengelola deadline dinamis?

CodingSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Permintaan asinkron mengetahui sisa anggarannya hanya setelah membaca metadata upstream. Rancanglah dengan asyncio.timeout atau timeout_at, dan jelaskan reschedule, expired, CancelledError, TimeoutError, nesting, dan wait_for.

Prompt dan konteks

Sebuah permintaan asinkron membaca metadata perutean sebelum mengetahui anggaran untuk panggilan downstream. Desain harus menggunakan jam monotonik milik event loop, membuat timeout tanpa deadline awal, menjadwalkan ulang deadline absolut setelah metadata tiba, dan menjaga pembatalan dari pemanggil tetap terpisah dari sebuah timeout.

Python mendokumentasikan asyncio.timeout() sebagai pengelola konteks asinkron yang dapat dijadwalkan ulang dan timeout_at() sebagai deadline absolut berdasarkan jam event loop. Pembatalan yang disebabkan oleh konteks diubah menjadi TimeoutError di luar konteks.

Apa yang sedang diuji oleh pewawancara

Mencari perbedaan yang jelas antara durasi relatif dan deadline absolut, timeout bisnis dan pembatalan eksternal, ditambah penggunaan yang benar dari reschedule(), expired(), nesting, dan wait_for(). Jawaban yang kuat menyebarkan satu anggaran ke setiap I/O dan membersihkan sumber daya.

Pertanyaan klarifikasi untuk diajukan terlebih dahulu

  • Komponen mana yang memiliki anggaran tersebut, dan berapa banyak yang telah terpakai untuk membaca metadata?
  • Apakah klien HTTP, database, dan antrean menerima sinyal timeout atau pembatalan?
  • Apakah panggilan anak harus berbagi satu deadline absolut atau memiliki anggaran independen?
  • Apakah timeout merupakan respons terdegradasi, percobaan ulang (retry), atau kesalahan?
  • Berapa versi minimum Python di lingkungan produksi?

Kerangka jawaban 30 detik

"Saya menghitung deadline absolut dengan loop.time(). Saya mulai dengan asyncio.timeout(None), kemudian memanggil cm.reschedule(deadline) setelah metadata tiba. Pembatalan yang dihasilkan oleh konteks menjadi TimeoutError saat keluar, jadi saya menangkapnya di luar; CancelledError eksternal tetap menjadi pembatalan setelah pembersihan. Setiap operasi downstream menerima sisa anggaran, dan panggilan bersarang tidak dapat mereset timeout penuh. Saya menggunakan cm.expired() untuk diagnosis dan menguji kondisi race."

Pembahasan mendalam langkah demi langkah

Langkah 1: Nyatakan deadline dengan jam monotonik

Jangan menghitung sisa waktu dari waktu jam dinding (wall-clock) karena koreksi jam dapat melompat. Gunakan loop.time() dan teruskan satu deadline absolut ke setiap operasi downstream.

python
loop = asyncio.get_running_loop()
deadline = loop.time() + 2.0
async with asyncio.timeout_at(deadline):
    await call_dependency(deadline)

Langkah 2: Mulai konteks yang dapat dijadwalkan ulang

Saat anggaran belum diketahui, gunakan asyncio.timeout(None) as cm. Setelah metadata tiba, hitung deadline absolut dan panggil cm.reschedule(deadline). Pertahankan satu konteks sehingga pekerjaan awal dan panggilan downstream berbagi satu batasan yang sama.

Langkah 3: Sebarkan sisa anggaran

Setiap klien menghitung deadline - loop.time() dan memperlakukan nilai non-positif sebagai kegagalan langsung. Meneruskan deadline yang sama mencegah lapisan perutean, database, dan HTTP masing-masing memberikan timeout relatif penuh.

Langkah 4: Pahami konversi pembatalan-ke-timeout

Konteks membatalkan Task saat ini secara internal dan mengubah pembatalan tersebut menjadi TimeoutError saat keluar. Akibatnya, TimeoutError harus ditangkap di luar async with, bukan di dalamnya.

python
try:
    async with asyncio.timeout(1.0):
        await slow_call()
except TimeoutError:
    return degraded_result()

Langkah 5: Pertahankan pembatalan eksternal

Jika klien terputus atau induk membatalkan permintaan, CancelledError bukanlah timeout bisnis. Lepaskan koneksi, kunci, dan file sementara, lalu raise kembali alih-alih mengembalikan keberhasilan yang terdegradasi.

Langkah 6: Bandingkan timeout, timeoutat, dan waitfor

timeout(delay) menggunakan penundaan relatif dan dapat dijadwalkan ulang; timeout_at(when) mengambil deadline monotonik absolut dan ideal untuk penyebaran (propagasi). wait_for(aw, timeout) menargetkan satu awaitable, membatalkannya saat timeout, dan dapat menunggu hingga pembatalan selesai. Menggabungkan banyak panggilan wait_for dapat menghabiskan anggaran permintaan secara berlebihan.

Langkah 7: Tangani nesting dan kedaluwarsa

Konteks timeout dapat bersarang (nest); deadline bagian dalam tidak boleh melebihi deadline bagian luar. Setelah keluar, cm.expired() memberi tahu apakah konteks benar-benar mencapai deadline-nya. Pengecualian biasa, pembatalan eksternal, dan hasil yang berhasil harus tetap dapat dibedakan.

Langkah 8: Uji race condition dan pembersihan

Uji metadata yang tertunda, deadline yang sudah kedaluwarsa, timeout bagian dalam terlebih dahulu, pembatalan eksternal dan timeout yang bersamaan, operasi downstream yang mengabaikan pembatalan, reschedule(None), penjadwalan ulang berulang, dan kegagalan pembersihan. Catat deadline, sisa anggaran, alasan pembatalan, dan durasi downstream tanpa payload sensitif.

Model jawaban berkualitas tinggi

"Saya membuat satu deadline absolut dengan loop.time() dan meneruskannya ke downstream. Saat anggaran belum diketahui, saya masuk ke timeout(None) dan menjadwalkan ulang setelah metadata tiba. Konteks mengubah pembatalan internalnya menjadi TimeoutError di luar blok; CancelledError eksternal terus merambat. Panggilan bersarang menggunakan deadline paling awal, dan klien serta database menerima sisa anggaran. Pengujian race memverifikasi tidak ada Task atau sumber daya yang bocor."

Kesalahan umum

  • Menggunakan time.time() untuk deadline → koreksi jam mengubah anggaran → gunakan loop.time().
  • Menangkap TimeoutError di dalam konteks → pengecualian yang diubah tidak ada di sana → tangkap di luar.
  • Memperlakukan pembatalan eksternal sebagai timeout → pembatalan klien menjadi hasil terdegradasi palsu → jaga agar tipe pengecualian tetap berbeda.
  • Memberikan timeout penuh di setiap lapisan → total latensi melebihi kontrak → sebarkan satu deadline absolut.
  • Mengabaikan penungguan pembatalan wait_for → durasi sebenarnya melebihi batas waktu numerik → ukur konvergensi pembatalan.
  • Hanya menguji keberhasilan dan satu timeout → jalur race membocorkan sumber daya → uji penjadwalan ulang, pembatalan simultan, dan kegagalan pembersihan.

Pertanyaan lanjutan dan respons yang kuat

Pertanyaan lanjutan 1: Mengapa timeout_at lebih stabil daripada timeout per lapisan?

Setiap lapisan menargetkan instan absolut yang sama, sehingga pembulatan waktu relatif dan anggaran berulang tidak dapat terakumulasi melebihi kontrak permintaan.

Pertanyaan lanjutan 2: Bagaimana jika reschedule menerima deadline masa lalu?

Konteks akan kedaluwarsa pada kesempatan event loop berikutnya. Periksa sisa anggaran sebelum menjadwalkan ulang dan hindari memulai I/O yang tidak dapat dibatalkan ketika anggaran sudah habis.

Pertanyaan lanjutan 3: Bagaimana database mematuhi deadline?

Teruskan sisa detik ke statement timeout atau API pembatalan. Membatalkan Task Python saja belum tentu menghentikan kueri di sisi server.

Pertanyaan lanjutan 4: Apa yang Anda catat saat pembatalan dan timeout bersaing (race)?

Pertahankan alasan pembatalan induk dan jumlah pembatalan, serta sebarkan pembatalan eksternal. Simpan expired() sebagai bidang diagnostik alih-alih melabeli setiap proses keluar sebagai timeout bisnis.

Pertanyaan lanjutan 5: Kapan Anda akan memilih wait_for?

Gunakan untuk satu awaitable dengan batas relatif sederhana. Lebih pilih timeout atau timeout_at ketika panggilan berbagi deadline dinamis atau anggaran di seluruh permintaan.

Sumber publik

Pertanyaan terkait

Alat wawancara terkait

Gunakan Tangkapan Layar untuk perintah coding

Ambil tangkapan layar soal, lalu telusuri batasan, solusi, kode, edge case, dan kompleksitas secara berurutan.

Lihat alat