Ana Sayfa / Blog / Push notification: retry ve dedupe stratejisi

Push notification: retry ve dedupe stratejisi

Push notification gönderiyorum ama bazı kullanıcılar 2 kez alıyor, bazıları hiç. 12 uygulamada öğrendiğim retry ve dedupe pattern'leri.

Push notification backend geliştirme iOS geliştirmenin en underrated problemi. Client tarafı basit görünüyor (device token al, APNs’e gönder), ama production’da ölçekle birlikte tuhaf sorunlar çıkıyor. Aynı bildirimi iki kere alan kullanıcılar, hiç alamayan kullanıcılar, gecikmeli gelenler.

12 iOS uygulamamda push notification gönderiyorum ve her projede aynı problem’lerin farklı versiyonlarıyla karşılaştım. Bu yazıda gerçek production deneyiminden retry ve dedupe stratejileri.

APNs’in temel davranışları

Apple Push Notification Service bazı konularda garanti vermiyor:

1. At-most-once delivery. Apple “belki bir defa gönderecek, belki hiç” diyor. Delivery garantisi yok.

2. FIFO garantisi yok. Aynı cihaza 3 notification gönderirseniz, farklı sırada ulaşabilir.

3. Offline cihazlar için collapsing. Cihaz offline’sa APNs son notification’u tutuyor, öncekileri atıyor (varsayılan). collapse-id ile kontrol edebilirsiniz.

4. Token refresh. Kullanıcı app’i kaldırıp tekrar yüklediğinde token değişiyor. Eski token’a gönderilen notification kayboluyor.

Bu davranışları bilince backend tasarımı değişiyor.

Duplicate notification’ların sebepleri

Neden aynı notification iki kere geliyor?

1. Retry logic yanlış. Backend APNs’e gönderdi, network timeout oldu. Retry etti. Ama birinci istek aslında başarıyla gitmişti. Kullanıcı iki notification aldı.

2. Multiple token kaydı. Kullanıcı eski token bir, yeni token bir. Her ikisi de backend’de kayıtlı. Aynı mesaj iki token’a gidiyor.

3. Race condition. Aynı event için iki paralel worker tetiklenir. İkisi de notification gönderir.

4. Client duplicate detection yok. Bazı durumlarda APNs aynı payload’ı iki kez teslim ediyor (nadir ama oluyor). Client bunu deduplicate etmezse kullanıcı iki kez görüyor.

Çözüm 1: Unique notification ID

Her notification için unique ID üret. Backend’de bu ID’nin gönderildiğini işaretle:

{
  "aps": { ... },
  "notification_id": "550e8400-e29b-41d4-a716",
  "event_id": "order_placed_123"
}

Backend send logic:

  1. Unique notification_id üret (UUID v4)
  2. Redis veya DB’de “sent” olarak işaretle (60 dk TTL)
  3. APNs’e gönder
  4. Başarılıysa işaretli kalıyor
  5. Başarısızsa işareti sil, retry eligible

Bu şekilde aynı event için ikinci send tetiklenirse, önce check ediyorsun: “bu event_id için zaten gönderdim mi?” Cevap evet ise skip et.

Çözüm 2: Client-side deduplication

Client tarafında da guard koy. UserNotificationCenter’dan gelen notification’da check et:

func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
    let userInfo = notification.request.content.userInfo
    guard let id = userInfo["notification_id"] as? String else {
        completionHandler([.banner, .sound])
        return
    }
    if Self.seenNotifications.contains(id) {
        completionHandler([]) // Do not present
        return
    }
    Self.seenNotifications.insert(id)
    completionHandler([.banner, .sound])
}

seenNotifications bir Set, son 100-200 notification id’sini tutuyor (memory veya UserDefaults).

Çözüm 3: Retry strategy

Backend’de notification gönderme retry logic’i:

Exponential backoff: 1s, 2s, 4s, 8s, 16s delay’lerle max 5 deneme.

Different failure modes farklı handle:
400 BadDeviceToken: token invalid, DB’den sil, retry etme
410 Unregistered: cihaz app’i kaldırmış, token sil
503 ServiceUnavailable: APNs geçici sorun, retry et
429 TooManyRequests: rate limit, exponential backoff
timeout: retry et ama notification_id check’i önemli

Bu özellikle 410 response’u critical. Kullanıcı app’i kaldırdığında token invalid oluyor ama DB’de kalıyor. Her notification send’te hata ve retry. Database’de invalid token’ları aggresif temizleyin.

Çözüm 4: Collapse-id ile rationalize

Collapse-id sayesinde aynı “topic” üzerinde birden fazla notification birleştiriliyor. Örnek: bir chat uygulamasında:

Header: apns-collapse-id: thread-123

Aynı thread’te 5 mesaj gelirse, iPhone lock screen’de en son olan tek notification gösteriliyor. Eski 4 kenara itiliyor.

Bu kullanıcı deneyimi çok daha iyi. Spam hissi yok, bilgi güncel.

Bildirim gelmiyor sorunu

Başka tarafta, kullanıcı “bildirim gelmiyor” diyor. Sebepleri:

1. Token expired. 30 gün inactive kullanıcıya token invalidate olabiliyor. Kullanıcı app’i tekrar açtığında yeni token register ediyor.

2. Notification permission kaldırılmış. Kullanıcı Ayarlar’dan disable etti. Backend bunu bilmiyor, notification gönderiyor ama görünmüyor.

3. Focus mode. iOS 15+ Focus mode’lar notification’ları filter ediyor. Senin uygulamandan gelen notification kullanıcıya göre “hiç gelmedi” görünüyor.

4. Silent mode + no badge. Badge guncellensin ama sound gelmesin? Bazı kullanıcılar bunu “bildirim gelmiyor” sanıyor.

Troubleshooting: backend log’larında APNs response’larını tut. Kullanıcı şikayet ederse:
– Son 30 gün notification log: kaç gönderildi, response ne döndü?
– Token son ne zaman güncellendi?
– 410 hata var mı?

Test etmek

Sandbox APNs environment debug için perfect. Production token’larıyla karışmıyor. Xcode’da debug build için default sandbox.

Benim test checklist’im:

  1. App foreground: notification geliyor mu? (willPresent çağrılıyor mu?)
  2. App background: banner görünüyor mu?
  3. App killed: launch ediliyor mu?
  4. Duplicate test: aynı event 3 kere trigger ediyor mu, sadece 1 notification mi geliyor?
  5. Invalid token: 410 alınıyor mu, token siliniyor mu?
  6. Network timeout: retry yapılıyor mu, duplicate oluyor mu?

Monitoring

Production’da ölçmen gereken metrikler:

  • Send rate: Saatte kaç notification gönderiyorsun?
  • Success rate: APNs 200 response oranı
  • 410 rate: Invalid token oranı (artıyorsa token cleanup yetersiz)
  • Retry rate: Kaç retry yapılıyor, kaç tanesi succeed ediyor
  • Latency: Backend’de notification queue’lanmasından teslim’e kadar süre

Bu 5 metric dashboard’ta olmalı. Anomaly’ler hızlı görülsün.

Sonuç

Push notification subsystem görünüşten daha karmaşık. Unique ID ile dedupe, exponential backoff retry, aggressive invalid token cleanup, collapse-id ile rationalize. Bu 4 pattern spam’i önlüyor ve delivery rate’ini yükseltiyor.

Birinci versiyonda basit gönder-unut yaklaşımı yeter. 10K+ kullanıcıya gittiğinde bu pattern’leri devreye alın, yoksa şikayetler ciddi olur.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç