Pertanyaan dan cakupan
Sebuah kluster memiliki puluhan ribu Pod dan custom resource, dan controller menjalankan permintaan LIST penuh setelah restart. Encoder lama melakukan serialisasi seluruh array items ke dalam satu buffer yang berdampingan, menciptakan lonjakan memori API-server. Rancang streaming encoding dan jelaskan cakupannya untuk JSON dan Kubernetes Protobuf, hubungannya dengan paginasi limit/continue dan gzip, pemulihan klien, backpressure, observabilitas, serta kondisi rollback.
Hal yang diuji oleh pewawancara
- Menemukan lonjakan pada buffer encoding alih-alih mereduksi masalah menjadi sekadar bandwidth jaringan.
- Memisahkan streaming encoding, paginasi, kompresi, dan semantik watch.
- Mempertahankan struktur JSON/Protobuf, resource version, dan semantik kesalahan untuk satu respons LIST.
- Menangani klien lambat, pemutusan koneksi (disconnects), peningkatan CPU, buffering proxy, dan kompatibilitas klien lama.
- Menentukan tolok ukur (benchmark) yang dapat direproduksi, metrik, canary, dan gerbang rollback.
Pertanyaan klarifikasi
- Apakah permintaan memiliki cakupan namespace (namespace-scoped) atau seluruh kluster (cluster-wide)? Ukuran objek, jumlah, dan permintaan LIST yang bersamaan (concurrent) menentukan anggaran memori.
- Haruskah klien menerima satu respons lengkap, atau boleh menggunakan
limit/continue? Paginasi membatasi ukuran hasil tetapi tidak menggantikan pengoptimalan encoding di sisi server. - Apakah HTTP/2, gzip, atau reverse proxy berada di jalur komunikasi? Buffering proxy mengubah manfaat end-to-end dari pelepasan memori selama proses encoding.
- Apakah JSON, Kubernetes Protobuf, atau keduanya diperlukan? Cakupan encoder mengubah urutan peluncuran (rollout).
Kerangka jawaban 30 detik
Pertama-tama, saya akan mengaitkan lonjakan memori dengan serialisasi seluruh array items, lalu mengubah encoder agar menulis secara inkremental: memancarkan awalan koleksi, melakukan encode dan menulis setiap item, lalu memancarkan akhiran. JSON dan Kubernetes Protobuf tetap mempertahankan semantik resource mereka; paginasi mengontrol ukuran hasil dan kompresi mengontrol bandwidth. Bounded buffer dan backpressure penulisan menangani klien yang lambat. Saya akan mengukur lonjakan RSS, CPU encoding, time to first byte, latensi penyelesaian, dan disconnects, melakukan canary JSON terlebih dahulu, lalu memvalidasi Protobuf, proxy, dan klien lama sebelum memperluas peluncuran. Melebihi batasan gerbang akan memicu rollback.
Jawaban mendalam
1. Dekomposisi lonjakan memori
Jalur lama dapat menampung daftar objek, status encoding perantara, dan buffer respons lengkap secara bersamaan. Seiring bertambahnya jumlah objek, byte respons tumbuh seiring total payload items; permintaan LIST yang bersamaan menambahkan lonjakannya masing-masing. Streaming hanya menjanjikan pengurangan memori sementara pada tahap encoding. Ini tidak menghilangkan memori yang diperlukan untuk pembacaan, cache, pengurutan, atau pemfilteran otorisasi. Konfirmasikan bottleneck dengan heap profile dan beban bersamaan sebelum mengubah encoder atau menambahkan rate limit.
2. Rancang encoder item-demi-item
Tulis field koleksi yang tetap terlebih dahulu. Setelah pembukaan items, lakukan serialisasi satu objek, tulis objek tersebut, dan lepaskan buffer sementaranya sebelum memproses objek berikutnya. Jangan menambahkan item ke byte string yang tidak terbatas (unbounded). Implementasi Kubernetes menargetkan encoder koleksi JSON dan Protobuf serta berfokus pada field items massal tanpa mengubah bentuk objek API.
write(listPrefix)
for item in items:
encoded = encodeOne(item)
writeWithBackpressure(encoded)
release(encoded)
write(listSuffix)Streaming mengubah masa pakai memori (memory lifetime), bukan urutan objek, metadata, resource version, atau definisi error. Jika klien terputus di tengah respons, hentikan encoding dan lepaskan item saat ini; body parsial bukanlah respons LIST yang sukses.
3. Pisahkan paginasi, kompresi, dan watch
limit/continue membagi satu koleksi menjadi halaman-halaman yang konsisten, mengurangi ukuran satu permintaan dan memungkinkan klien memprosesnya secara inkremental; hal ini memunculkan token kedaluwarsa, restart, dan loop klien. Streaming tetap penting ketika satu halaman berukuran besar karena streaming mengontrol lonjakan encoding di sisi server. gzip mengurangi byte jaringan, tetapi kompresor dapat menambahkan buffering, jadi ukur kebijakan flush-nya. watch adalah aliran event berkelanjutan dengan semantik pemulihan yang berbeda dan tidak dapat menggantikan satu LIST yang konsisten.
4. Tangani backpressure dan proxy
Ketika socket lambat, encoder harus menghormati penulisan yang terblokir dan pembatalan serta membatasi buffering per-koneksi. Jika tidak, encoder "streaming" masih dapat dikumpulkan kembali (reassembled) oleh proxy atau gateway. Catat waktu first-byte dan last-byte, membedakan antara waktu tunggu encoding, backpressure jaringan, buffering proxy, dan pembacaan klien yang lambat. Pemisahan frame HTTP/2 tidak membuktikan bahwa aplikasi telah melepaskan buffer respons lengkapnya; validasi heap server dan jalur penulisan.
5. Kompatibilitas, canary, dan rollback
Pertahankan struktur wire JSON/Protobuf dan negosiasi konten tanpa perubahan, termasuk continue, resource version, dan penanganan error. Aktifkan fitur ini terlebih dahulu untuk tipe resource dengan banyak objek dan konkurensi rendah, bandingkan lonjakan RSS, CPU, latensi first-byte, latensi penyelesaian, disconnects, dan tingkat error API, lalu perluas. Jika proxy lama tidak dapat menerima respons chunked atau terkompresi, pertahankan feature gate atau pilih encoder lama berdasarkan kapabilitas klien. Lakukan rollback jika terjadi regresi lonjakan memori, latensi P99, atau tingkat error, bukan hanya berdasarkan rata-rata throughput saja.
Contoh jawaban berkualitas tinggi
Saya akan membatasi cakupan masalah pada lonjakan encoding API-server. Saya akan mereproduksinya dengan beban LIST yang bersamaan, lalu membuat encoder memancarkan awalan koleksi, melakukan serialisasi dan menulis setiap item, melepaskan buffer sementaranya, dan memancarkan akhiran. Hal ini mengurangi memori sementara pada tahap encoding; ini tidak menghilangkan biaya membaca, mengurutkan, atau mengotorisasi objek. JSON dan Kubernetes Protobuf mempertahankan bentuk wire-nya, limit/continue tetap berfungsi sebagai paginasi, gzip tetap sebagai kontrol bandwidth, dan watch tetap sebagai kontrak event yang berbeda. Operasi penulisan mematuhi backpressure socket dan pembatalan, serta buffering proxy diukur secara terpisah. Saya akan melakukan canary pada JSON, kemudian Protobuf dan proxy utama, membandingkan lonjakan RSS, CPU encoding, waktu first-byte, latensi penyelesaian, dan disconnects. Sebuah gate menonaktifkan fitur atau memilih encoder lama ketika regresi melampaui baseline. Pengujian mencakup klien lambat, disconnects, daftar kosong, item besar, continue token yang kedaluwarsa, dan klien lama.
Kesalahan umum
- Hanya menambahkan paginasi → satu halaman masih bisa berukuran besar dan di-buffer sepenuhnya → validasi paginasi dan streaming item secara bersamaan.
- Memperlakukan gzip sebagai perbaikan memori → kompresi dapat menahan buffer → ukur tahap encoding, kompresi, dan socket secara terpisah.
- Memperlakukan pemisahan frame HTTP/2 sebagai streaming server → aplikasi mungkin masih membangun full body → periksa heap API-server dan jalur penulisan.
- Membiarkan buffering tak terbatas untuk klien lambat → koneksi lambat yang bersamaan memperbesar penggunaan memori → batasi koneksi, pembatalan, dan backpressure.
- Mengubah bentuk LIST atau resource version → semantik klien dan konsistensi menjadi rusak → ganti hanya masa pakai encoding dan jalankan regresi wire.
Pertanyaan lanjutan dan tanggapan
Apa yang terjadi jika klien terputus setelah objek ke-3.000?
Batalkan konteks encoding, hentikan pembacaan objek-objek selanjutnya, lepaskan buffer saat ini, dan catat alasannya. Klien tidak dapat memperlakukan body parsial sebagai respons LIST yang berhasil; klien harus memulai ulang dari batas paginasi awal jika membutuhkan set data lengkap.
Jika paginasi membatasi satu halaman hingga 500 objek, mengapa harus di-stream juga?
Lima ratus objek masih bisa berukuran sangat besar, terutama custom resource. Paginasi membatasi set hasil; streaming membatasi memori server sementara selama proses encoding. Keduanya menangani tahap yang berbeda dan dapat digabungkan.
Apakah desain ini gagal jika proxy mem-buffer seluruh respons sebelum meneruskannya?
Server masih dapat mengurangi lonjakan encoding-nya sendiri, tetapi manfaat first-byte dan pelepasan end-to-end bagi klien akan hilang. Perlakukan buffering proxy sebagai prasyarat penerapan (deployment prerequisite), catat waktu first-byte berdasarkan rute, dan nonaktifkan atau persempit canary jika proxy tidak dapat melakukan streaming.