Açık konuşayım: Azure Service Bus ile uğraşmayı seviyorum, ama batch processing tarafında yıllardır içimi kemiren bir konu vardı (ciddiyim). Şimdi o dert bayağı hafifliyor. Microsoft, Azure Functions için per-message settlement özelliğini duyurdu ve bence bu, son iki yılda Service Bus tarafında gelen en işe yarar geliştirmelerden biri.
Ne yalan söyleyeyim, Neden bu kadar önemli? Çünkü sektördeki en sinsi problemlerden birini azaltıyor. Hani sorunsuz gidiyor sanırsın, sonra production’da gece 3’te seni dürten o klasik “duplicate processing” olayı var ya, işte tam ona dokunuyor. Kısacası mesele küçük değil.
Eski Modelin Sorunu: Hep Ya Da Hiç
Bir sahne kurayım. Azure Function’ınız Service Bus’tan 50 mesajlık bir batch alıyor, 49’u gayet düzgün işleniyor, sonuncusunda — diyelim JSON parse patladı — her şey dağılıyor. Eski düzende ne ölüyordu? 50 mesajın tamamı queue’ya geri dönüyordu. Evet, başarılı olan 49 da dahil. Sonra yine işleniyordu. Tekrar. Bir daha.
Bence, 2022 yılında bir e-ticaret müşterimizde bunu birebir yaşadık. Sipariş processing fonksiyonumuz vardı, batch size 100’dü; Black Friday gecesi tek bir bozuk payload geldi (sonradan anladık ki müşterinin frontend’inde bug varmış) ve o tek mesaj yüzünden binlerce sipariş defalarca işlendi. Sonuç mu? Bazı müşterilere aynı ürün 3 kez gitti, stok sistemi de allak bullak oldu. O geceyi unutmak zor.
Bu örüntünün adı klasik literatürde “all-or-nothing batch failure” (kendi tecrübem). Sonuçlar da malum:
- Duplicate processing: Zaten halledilmiş işler tekrar tekrar yapılıyor
- Boşa giden compute: Aynı iş için iki, üç, bazen on kez ödeme yapıyorsunuz (bu kritik)
- Sonsuz retry döngüleri: Bir tane “poison message” tüm batch’i rehin alıyor
- Idempotency yükü: Downstream servislerinizin tamamı duplicate’lere karşı bağışıklı olmak zorunda — ki bu da kod tabanına ciddi karmaşıklık ekliyor
“Idempotency bedavaya gelmez. Her downstream API’ye unique key gönderip kontrol etmek, sadece koda değil, veritabanı index’lerine ve okuma maliyetine de yük bindirir. Buradaki tasarruf, ilk bakışta görünenden çok daha büyük.”
Çözüm: Mesaj Başına Settlement
Yeni model şunu diyor: her mesajı ayrı ayrı çözebilirsin. Function tetiklendiğinde batch geliyor ama sonucu tek parça hâlinde işaretlemek zorunda değilsin. Her mesaj için ayrı karar verebiliyorsun; basit gibi duruyor ama etkisi bayağı büyük.
Kullanabileceğin dört aksiyon var:
| Aksiyon | Ne Yapar? | Ne Zaman Kullanılır? |
|---|---|---|
| Complete | Mesajı queue’dan kaldırır | Başarıyla işlendi, gönder gitsin |
| Abandon | Lock’u bırakır, mesaj tekrar denenmek üzere queue’ya döner | Geçici hata (timeout, throttling) |
| Dead-letter | Mesajı DLQ’ya taşır | Asla başarılı olmayacak (bozuk format) |
| Defer | Mesaj queue’da kalır ama sadece sequence number ile alınabilir | Manuel müdahale gerekiyor |
Aynı örneğe dönelim. Artık şunu yapabiliyorsun: 47’sını complete et, 2’sını abandon et (mesela database bağlantısı bir an koptu), 1’ını dead-letter’a at (payload bozuk, ne kadar denesen de düzelmeyecek). Tek invocation içinde üç farklı sonuç çıkıyor. Açıkçası olması gereken buydu.
Peki Kod Tarafında Ne Oluyor?
[Function("ProcessOrders")]
public async Task Run(
[ServiceBusTrigger("orders", IsBatched = true)]
ServiceBusReceivedMessage[] messages,
ServiceBusMessageActions messageActions)
{
foreach (var message in messages)
{
try
{
var order = JsonSerializer.Deserialize<Order>(message.Body);
await ProcessOrder(order);
await messageActions.CompleteMessageAsync(message);
}
catch (JsonException)
{
// Bozuk payload — DLQ'ya gitsin
await messageActions.DeadLetterMessageAsync(
message, "InvalidPayload", "JSON deserialization failed");
}
catch (TransientException ex)
{
// Geçici hata — retry sayısını artırıp geri at
var props = new Dictionary<string, object>
{
["RetryCount"] = (int)(message.ApplicationProperties
.GetValueOrDefault("RetryCount", 0)) + 1
};
await messageActions.AbandonMessageAsync(message, props);
}
}
}
İnanın, Dikkat edin: ServiceBusMessageActions binding’i runtime tarafından enjekte ediliyor. Bu yapı eskiden tek mesaj modunda vardı ama batch modunda yoktu; şimdi geldi. İşin özü bu kadar net.
Neden Bu Kadar Önemli?
Evet, Duplicate Processing Azalıyor
Bir mesajı complete ettiğin anda queue’dan siliniyor. Aynı batch içindeki başka bir mesaj patlasa bile tamamlananlar geri gelmiyor. Idempotency için yazdığınız o meşhur “did I already process this?” mantığına hâlâ ihtiyaç olabilir tabii,. Yük ciddi biçimde azalıyor. Tamamen çöpe atın demiyorum; defense in depth yine iyi fikir.
Ve işler burada ilginçleşiyor.
Evet Ama Hata Türleri Ayrışıyor
Bütün hatalar aynı değil ki zaten. Bunu kabul etmek lazım. Bozuk JSON ile geçici network hatası aynı tepkiyi hak etmiyor; eski modelde ikisi de çoğu zaman “abandon batch” seviyesine düşüyordu ve orada olay kilitleniyordu biraz. Daha fazla bilgi için Kubernetes v1.36: Askıdaki Job’lara Kaynak Ayarı Geldi yazımıza bakabilirsiniz.
- Boruk olmayan bir payload yerine bozuk mesaj → anında dead-letter
- Geçici hata → abandon + retry counter güncelle
- Zor durumda manuel müdahale lazım → defer et
- Sorunsuz akış → complete
Baya İş Gören Bir Backoff Triki Var mı?
Dürüst olmak gerekirse, Evet var; hatta güzel tarafı burada saklı biraz. Abandon‘a application properties geçebiliyorsunuz. Yani mesaja “bu senin üçüncü denemen” gibi not bırakıp sonraki turda farklı davranmasını sağlayabiliyorsunuz (mesela bekleme süresini artırıyorsunuz). Redis’e ya da ayrı bir state store’a hemen koşmanız gerekmiyor.
Peki Türkiye’deki Şirketler İçin Ne Anlama Geliyor?
Şöyle söyleyeyim, Bunu yerel tarafta açayım biraz. Türkiye’deki kurumsal müşterilerimde gördüğüm tablo şu: Service Bus kullanımı yaygın ama batch processing tarafında ekipler ya tek tek mesaj alıp yavaş kalıyor ya da batch alıp duplicate derdiyle uğraşıyorlar. Arada kalan yol eksikti; şimdi biraz olsun doluyor. Kubernetes CVE Kayıtları Düzeltiliyor: Sahadan Notlar yazımızda bu konuya da değinmiştik.
Maliyet kısmına bakalım hani kısa kısa geçmeyelim diye söylüyorum bunu da. Service Bus Standard tier’da operation başına ücretlendirme var; eski modelde bir mesaj beş kez tekrar işlendiğinde sadece compute değil messaging operation da beşe katlanıyordu. Orta ölçekli bir e-ticaret yükünde aylık birkaç bin TL fark çıkabiliyor. Logosoft’taki bir bankacılık projesinde geçen sene benzer bir optimizasyonu farklı yöntemle yapmıştık ve aylık Service Bus faturası %22 düşmüştü; bu yeni özellikle benzer sonuçları yakalamak daha kolay olabilir gibi duruyor.
Küçük ekip mi büyük organizasyon mu? Fark ediyor tabi. Küçük bir ekipseniz. Trafik düşükse mevcut idempotency yapınızı kırmadan yavaş yavaş ilerleyin; hatta bence önce dead-letter mantığını oturtun çünkü en hızlı kazanç orada geliyor gibi duruyor. Daha fazla bilgi için AKS Fleet Manager Cross-Cluster Networking: Saha Notları yazımıza bakabilirsiniz.
IsBatched = true modunda çalışıyor.Tek mesaj modundaki kontrol zaten vardı.
Yani feature aslında batch modu nihayet tek mesaj moduyla eşit kabiliyetlere yaklaştırıyor.
Geçiş Stratejisi: Adım Adım
Mevcut bir fonksiyonu nasıl migrate edersiniz? Benim önerdiğim sıra şu: Daha fazla bilgi için Agent Sandbox: Kubernetes’te AI Agent’ları Çalıştırmak yazımıza bakabilirsiniz.
- Önce gözlem: Mevcut fonksiyonun hata dağılımını çıkarın.
Hatalar genelde transient mi, persistent mi?
Application Insights ‘tan bir hafta veri toplayın. - SDK güncelle:
Microsoft.Azure.Functions.Worker.Extensions.ServiceBuspaketini son sürüme çekin.
Şu an sağlıklı çalışan sürümlerde destek var. - Auto-complete ‘i kapat:
host.jsoniçindeautoCompleteMessages : falseayarını yapın.
Bu kritik, yoksa hem siz complete edersiniz hem runtime karışır. - Aşamalı geçiş: Önce sadece dead-letter mantığını ekleyin.
Sonra abandon + retry.
En son tüm settlement matrix’i. - Test: Bozuk mesajları manuel olarak queue ‘ya yollayıp davranışı doğrulayın.
Production ‘a almadan önce bunu MUTLAKA yapın.
Bazı şeyler sessizce bozuluyor ya, işte burası onlardan biri. Auto-complete ‘i kapatıp settlement çağrısını da unutursanız, mesajın lock’u expire olana kadar bekler, sonra tekrar queue ‘ya döner. Yani açık açık hata vermeden sızı uğraştırır. İlk hafta logları sıkı takıp edin; boşuna demiyorum.
Bu konuyla ilgili Visual Studio Nisan Güncellemesi: Cloud Agent Devri Başladı yazımıza da göz atmanızı tavsiye ederim.
Eksiklikler ve Dikkat Edilmesi Gerekenler
Evet, övdük ama biraz fren de lazım; yoksa yazı fazla cilalı ölür. Per-message settlement her şeyi çözmüyor. Mesela:
Küçük bir detay: Lock duration konusu hâlâ kafa karıştırabiliyor (evet, doğru duydunuz). Batch’in tamamını işlerken son mesaja gelene kadar bazı mesajların lock süresi dolabilir. Bunun için ya batch size ‘ı küçük tutmalısınız (50 yerine 20 gibi) ya da queue tarafında lock duration ‘ı uzatmalısınız. Default süre kısa kalabiliyor; özellikle uzun işlem yapan akışlarda hemen hissediliyor.
Bir de feature görece yeni olduğu için production öncesi kendi load test’ınızı mutlaka yapın.
Microsoft Docs’ta yazanla pratik bazen birebir örtüşmüyor; kötü niyet değil, edge case bolca çıkıyor.
Geçen ay bir müşteride CompleteMessageAsync çağrısı sessizce timeout’a düştü, sebebi service bus namespace’in yanlış region’da olmasıymış.
Hata mesajı pek anlamlı değildi, açık söyleyeyim.
Tahminim ileride toparlanır,. Şimdilik temkin iyi fikir. Tahmin eder mısınız?
Kısa bir not düşeyim buraya.
This konuyu daha derin anlamak için not düşeyim:
Azure IaaS Performans: Sistem Düzeyli Yaklaşımın Gücü
yazımdaki sistemik bakış açısı yaklaşımı size yardımcı olabilir.
Bir de event-driven mimarı kurarken
Cosmos DB Güvenliği: Yeni Projede İlk Gün Kararları
yazısındaki ilk gün kararları çerçevesi işinizi görebilir.
Pratik Senaryolar
Sipariş İşleme
E-ticaret düşünün.
Her mesaj bir sipariş.
Stok kontrolü başarısızsa abandon,
stok az sonra dolacaksa tekrar dene;
payload bozuksa dead-letter,
başarılıysa complete.
Manuel onay isteyen büyük siparişlerde defer kullanırsınız.
Dört aksiyonun hepsi aynı use case içinde rahatça yer buluyor.
Sıkça Sorulan Sorular
Per-message settlement hangi dillerde çalışıyor?
.NET, JavaScript, TypeScript ve Python için Azure Functions worker extension’ında destekleniyor. Java tarafı roadmap’te var ama henüz GA olmadı. Yani Java kullanıyorsanız, açıkçası şimdilik Service Bus SDK’sını doğrudan kullanmak çok daha mantıklı.
Şu özellik mevcut fonksiyonlarımı bozar mı?
Hayır, opt-in bir şey bu. Eski davranış default olarak korunuyor — hani autoCompleteMessages ayarına dokunmadığınız sürece eski kodunuz aynen çalışmaya devam eder. Yeni mantığa geçmek istiyorsanız da kademeli yapabilirsiniz, telaş yok (kendi tecrübem)
Dead-letter queue’daki mesajları nasıl takıp ederim?
Azure Monitör üzerinden DeadletteredMessages metriğini izleyebilirsiniz. Bence mutlaka threshold’lu bir alert kurun — çünkü DLQ büyümeye başladıysa genelde altta bir bug yatıyordur. Manuel inceleme için de Service Bus Explorer aracı oldukça işe yarıyor.
Batch işlerken lock duration ne kadar olmalı?
Default 30 saniye çoğu durumda yetersiz kalıyor. Şöyle hesaplayın: mesaj — kendi adıma konuşayım — başına ortalama işlem süresi × batch size, üstüne bir de %50 emniyet payı ekleyin. Mesela 50 mesajlık bir batch’te her mesaj için 2 saniye harcıyorsanız, lock duration’ı en az 2-3 dakikaya çekmenizi öneririm.
Bu özellik Service Bus’ın hangi tier’larında çalışıyor?
Standard ve Premium tier’larda destekleniyor. Basic tier’da zaten queue özellikleri kısıtlı, settlement aksiyonlarının hepsi mevcut değil. Tecrübeme göre production yükü için Premium’u tercih edin — hem performans hem networking açısından farkı gerçekten hissediyorsunuz.
Kaynaklar ve İleri Okuma
İnanın, Azure SDK Blog: Per-Message Settlement for Azure Service Bus (orijinal duyuru)
Microsoft Learn: Azure Service Bus Trigger for Azure Functions
Service Bus Message Transfers, Locks, and Settlement (resmî dokümantasyon)
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



