Topik wawancara representatif

Wawancara Koding: Bagaimana Java 25 ScopedValue Mempropagasi Konteks Permintaan secara Aman?

CodingSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Bagaimana Java 25 ScopedValue mempropagasi konteks permintaan secara aman?

Pertanyaan dan use case

Layanan Java harus meneruskan ID pelacakan (trace ID), tenant, atau prinsipal keamanan ke callback tingkat dalam dan virtual thread tanpa menambahkan parameter ke setiap metode atau membocorkan state melalui pool yang digunakan kembali. Jelaskan cakupan leksikal ScopedValue di Java 25, binding dan rebinding, Carrier, virtual thread, serta konkurensi terstruktur, kemudian tentukan batasan untuk migrasi dari ThreadLocal.

Apa yang sedang diuji oleh pewawancara

  • Menjelaskan rentang visibilitas dan masa pakai (lifetime) dari konteks implisit.
  • Memahami bahwa binding ScopedValue hanya dapat dibaca di dalam cakupan run, call, atau where.
  • Membedakan propagasi snapshot yang immutable dari state thread-local yang mutable.
  • Menangani binding bersarang (nested), tugas turunan, exception, dan pembatalan (cancellation).
  • Mengenali risiko terkait prinsipal keamanan, objek mutable, dan migrasi pool.

Pertanyaan untuk diklarifikasi terlebih dahulu

  • Apakah konteks tersebut merupakan nilai hanya-baca (read-only) dengan cakupan permintaan atau state mutable yang harus ditulis kembali lintas thread?
  • Apakah tugas menggunakan virtual thread, konkurensi terstruktur, atau pool tradisional?
  • Haruskah tugas turunan mewarisi konteks tersebut, dan bolehkah mereka menimpa (override) suatu key?
  • Versi JDK apa saja yang didukung, dan bisakah migrasi dilakukan secara bertahap?

Jawaban tiga puluh detik

Modelkan konteks permintaan sebagai nilai immutable dan buat cakupan leksikal dengan ScopedValue.where(key, value).run atau call. Kode tingkat dalam membaca melalui key, dan binding akan hilang saat cakupan berakhir; cakupan bersarang dapat melakukan rebinding sementara, sedangkan Carrier bersifat immutable dan aman untuk thread (thread-safe). Hal ini memberikan masa pakai yang lebih ketat pada kode virtual-thread dan konkurensi terstruktur dibandingkan ThreadLocal yang mutable, tetapi ini bukan untuk state yang harus ditulis lintas cakupan. Pertahankan parameter eksplisit dan pengujian batasan selama migrasi.

Jawaban mendalam, langkah demi langkah

1. Tentukan context key

Gunakan key ScopedValue privat dan letakkan trace ID, tenant ID, serta prinsipal dalam Context yang immutable. Jangan mengekspos map mutable atau objek sesi yang dapat ditulis ke kode tingkat dalam.

2. Buat cakupan leksikal

java
static final ScopedValue<RequestContext> REQUEST = ScopedValue.newInstance();

ScopedValue.where(REQUEST, context).run(() -> handle(request));

Panggilan mendalam di dalam handle dapat membaca binding saat ini, tetapi tidak akan terlihat setelah cakupan berakhir. Masa pakai mengikuti blok kode, bukan reklamasi thread.

3. Membaca dan menangani ketiadaan nilai (absence)

Gunakan isBound() ketika ketiadaan nilai diperbolehkan, atau orElse untuk nilai default internal yang eksplisit. Konteks yang wajib seperti prinsipal keamanan harus menggunakan orElseThrow alih-alih berjalan tanpa peringatan sebagai anonim.

4. Jelaskan Carrier

ScopedValue.Carrier adalah pemetaan key-value yang immutable dan thread-safe; merangkai where akan menghasilkan carrier baru. Gabungkan beberapa binding dan panggil run atau call; jangan membagikan carrier mutable.

5. Tangani rebinding bersarang

where bagian dalam dapat mengikat nilai baru untuk key yang sama untuk sementara waktu, setelah itu nilai luar akan dipulihkan. Buat diagram pohon cakupan selama review agar callback, exception handler, dan log tidak membaca tenant atau prinsipal yang salah.

6. Gabungkan dengan virtual thread dan konkurensi terstruktur

Buat tugas turunan di dalam cakupan leksikal sehingga mereka menerima snapshot konteks yang diinginkan. Pembatalan atau penyelesaian dengan exception tetap akan keluar dari cakupan tanpa pembersihan thread-local manual; dokumentasikan tugas turunan mana yang boleh menimpa suatu key.

7. Bandingkan dengan ThreadLocal

ThreadLocal cocok untuk API lawas yang memerlukan asosiasi thread dan penulisan mutable, tetapi penggunaan kembali pool membuat pembersihan mudah terlupakan. ScopedValue mengutamakan visibilitas read-only, berumur pendek, dan terbatas serta tidak dapat menggantikan semua ThreadLocal, terutama state yang harus dimodifikasi lintas callback.

8. Rencanakan migrasi dan observabilitas

Tentukan skema konteks, pembaca yang diizinkan, dan matriks JDK, lalu gunakan adapter agar kode lama dapat bermigrasi secara bertahap. Catat kegagalan saat unbound, batas thread, penimpaan bersarang, dan referensi yang tertahan setelah permintaan selesai agar konteks implisit tidak menjadi variabel global yang tak terlihat.

Trade-off dan batasan

ScopedValue meneruskan referensi binding; jika nilainya mutable, data race dan perubahan hak akses tetap dapat terjadi. Ini tidak memberikan semantik bisnis secara otomatis ke sembarang thread baru dan tidak menggantikan parameter eksplisit, kebijakan autentikasi, atau protokol pembatalan. Terapkan API Java 25 dengan baseline JDK dan model konkurensi terstruktur yang jelas, sembari mempertahankan jalur kompatibilitas untuk rilis yang lebih lama.

Rencana peluncuran dan bukti

  1. Buat inventaris key ThreadLocal yang ada, penulis (writers), pembersihan, dan batasan thread.
  2. Buat key ScopedValue dan tipe data immutable untuk konteks permintaan read-only.
  3. Bungkus entry point permintaan dan pembuatan tugas terstruktur dengan where(...).run/call.
  4. Uji rebinding bersarang, exception, pembatalan, virtual thread, penggunaan kembali pool, dan akses unbound.
  5. Gunakan panduan API Java 25 Oracle tentang ScopedValue, Carrier, kontrol akses, dan konkurensi terstruktur sebagai bukti rilis.

Kesalahan umum dan tindak lanjut

Kesalahan 1: Memperlakukan ScopedValue sebagai variabel global yang mutable

Key mengontrol visibilitas, tetapi objek yang diikat mungkin masih bersifat mutable. Gunakan konteks immutable dan batasi operasi tulis.

Kesalahan 2: Melupakan batasan cakupan

Membaca setelah cakupan berakhir akan gagal atau mengembalikan binding luar. Identifikasi entry point, callback, dan pembuatan tugas asinkron secara eksplisit.

Kesalahan 3: Mengganti setiap ThreadLocal secara langsung

Penulisan lintas cakupan dan dukungan JDK yang lebih lama memiliki kendala yang berbeda. Klasifikasikan penggunaan, lalu migrasikan konteks permintaan read-only terlebih dahulu.

Kesalahan 4: Mengasumsikan setiap thread mewarisi secara otomatis

Propagasi konteks harus dirancang bersama pembuatan tugas, executor, dan konkurensi terstruktur; nama thread bukanlah sebuah kontrak propagasi.

Kesalahan 5: Mengabaikan penimpaan prinsipal

Rebinding bersarang dapat mengubah identitas audit. Jaga agar prinsipal tetap immutable, wajibkan jika diperlukan, dan lakukan audit terhadap setiap penimpaan.

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