Request Deduplication — adalah mekanisme penggabungan permintaan paralel identik menjadi satu, sehingga sumber data hanya menerima satu panggilan, bukan puluhan. Dalam aplikasi seluler, deduplikasi sangat penting: beberapa layar dapat meminta profil pengguna yang sama atau daftar produk secara bersamaan. Menurut Square Engineering (2024), penerapan deduplikasi mengurangi beban API mereka sebesar 30% tanpa mengubah logika server.
Utama
Request Deduplication — adalah teknik yang mencegah eksekusi beberapa permintaan identik ke satu sumber data dalam satu jendela waktu. Alih-alih mengirim 10 permintaan HTTP identik, sistem mengirim satu, dan 9 lainnya menunggu hasilnya.
Masalah permintaan duplikat sangat akut dalam aplikasi seluler dengan arsitektur berbasis status (MVVM, MVI, Redux). Ketika beberapa pengamat berlangganan data yang sama dalam waktu singkat, masing-masing memulai permintaannya sendiri, menciptakan beban berlebihan. Menurut Uber Engineering (2024), hingga 18% dari semua permintaan di klien seluler Uber adalah duplikat, dan deduplikasi di sisi klien mengurangi jumlahnya sebanyak 4 kali lipat.
Deduplikasi tidak sama dengan caching. Cache menyimpan hasil permintaan setelah eksekusi. Deduplikasi mencegah permintaan berlebihan sebelum dan selama eksekusinya. Setelah permintaan selesai, cache mulai berlaku dan menyimpan hasilnya.
class DeduplicatorT(
private val source: suspend () -> T
) {
private val inFlight = ConcurrentHashMap<String, Deferred<T>>()
suspend fun get(key: String): T = inFlight.getOrPut(key) {
async {
source().also { inFlight.remove(key) }
}
}.await()
}
Kelas Kotlin ini menjamin bahwa untuk setiap kunci hanya satu coroutine yang dieksekusi. Semua panggilan paralel dengan kunci yang sama menunggu satu Deferred. Setelah selesai, kunci dihapus dan permintaan berikutnya dieksekusi secara normal.
Mengurangi beban server — alasan pertama dan paling jelas. Setiap permintaan duplikat mengonsumsi sumber daya server: CPU, memori, koneksi basis data. Dalam skala jutaan perangkat, bahkan 10–15% permintaan duplikat menciptakan beban signifikan yang memerlukan server tambahan.
Menghemat baterai dan trafik — setiap permintaan HTTP pada perangkat seluler mengonsumsi energi modul radio. Menurut Google I/O (2025), satu permintaan gagal atau duplikat dapat menghabiskan hingga 15% energi satu sesi jaringan. Deduplikasi mengurangi jumlah pengaktifan modul radio, memperpanjang waktu kerja perangkat dengan baterai.
Menghindari konflik data — jika dua permintaan duplikat menulis data ke penyimpanan lokal, race condition dapat terjadi: permintaan kedua dapat menimpa hasil pertama dengan data usang. Deduplikasi menjamin bahwa penulisan ke penyimpanan lokal dilakukan sekali, menghilangkan kondisi balapan.
Meningkatkan UX — pengguna tidak melihat beberapa indikator pemuatan untuk data yang sama. Status UI (loading / success / error) dikelola oleh satu sumber kebenaran, bukan oleh beberapa permintaan yang bersaing.
Memoization (memoisasi) — adalah caching hasil fungsi selama eksekusinya. Jika fungsi sudah berjalan dengan argumen yang sama, panggilan baru tidak memulai proses kedua, melainkan menerima hasil dari yang pertama. Ini adalah bentuk deduplikasi paling sederhana untuk skenario dalam proses.
Implementasi tipikal dalam aplikasi seluler — HashMap kunci di Deferred atau Promise. Kunci biasanya berupa string URL permintaan atau gabungan parameter. Masa pakai catatan — dari permintaan pertama hingga selesainya respons. Menurut Dropbox Engineering (2024), memoisasi di klien seluler Dropbox mengurangi jumlah permintaan duplikat ke API sebesar 40%.
Flawed deduplication — kesalahan berbahaya: jika Anda tidak menghapus kunci setelah kesalahan, semua permintaan selanjutnya akan selalu mengembalikan kesalahan yang sama. Implementasi yang benar harus menangani Error dan Failure, membersihkan cache, dan memungkinkan percobaan ulang.
class MemoizedLoaderT(
private val loader: suspend () -> T
) {
private var cachedResult: Result<T>? = null
suspend fun get(): T = cachedResult ?.getOrThrow() ?: run {
loader().let {
Result.success(it)
}.also { cachedResult = it }
}.await()
}
MemoizedLoader menggunakan Result<T> untuk penanganan kesalahan yang benar: saat berhasil — menyimpan cache, saat gagal — memungkinkan percobaan ulang. Pendekatan ini menjamin bahwa kegagalan jaringan sementara tidak akan memblokir permintaan selanjutnya.
Request Merging (penggabungan permintaan) — teknik di mana beberapa permintaan berbeda ke sumber yang sama dikumpulkan dalam satu grup dan dikirim sebagai satu permintaan batch. Berbeda dengan deduplikasi, permintaan tidak identik — berbeda dalam parameter, tetapi merujuk ke sumber daya yang sama.
Skenario tipikal: 5 layar aplikasi meminta profil pengguna yang berbeda. Alih-alih 5 permintaan individu ke /api/users/1, /api/users/2 dll., sistem menunggu 20 ms, mengumpulkan semua ID, dan mengirim satu permintaan /api/users?ids=1,2,3,4,5. Batas waktu jendela — parameter kunci: jendela terlalu lama memperburuk UX, jendela terlalu pendek tidak memungkinkan pengumpulan permintaan yang cukup.
Menurut Netflix Engineering (2023), dalam agregator GraphQL BFF (Backend for Frontend), penggabungan permintaan mengurangi jumlah panggilan HTTP antar lapisan sebesar 65% dan waktu respons rata-rata sebesar 120 ms dengan menghilangkan RTT yang berlebihan. Jendela asinkron (debounce) — implementasi standar melalui coroutine atau RxJava.
class BatchMergerT {
private val pending = ConcurrentLinkedQueue<Pair<String, CompletableDeferred<T>>>()
suspend fun get(id: String): T = suspendCoroutine { cont ->
pending.add(Pair(id, cont))
scheduleFlush()
}
}
Mixin ini menggunakan suspendCoroutine untuk menangguhkan setiap permintaan dan jendela 30 ms untuk mengumpulkan grup. Setelah timer berakhir, semua ID yang terkumpul dikirim dengan satu permintaan batch, dan setiap coroutine menerima hasilnya sendiri.
DataLoader — adalah pustaka (awalnya untuk JavaScript/GraphQL) yang mengimplementasikan batching dan memoisasi di sisi server. Ini mengelompokkan semua permintaan ke sumber data yang sama dalam satu tick event loop dan mengeksekusinya dengan satu panggilan. DataLoader banyak digunakan dengan GraphQL, tetapi dapat diterapkan di aplikasi REST mana pun.
Prinsip kerja: semua panggilan loader.load(id) dalam satu microtask dikumpulkan ke dalam array ID dan diteruskan ke fungsi batch. Setelah menerima hasil, setiap ID menerima elemen arraynya sendiri. Caching di DataLoader hanya berfungsi dalam satu permintaan HTTP — pada permintaan berikutnya, cache direset, menjamin keaktualan data.
Menurut Meta Engineering (2024), penerapan DataLoader di lapisan GraphQL Facebook menghilangkan masalah N+1, mengurangi jumlah kueri basis data dari 200 menjadi 10 per halaman tipikal. Batch scheduling — inovasi kunci DataLoader — menggunakan process.nextTick (Node.js) atau DispatchQueue.main (iOS) untuk mengoptimalkan pengelompokan.
Memoization — optimal untuk satu proses (aplikasi seluler, mikrolayanan). Sederhana diimplementasikan dan efektif untuk panggilan paralel identik. Kekurangan — tidak bekerja antar proses atau perangkat.
Request Merging — cocok untuk lapisan BFF atau layanan agregator. Memerlukan dukungan endpoint batch di server. Pilihan terbaik ketika frontend membuat banyak permintaan kecil ke data berbeda dari jenis yang sama.
DataLoader — standar deduplikasi untuk server GraphQL. Secara otomatis menyelesaikan masalah N+1 dan tidak memerlukan konfigurasi cache manual. Direkomendasikan untuk server mana pun dengan lapisan GraphQL.
Cache HTTP dengan deduplikasi — di tingkat OkHttp (Android) atau URLSession (iOS) dapat dikonfigurasi deduplikasi melalui Interceptor atau delegate. OkHttp CacheInterceptor — penyadap kustom yang memeriksa apakah permintaan dengan URL yang sama sudah berjalan dan menggabungkannya. Metode ini bekerja di tingkat di bawah logika bisnis dan mencakup semua permintaan aplikasi tanpa mengubah kode fitur.
Pertanyaan yang Sering Diajukan
Deduplikasi mencegah eksekusi permintaan duplikat saat yang pertama masih berjalan. Caching menyimpan hasil setelah eksekusi. Mereka saling melengkapi: deduplikasi melindungi dari permintaan berulang selama pemuatan, cache — dari permintaan berulang setelahnya.
Jika kunci deduplikasi dipilih secara tidak benar. Misalnya, jika semua pengguna menggunakan satu kunci, permintaan pertama akan memblokir semua yang lain. Kunci harus spesifik: mencakup URL, parameter, ID pengguna. Juga, deduplikasi dapat menyembunyikan masalah server dengan menyembunyikan frekuensi permintaan sebenarnya dalam metrik.
Jendela optimal — 20–50 ms untuk skenario pengguna. Ini cukup untuk mengumpulkan grup permintaan, tetapi tidak cukup untuk membuat pengguna menyadari keterlambatan. Untuk operasi latar belakang (log, analitik), jendela dapat ditingkatkan hingga 200–500 ms. Aturan empiris: jendela tidak boleh melebihi 10% dari waktu eksekusi satu permintaan.
Ya, prinsipnya sama: jika beberapa bagian aplikasi berlangganan saluran WebSocket yang sama, deduplikator membuka satu koneksi dan mengirimkan pesan ke semua pelanggan. RxJava Share atau Kotlin SharedFlow — alat ideal untuk deduplikasi pesan WebSocket di sisi klien.
Gunakan MockWebServer (OkHttp) untuk Android atau OHHTTPStubs untuk iOS. Jalankan 10 permintaan paralel dengan parameter yang sama dan periksa bahwa server menerima tepat satu panggilan. CountDownLatch atau coroutineScope akan membantu menyinkronkan panggilan paralel dalam pengujian.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga