Cloud Functions'te Retry ve DLQ
Serverless fonksiyonlarda hata yönetimi, "fonksiyon başarısız olduğunda ne olacak?" sorusuyla başlar. Google Cloud Functions'ta bu sorunun cevabı, fonksiyonun tetikleme türüne (HTTP mi, event-driven mi) ve nesline (Gen 1 mi, Gen 2 mi) göre değişir. Özellikle Gen 2'de retry ve Dead Letter Queue (DLQ) yönetimi, Pub/Sub abonelik modeline dayandığı için Gen 1'den önemli ölçüde farklıdır.
1. HTTP Fonksiyonları: Otomatik Retry Yok
HTTP trigger'lı fonksiyonlarda otomatik retry mekanizması yoktur. Fonksiyon bir hata döndürürse (5xx), hata doğrudan çağırana iletilir. Yeniden deneme sorumluluğu çağıran taraftadır . Bu nedenle, HTTP fonksiyonlarında retry stratejisi istemci tarafında (retry policy, exponential backoff) veya fonksiyonun önüne konulan bir API Gateway katmanında yönetilmelidir.
2. Event-Driven Fonksiyonlar (Gen 2): Retry Davranışı
Gen 2 event-driven fonksiyonlarda (Pub/Sub, Eventarc, Cloud Storage, Firestore tetikleyicileri) retry davranışı, altta yatan Pub/Sub aboneliği veya Eventarc trigger'ı tarafından yönetilir .
Kritik Ayrım: Varsayılan Retry Durumu
Gen 2 fonksiyonlarda retry'ın varsayılan durumu, kaynağa göre değişir ve bu konuda kafa karışıklığı yaygındır:
-
Pub/Sub trigger (gcloud ile deploy edilen):
gcloud functions deploy --gen2 --trigger-topic=...ile deploy edilen fonksiyonlarda retry varsayılan olarak kapalıdır. Etkinleştirmek için--retrybayrağı eklenmelidir . -
Eventarc trigger (Cloud Storage, Firestore vb.): Retry, trigger'ın retry politikası tarafından kontrol edilir ve yine
--retryile etkinleştirilir .
--retry Etkinleştirildiğinde Ne Olur?
--retry bayrağı eklendiğinde, Cloud Run functions başarısız event-driven çağrıları 24 saate kadar, 10 ila 600 saniye arasında üstel geri çekilme (exponential backoff) ile yeniden dener .
Fonksiyonun Sorumluluğu: Fonksiyon başarıyla dönerse (ACK), mesaj işlenmiş sayılır ve bir daha gönderilmez. Fonksiyon hata fırlatırsa veya reddedilmiş bir promise dönerse, sistem bunu "başarısız oldu, lütfen tekrar dene" sinyali olarak alır .
3. DLQ (Dead Letter Queue) Yapılandırması: Gen 2'de Yönetilen Abonelik Sorunu
Gen 2 fonksiyonları --trigger-topic ile deploy ettiğinizde, Google sizin adınıza bir Pub/Sub aboneliği oluşturur ve yönetir. Bu yönetilen abonelik üzerinde DLQ politikası doğrudan yapılandırılamaz .
Çözüm: Kendi Push Aboneliğinizi Yönetin
DLQ kullanmak istiyorsanız, yönetilen trigger yerine kendi Pub/Sub push aboneliğinizi oluşturmanız ve fonksiyonunuzu HTTP trigger olarak deploy etmeniz gerekir .
Adım Adım DLQ Kurulumu:
Adım 1: DLQ Topic ve Aboneliği Oluşturun
bash
gcloud pubsub topics create my-topic-dead-letter gcloud pubsub subscriptions create my-topic-dead-letter-sub \ --topic=my-topic-dead-letter \ --ack-deadline=120
Adım 2: Pub/Sub Servis Hesabına İzin Verin
Pub/Sub servis hesabının DLQ topic'ine publish etme ve orijinal abonelikten mesaj onaylama yetkisi olmalıdır :
bash
PROJECT_NUMBER=$(gcloud projects describe my-project --format="value(projectNumber)")
PUBSUB_SA="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"
gcloud pubsub topics add-iam-policy-binding my-topic-dead-letter \
--member="serviceAccount:${PUBSUB_SA}" \
--role="roles/pubsub.publisher"
gcloud pubsub subscriptions add-iam-policy-binding my-subscription-name \
--member="serviceAccount:${PUBSUB_SA}" \
--role="roles/pubsub.subscriber"
Adım 3: DLQ Politikasını Yapılandırın
--max-delivery-attempts parametresi, mesajın DLQ'ya taşınmadan önce toplam kaç kez teslim edileceğini belirtir. Örneğin 5, "ilk deneme + 5 ek deneme" değil, yaklaşık 5 toplam teslimat anlamına gelir :
bash
gcloud pubsub subscriptions update my-subscription-name \ --dead-letter-topic=my-topic-dead-letter \ --max-delivery-attempts=5
4. Retry-Aware Fonksiyon Yazma: Geçici vs. Kalıcı Hatalar
Retry mekanizmasını etkinleştirdiğinizde, fonksiyonunuzun geçici hatalar (ağ sorunu, rate limit) ile kalıcı hatalar (geçersiz veri, iş kuralı ihlali) arasında ayrım yapması kritiktir. Geçici hatalar retry'a değer; kalıcı hatalar ise sonsuza kadar denenmemelidir .
Desen: Kalıcı Hataları DLQ'ya Gönderin, Geçici Hatalarda Retry'a İzin Verin
csharp
// .NET örneği (kavramsal)
public async Task ProcessEvent(CloudEvent cloudEvent)
{
var deliveryAttempt = cloudEvent.Data.DeliveryAttempt ?? 1;
try
{
// İş mantığı...
}
catch (PermanentException ex)
{
// Kalıcı hata: DLQ'ya gönder, retry'ı durdur
await _deadLetterPublisher.PublishAsync(cloudEvent);
return; // Hata fırlatma → ACK → retry durur
}
catch (TransientException ex)
{
// Geçici hata: hata fırlat → retry tetiklenir
throw;
}
}
Idempotency (Yinelenebilirlik): At-least-once teslimat nedeniyle aynı mesaj birden fazla kez gelebilir. Fonksiyonunuz, aynı mesajı iki kez işlemenin zararsız olduğundan emin olmalıdır (message ID ile deduplication) .
5. Ne Zaman Hangi Yaklaşım?
| Senaryo | Öneri |
|---|---|
| HTTP fonksiyonu, basit hata yönetimi | Retry'ı istemci tarafında yönetin |
| Gen 2 event-driven, sadece retry yeterli | --retry bayrağı ile managed retry (24 saat, üstel backoff) |
| Gen 2 event-driven, DLQ gerekli | Kendi Pub/Sub push aboneliğinizi yönetin + --dead-letter-topic |
| Kalıcı hataların birikmesini istemiyorsunuz | Retry-aware fonksiyon yazın; kalıcı hataları DLQ'ya yönlendirin |
6. En İyi Pratikler
| Pratik | Açıklama |
|---|---|
| Hata türlerini ayırt edin | Geçici hatalarda throw, kalıcı hatalarda DLQ'ya gönderip return edin |
| Idempotency sağlayın | Message ID ile deduplication yapın |
| DLQ'yu izleyin | DLQ'daki mesajları manuel inceleyin; kalıcı sorunları giderin |
max-delivery-attempts değerini doğru anlayın |
Toplam teslimat sayısıdır; "retry sayısı" değil |
| Yönetilen trigger ile DLQ'yu karıştırmayın | --trigger-topic ile DLQ aynı anda kullanılamaz; kendi push aboneliğinizi kurun |
Sonuç
Cloud Functions'te retry ve DLQ, tetikleme türüne ve yönetim modeline göre farklılaşır. HTTP fonksiyonlarında otomatik retry yoktur; sorumluluk çağırandadır. Gen 2 event-driven fonksiyonlarda --retry ile 24 saate kadar üstel backofflu retry etkinleştirilebilir, ancak DLQ kullanmak için yönetilen trigger yerine kendi Pub/Sub push aboneliğinizi yönetmeniz gerekir. Retry-aware fonksiyon yazarak geçici ve kalıcı hataları ayırt etmek, hem gereksiz retry'ları önler hem de kalıcı hataların DLQ'da birikmesini sağlar. Unutmayın: Retry, geçici sorunlar içindir; kalıcı hatalar için DLQ ve manuel müdahale şarttır.