Request Deduplication (İstek Yinelenme Önleme), aynı paralel istekleri tek bir istekte birleştiren bir mekanizmadır, böylece veri kaynağı düzinelerce yerine yalnızca bir çağrı alır. Mobil uygulamalarda yinelenme önleme özellikle önemlidir: birden fazla ekran aynı anda aynı kullanıcı profilini veya ürün listesini talep edebilir. Square Engineering (2024)'e göre, yinelenme önlemenin uygulanması, sunucu mantığını değiştirmeden API yüklerini %30 oranında azaltmıştır.
Önemli Noktalar
Request Deduplication, aynı zaman penceresi içinde aynı veri kaynağına birden çok özdeş isteğin yürütülmesini önleyen bir tekniktir. 10 özdeş HTTP isteği göndermek yerine, sistem bir tane gönderir, diğer 9'u sonucunu bekler.
Yinelenen istek sorunu, özellikle durum tabanlı mimari (MVVM, MVI, Redux) kullanan mobil uygulamalarda akuttur. Birden çok gözlemci kısa bir süre içinde aynı verilere abone olduğunda, her biri kendi isteğini tetikleyerek gereksiz yük oluşturur. Uber Engineering (2024)'e göre, Uber mobil istemcilerindeki tüm isteklerin %18'e kadarı yinelenmektedir ve istemci tarafı yinelenme önleme, bu sayıyı 4 kat azaltmıştır.
Yinelenme önleme, önbelleğe alma ile aynı şey değildir. Önbellek, yürütmeden sonra istek sonucunu saklar. Yinelenme önleme, gereksiz istekleri yürütülmelerinden önce ve sırasında önler. İstek tamamlandıktan sonra önbelleğe alma devreye girer.
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()
}
Bu Kotlin sınıfı, anahtar başına yalnızca bir coroutine çalıştırılmasını garanti eder. Aynı anahtara sahip tüm eşzamanlı çağrılar, tek bir Deferred bekler. Tamamlandıktan sonra anahtar kaldırılır ve sonraki istek normal şekilde yürütülür.
Sunucu yükünün azaltılması ilk ve en belirgin nedendir. Her yinelenen istek, sunucu kaynaklarını tüketir: CPU, bellek, veritabanı bağlantıları. Milyonlarca cihaz ölçeğinde, %10–15 yinelenen istek bile önemli yük oluşturur ve ek sunucular gerektirir.
Pil ve veri kullanımının azaltılması — mobil cihazdaki her HTTP isteği radyo modülü enerjisi tüketir. Google I/O (2025)'e göre, tek bir başarısız veya yinelenen istek, bir ağ oturumunun enerjisinin %15'ine kadarını tüketebilir. Yinelenme önleme, radyo modülü etkinleştirme sayısını azaltarak cihazın pil ömrünü uzatır.
Veri çakışmalarını önleme — iki yinelenen istek yerel depolamaya veri yazarsa, yarış koşulları oluşabilir: ikinci istek, birincinin sonucunu eski verilerle üzerine yazabilir. Yinelenme önleme, yerel depolamaya yazmanın yalnızca bir kez gerçekleşmesini garanti ederek yarışları ortadan kaldırır.
İyileştirilmiş UX — kullanıcı aynı veriler için birden çok yükleme göstergesi görmez. UI durumu (yükleniyor / başarılı / hata), birden çok rekabet eden istek yerine tek bir doğruluk kaynağı tarafından yönetilir.
Memoization, bir işlevin sonucunun yürütülmesi sırasında önbelleğe alınmasıdır. Bir işlev zaten aynı argümanlarla çalışıyorsa, yeni bir çağrı ikinci bir süreç başlatmaz, birincinin sonucunu alır. Bu, süreç içi senaryolar için en basit yinelenme önleme biçimidir.
Mobil uygulamalarda tipik bir uygulama, Deferred veya Promise'e yönelik anahtarların HashMap'idir. Anahtar genellikle istek URL dizesi veya parametrelerin birleştirilmesidir. Girişin ömrü, ilk istekten yanıt tamamlanana kadardır. Dropbox Engineering (2024)'e göre, Dropbox mobil istemcisinde memoization, yinelenen API isteklerini %40 azaltmıştır.
Kusurlu yinelenme önleme — tehlikeli bir hata: bir hatadan sonra anahtar kaldırılmazsa, sonraki tüm istekler sonsuza kadar aynı hatayı döndürür. Doğru bir uygulama, Error ve Failure'ı işlemeli, önbelleği temizlemeli ve yeniden denemeye izin vermelidir.
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, doğru hata işleme için Result<T> kullanır: başarı durumunda — önbelleğe alır, hata durumunda — yeniden denemeye izin verir. Bu yaklaşım, geçici bir ağ arızasının sonraki istekleri engellememesini sağlar.
Request Merging, aynı kaynağa yönelik birden çok farklı isteğin bir grupta toplandığı ve tek bir toplu istek olarak gönderildiği bir tekniktir. Yinelenme önlemeden farklı olarak, buradaki istekler özdeş değildir — parametrelerde farklılık gösterir ancak aynı kaynağı adresler.
Tipik bir senaryo: Uygulamanın 5 ekranı farklı kullanıcıların profillerini ister. /api/users/1, /api/users/2 vb. adreslerine 5 ayrı istek göndermek yerine, sistem 20 ms bekler, tüm ID'leri toplar ve tek bir istek /api/users?ids=1,2,3,4,5 gönderir. Pencere zaman aşımı anahtar parametredir: çok uzun bir pencere UX'i bozar, çok kısa — yeterli istek toplayamaz.
Netflix Engineering (2023)'e göre, GraphQL toplayıcı BFF'de (Frontend için Backend), istek birleştirme, katmanlar arasındaki HTTP çağrı sayısını %65 ve fazladan RTT'leri ortadan kaldırarak ortalama yanıt süresini 120 ms azaltmıştır. Async pencere (debounce), coroutine veya RxJava aracılığıyla standart uygulamadır.
class BatchMergerT {
private val pending = ConcurrentLinkedQueue<Pair<String, CompletableDeferred<T>>>()
suspend fun get(id: String): T = suspendCoroutine { cont ->
pending.add(Pair(id, cont))
scheduleFlush()
}
}
Bu mixin, her isteği askıya almak için suspendCoroutine ve grubu toplamak için 30 ms pencere kullanır. Zamanlayıcı süresi dolduğunda, toplanan tüm ID'ler tek bir toplu istekte gönderilir ve her coroutine sonucunu alır.
DataLoader, sunucu tarafında toplu işleme ve memoization uygulayan bir kitaplıktır (başlangıçta JavaScript/GraphQL için). Tek bir olay döngüsü tiki içinde aynı veri kaynağına yönelik tüm istekleri gruplar ve bunları tek bir çağrıyla yürütür. DataLoader, GraphQL ile yaygın olarak kullanılır ancak herhangi bir REST uygulamasında uygulanabilir.
Nasıl çalışır: Tek bir mikro görev içindeki tüm loader.load(id) çağrıları, bir ID dizisinde toplanır ve toplu işleve iletilir. Sonuçlar alındıktan sonra her ID, dizi öğesini alır. DataLoader'daki önbelleğe alma yalnızca tek bir HTTP isteği içinde çalışır — sonraki istekte önbellek temizlenir ve veri tazeliği sağlanır.
Meta Engineering (2024)'e göre, Facebook'un GraphQL katmanında DataLoader'ın uygulanması N+1 sorununu ortadan kaldırarak, tipik bir sayfa başına veritabanı sorgularını 200'den 10'a düşürmüştür. Toplu zamanlama — DataLoader'ın temel yeniliği — gruplamayı optimize etmek için process.nextTick (Node.js) veya DispatchQueue.main (iOS) kullanır.
Memoization tek bir süreç (mobil uygulama, mikro hizmet) için idealdir. Uygulaması basit ve özdeş paralel çağrılar için etkilidir. Dezavantajı, süreçler veya cihazlar arasında çalışmamasıdır.
Request Merging, BFF katmanı veya toplayıcı hizmet için uygundur. Sunucuda toplu uç nokta desteği gerektirir. Frontend'in aynı türdeki farklı veriler için çok sayıda küçük istek yaptığı durumlarda en iyi seçimdir.
DataLoader, GraphQL sunucuları için standarttır. N+1 sorununu otomatik olarak çözer ve manuel önbellek yapılandırması gerektirmez. GraphQL katmanı olan herhangi bir sunucu için önerilir.
Yinelenme önlemeli HTTP önbelleği — OkHttp (Android) veya URLSession (iOS) düzeyinde, Interceptor veya delegate aracılığıyla yinelenme önleme yapılandırılabilir. OkHttp CacheInterceptor, aynı URL'ye sahip bir isteğin zaten yürütülüp yürütülmediğini kontrol eden ve bunları birleştiren özel bir interceptordür. Bu yöntem, iş mantığı düzeyinin altında çalışır ve özellik kodunu değiştirmeden tüm uygulama isteklerini kapsar.
Sıkça Sorulan Sorular
Yinelenme önleme, ilk istek hâlâ çalışırken yinelenen bir isteğin yürütülmesini engeller. Önbelleğe alma, yürütmeden sonra sonucu kaydeder. Birbirlerini tamamlarlar: yinelenme önleme, yükleme sırasında tekrarlanan isteklere karşı korur, önbellek ise daha sonraki tekrarlanan isteklere karşı korur.
Yinelenme önleme anahtarı yanlış seçilirse. Örneğin, tüm kullanıcılar aynı anahtarı kullanırsa, ilk istek diğerlerini engeller. Anahtar belirli olmalıdır: URL, parametreler ve kullanıcı kimliğini içermelidir. Yinelenme önleme ayrıca metriklerdeki gerçek istek sıklığını gizleyerek sunucu sorunlarını maskeleyebilir.
Kullanıcı senaryoları için optimum pencere 20–50 ms'dir. Bu, bir grup isteği toplamak için yeterlidir ancak kullanıcının bir gecikme fark etmesi için yeterli değildir. Arka plan işlemleri (günlükler, analitik) için pencere 200–500 ms'ye yükseltilebilir. Ampirik kural: pencere, tek bir isteğin yürütme süresinin %10'unu geçmemelidir.
Evet, aynı prensip geçerlidir: uygulamanın birden çok bölümü aynı WebSocket kanalına abone olursa, yinelenme önleyici tek bir bağlantı açar ve mesajları tüm abonelere dağıtır. İstemcide WebSocket mesajlarının yinelenmesini önlemek için RxJava Share veya Kotlin SharedFlow ideal araçlardır.
Android için MockWebServer (OkHttp) veya iOS için OHHTTPStubs kullanın. Aynı parametrelerle 10 paralel istek çalıştırın ve sunucunun tam olarak bir çağrı aldığını doğrulayın. CountDownLatch veya coroutineScope, testte paralel çağrıları senkronize etmeye yardımcı olur.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun