Önbellek Çalışıyordu, Ancak Yine de Yinelenen API Çağrılarına Neden Oluyordu
Önbellek bozuk değildi.
Yine de, aynı kullanıcı adı için yapılan üç eşzamanlı istek GitHub'a üç kez ulaştı.
Bu durum, GitHub verilerini SVG rozetlerine dönüştüren bir Next.js API'si olan CommitPulse'da gerçekleşti. Bir README dosyası viral olduğunda, binlerce kişi rozeti aynı anda görüntüler. Bu da devasa bir trafik oluşturur.
Önbellek sıralı istekler için çalışıyordu. Eşzamanlı isteklerde ise başarısız oldu.
Sorun şuydu:
- İstek A önbelleği kontrol eder. Önbellekte veri bulunamadı (miss). İstek A, GitHub'dan veri çekmeye başlar.
- İstek B 5ms sonra gelir. Önbelleği kontrol eder. İstek A henüz tamamlanmadığı için önbellekte hala veri yoktur (miss). İstek B ikinci bir veri çekme işlemi başlatır.
- İstek C 10ms sonra gelir. O da bir önbellek kaçırması (cache miss) görür ve üçüncü bir veri çekme işlemi başlatır.
Bu, thundering herd problemidir. Yüksek yük altında bir önbellek kaçırması, üst sağlayıcınıza (upstream provider) yapılan bir dizi özdeş çağrıyı tetikler. GitHub gibi hız sınırlamalı (rate-limited) bir API kullanıyorsanız, bu durum limitlerinizi anında tüketebilir.
Çözüm, istek birleştirme (request coalescing) işlemidir.
Bekleyen istekleri, tamamlanmış önbellek girişlerinden ayrı olarak takip etmelisiniz. Bunu yönetmek için devam eden (in-flight) istekleri tutan bir Map uyguladım:
- Bir istek başladığında, Promise'ini bir Map içinde saklayın.
- Aynı anahtar (key) için ikinci bir istek gelirse, yeni bir veri çekme işlemi başlatmayın.
- Bunun yerine, Map'teki mevcut Promise'i döndürün.
- İstek tamamlandığında, onu Map'ten kaldırın ve sonucu önbelleğe kaydedin.
Bu, aynı anda kaç kişi aynı veriyi isterse istesin, API'ye yalnızca tek bir çağrı yapılmasını sağlar.
Bunu düzeltirken, aynı dosyadaki diğer üç uç durum (edge-case) hatasını da giderdim:
- Eksik Token Hataları: Sistem, kimlik bilgisi olarak "undefined" gönderiyordu. İstek yapmadan önce token'ları doğrulaması için sistemi güncelledim.
- Bellek Sızıntıları: Yeniden deneme mantığı, AbortSignals üzerinde güncelliğini yitirmiş (stale) olay dinleyicileri bırakıyordu. Sızıntıları önlemek için temizleme mantığı ekledim.
- URL Injection: Özel karakter içeren kullanıcı adları API yollarını bozuyordu. URL yapısını korumak için kodlama (encoding) ekledim.
Sadece bir önbellek yeterli değildir. Halihazırda devam eden (in-flight) istekleri de tekilleştirmeniz (deduplicate) gerekir.
Kaynak: https://dev.to/eshaanagrawal/the-cache-was-working-and-still-causing-duplicate-api-calls-3n51
