Diyarbakır Yazılım LogoDİYARBAKIR
>Ana Sayfa>Projeler>Atölye>Yazılar
>Ana Sayfa>Projeler>Atölye>Yazılar
durum: inşa ediliyor
Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri
  1. Anasayfa
  2. Yazılar
  3. Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri

Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri

Diyarbakır Yazılım
Diyarbakır Yazılım
16.08.2026
#Yazılım#Teknoloji#Topluluk

Bir Node.js uygulamasının düşük trafikte hızlı çalışması, yüksek yük altında da aynı davranışı göstereceği anlamına gelmez. Binlerce eş zamanlı istek, büyük dosyalar, yoğun veritabanı işlemleri ve uzun süren arka plan görevleri başladığında mimari kararların etkisi çok daha net görünür. Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri konusu tam olarak bu noktada event loop davranışından queue tasarımına kadar uzanan geniş bir karar alanını kapsar. Yıllar içinde üzerinde çalıştığım backend projelerinde performans sorunlarının önemli bölümünün tek bir yavaş fonksiyondan değil, concurrency sınırlarının, backpressure mekanizmalarının ve iş yükü sınıflandırmasının yanlış yapılmasından kaynaklandığını gördüm. Bu rehberde Node.js asenkron veri işleme nasıl yapılır, yüksek hacimli veriler nasıl yönetilir ve hangi durumda async/await, stream, Worker Threads ya da queue tercih edilmelidir sorularını üretim ortamına dönük örneklerle ele alacağım.

Node.js'te Asenkron Veri İşleme Nedir?

Node.js'te asenkron veri işleme, bir işlemin tamamlanmasını beklerken uygulamanın diğer işleri ilerletebilmesini sağlayan programlama ve sistem tasarımı yaklaşımıdır. Buradaki temel amaç her şeyi aynı anda çalıştırmak değil, bekleme sürelerini verimli değerlendirmektir. Ağ bağlantıları, veritabanı sorguları ve dosya erişimleri bu modelden güçlü biçimde yararlanır. Buna karşılık CPU üzerinde uzun süre çalışan bir hesaplama aynı yöntemle ele alındığında event loop bloke olabilir. Sağlıklı bir mimari bu iki iş türünü daha tasarım aşamasında birbirinden ayırır.

Asenkron Programlama Nedir?

Asenkron programlama, bir görevin sonucunu beklerken çağıran kodun tüm uygulamayı durdurmamasını sağlar. Node.js tarafında Promise, async/await, callback, event emitter ve stream gibi mekanizmalar bu yaklaşımın farklı biçimleridir. Örneğin bir HTTP isteği gönderildiğinde Node.js ağ yanıtını beklerken başka bağlantıları işleyebilir. Bu davranış özellikle yüksek sayıda I/O operasyonu bulunan servislerde ciddi kapasite avantajı sağlar. Ancak asenkron sözdizimini kullanmak tek başına kodun hızlı veya bloklamayan bir yapıya sahip olduğunu garanti etmez.

Non-Blocking I/O Nedir?

Non-blocking I/O, bir giriş veya çıkış işleminin tamamlanmasını beklerken ana JavaScript yürütmesinin durmaması anlamına gelir. Dosya okuma, socket iletişimi ve ağ istekleri bunun tipik örnekleridir. Node.js işlemi başlatır, uygun altyapıya devreder ve sonuç hazır olduğunda ilgili callback ya da Promise devamını çalıştırır. Böylece tek bir yavaş dış servis, teorik olarak diğer bağımsız bağlantıları bekletmek zorunda kalmaz. Ölçeklenebilir backend tasarımında bu davranışı korumak, yalnızca hızlı kod yazmaktan daha değerlidir.

Concurrency Nedir?

Concurrency, birden fazla işin aynı zaman aralığında ilerleyebilmesini ifade eder. İşlerin aynı CPU çekirdeğinde gerçekten aynı anda hesaplanması şart değildir. Bir istek ağ yanıtı beklerken başka bir isteğin veritabanı sonucu işlenebilir ve üçüncü bir istek parse edilebilir. Node.js özellikle I/O ağırlıklı senaryolarda bu modeli etkili biçimde kullanır. Bununla birlikte concurrency sınırsız bırakılırsa connection pool, socket, bellek veya harici API kapasitesi kolayca aşılabilir.

Parallelism Nedir?

Parallelism, iki veya daha fazla hesaplamanın gerçekten aynı anda farklı işlem kaynaklarında yürütülmesidir. Node.js uygulamalarında Worker Threads bu davranışı CPU ağırlıklı JavaScript görevleri için sağlayabilir. Örneğin dört çekirdekli bir sistemde birden fazla worker farklı hesaplamaları eş zamanlı ilerletebilir. Bu yöntem I/O işlemlerinin yerine geçen genel amaçlı bir çözüm değildir. Paralellik eklenmeden önce görevin gerçekten CPU-bound olup olmadığı ölçülmelidir.

Asenkronluk ile Paralellik Arasındaki Fark

Asenkronluk beklemeyi verimli yönetirken paralellik hesaplamayı birden fazla işlem kaynağına yayar. Bir veritabanı sorgusunu await etmek asenkron davranıştır fakat sorgunun JavaScript kodunda paralel hesaplandığı anlamına gelmez. Bir görüntü dönüştürme görevini Worker Thread üzerinde çalıştırmak ise gerçek CPU paralelliği sağlayabilir. Bu iki kavramın karıştırılması gereksiz worker kullanımı veya event loop bloklanması gibi sorunlara yol açar. Mimari seçim yaparken önce işlemin zamanının beklemede mi yoksa CPU hesaplamasında mı geçtiğine bakmak gerekir.

Node.js Neden Asenkron İş Yüklerinde Güçlüdür?

Node.js, event-driven çalışma modeli ve non-blocking I/O API'leri sayesinde çok sayıda beklemeli işi düşük thread maliyetiyle yönetebilir. Özellikle API gateway, gerçek zamanlı iletişim, backend-for-frontend ve ağ ağırlıklı servislerde bu model güçlü sonuçlar verir. JavaScript callback ve Promise mekanizmaları event loop ile birlikte çalışarak I/O tamamlanmalarını uygulamaya taşır. Aynı zamanda stream, Worker Threads ve queue çözümleri farklı iş yüklerinin ayrı stratejilerle ele alınmasına izin verir. Node.js yüksek hacimli veriler asenkron nasıl işlenir sorusunun cevabı da tek bir API değil, bu araçların doğru kombinasyonudur.

Node.js Event Loop Nasıl Çalışır?

Event loop, Node.js uygulamasının hangi callback ve devam işlemlerinin ne zaman çalışacağını yöneten temel çalışma mekanizmasıdır. JavaScript kodu ana yürütme akışında ilerlerken I/O işlemleri işletim sistemi ve libuv gibi altyapı bileşenleriyle koordine edilir. Hazır hale gelen işler uygun queue yapılarına alınır ve event loop fazlarında çalıştırılır. Bu mimari, tek bir JavaScript yürütme thread'i üzerinden çok sayıda eş zamanlı bağlantının yönetilebilmesine yardımcı olur. Performans analizi yaparken event loop'un yalnızca teorisini değil, hangi kodun onu ne kadar süre meşgul ettiğini de incelemek gerekir.

Call Stack

Call Stack, JavaScript fonksiyonlarının yürütülme sırasını tutan yapıdır. Bir fonksiyon çağrıldığında stack üzerine eklenir ve tamamlandığında çıkarılır. Uzun süren senkron bir fonksiyon stack üzerinde kaldığı sürece başka JavaScript callback'leri çalışamaz. Bu nedenle büyük döngüler veya ağır parse işlemleri event loop gecikmesini artırabilir. Asenkron tasarımın ilk performans kontrolü, call stack üzerinde uzun süre kalan işleri belirlemektir.

Event Loop

Event loop, hazır callback'leri ve belirli queue'lardaki görevleri uygun sırayla JavaScript yürütmesine taşır. Bu mekanizma sürekli çalışan rastgele bir döngüden daha fazlasıdır ve farklı aşamalara sahiptir. Timers, poll ve check gibi fazlar belirli callback türlerinin çalışmasına aracılık eder. Promise microtask'leri ise ilgili JavaScript çalışması tamamlandıktan sonra ayrı öncelik kurallarıyla işlenir. Event loop davranışını bilmek özellikle latency sorunlarını yorumlarken büyük avantaj sağlar.

libuv

libuv, Node.js'in event loop, bazı dosya sistemi işlemleri, thread pool ve platformlar arası I/O davranışında önemli rol oynayan altyapı kütüphanesidir. Node.js'in yalnızca JavaScript katmanından oluşmadığını anlamak için libuv'u bilmek yararlıdır. Bazı işlemler işletim sistemi mekanizmalarıyla, bazıları ise libuv thread pool üzerinden yürütülebilir. Bu ayrım performans darboğazlarının kaynağını tespit ederken önem kazanır. Örneğin JavaScript Worker Threads ile libuv thread pool aynı mekanizma değildir.

Callback Queue

Callback queue kavramı, tamamlanan asenkron işlemlerin JavaScript tarafında çalıştırılmak üzere beklediği kuyrukları açıklamak için sık kullanılır. Node.js içinde tüm callback'lerin tek bir basit queue üzerinde çalıştığını düşünmek doğru değildir. Farklı event loop fazları kendi davranışlarına ve görev kaynaklarına sahiptir. Bir callback hazır olsa bile JavaScript ana yürütmesi uzun bir senkron iş tarafından tutuluyorsa hemen çalışamayabilir. Bu nedenle callback gecikmesi yalnızca I/O süresinden kaynaklanmaz.

Microtask Queue

Microtask queue, Promise devamları ve queueMicrotask ile eklenen işler gibi yüksek öncelikli görevlerin işlenmesinde rol oynar. Mevcut JavaScript işlemi tamamlandığında microtask'ler genellikle bir sonraki event loop fazına geçilmeden önce çalıştırılır. Bu davranış küçük devam işlemleri için son derece kullanışlıdır. Fakat sürekli yeni microtask üreten zincirler I/O callback'lerinin çalışmasını geciktirebilir. Bu yüzden microtask mekanizmasını hızlı görevler için kullanmak ve uzun işi parçalara ayırmak daha sağlıklıdır.

I/O Polling

I/O polling, işletim sisteminden veya ilgili altyapıdan hangi I/O olaylarının tamamlandığını öğrenme sürecinin parçasıdır. Event loop'un poll fazı, uygun I/O callback'lerinin çalıştırılmasında merkezi rol oynar. Ağ bağlantılarının büyük bölümü bu event-driven modele uygun biçimde ele alınır. Uygulama kodu poll fazına yeterince sık kontrol bırakmıyorsa hazır I/O sonuçları gecikebilir. Bu nedenle uzun senkron hesaplamaların latency üzerindeki etkisi yalnızca CPU tüketimiyle sınırlı değildir.

Event Loop'un Tek Thread Olması Ne Demektir?

Node.js'te JavaScript kodunun ana yürütme akışı temel olarak tek thread üzerinde çalışır. Bu ifade Node.js prosesinde yalnızca tek işletim sistemi thread'i bulunduğu anlamına gelmez. libuv thread pool, Worker Threads ve bazı yerel kütüphaneler ek thread'lerden yararlanabilir. Asıl kritik nokta, ana JavaScript thread'ini uzun süre tutan bir görevin diğer callback'lerin çalışmasını geciktirmesidir. Üretim ortamında event loop delay ölçümü bu nedenle CPU yüzdesi kadar değerli olabilir.

Node.js Event Loop Aşamaları

Event loop aşamalarını bilmek, setTimeout, setImmediate, I/O callback'leri ve microtask'lerin neden bazen beklenenden farklı sırada çalıştığını anlamayı kolaylaştırır. Node.js çalışma döngüsü timers, pending callbacks, poll, check ve close callbacks gibi aşamalardan oluşur. Promise ve nextTick queue'ları ise bu fazların arasındaki yürütme davranışını etkileyebilir. Uygulama kodunun çoğunda fazları ezberlemek gerekmez fakat düşük seviye performans sorunlarında bu bilgi çok yararlıdır. Özellikle çok yoğun callback üretildiğinde sıranın latency üzerindeki etkisi gözle görülür hale gelebilir.

Timers

Timers aşaması, zamanı gelmiş setTimeout ve setInterval callback'lerinin çalıştırılmasıyla ilişkilidir. Verilen gecikme değeri callback'in tam olarak o milisaniyede çalışacağını garanti etmez. Bu değer callback'in en erken ne zaman çalışmaya uygun hale geleceğini ifade eder. Event loop başka bir uzun görevle meşgulse timer daha geç çalışabilir. Bu nedenle timer gecikmesini gerçek zamanlı işlem garantisi olarak kullanmamak gerekir.

Pending Callbacks

Pending callbacks aşaması bazı sistem düzeyindeki I/O callback'lerinin işlenmesine ayrılır. Uygulama geliştiricileri bu fazla genellikle doğrudan etkileşim kurmaz. Yine de event loop'un tek bir callback kuyruğundan oluşmadığını anlamak açısından önemlidir. Ağ ve sistem operasyonlarının bazı tamamlanma durumları burada ele alınabilir. Performans sorunlarını incelerken callback'in hangi aşamada beklediğinden çok event loop'un ne kadar süre bloke kaldığını ölçmek çoğu zaman daha değerlidir.

Poll

Poll aşaması event loop'un I/O olaylarını işlediği en önemli bölümlerden biridir. Hazır I/O callback'leri burada çalıştırılabilir ve uygun koşullarda yeni olaylar beklenebilir. Uzun süren JavaScript görevleri poll fazına dönüşü geciktirdiğinde ağ işlemleri tamamlanmış olsa bile callback yürütmesi bekleyebilir. Bu durum yüksek p95 veya p99 latency olarak uygulamaya yansıyabilir. CPU-bound görevleri ana thread'den ayırmanın önemli nedenlerinden biri budur.

Check

Check aşaması setImmediate ile planlanan callback'lerin çalıştırıldığı bölümdür. I/O akışının ardından kontrolü bir sonraki iş parçasına bırakmak gereken senaryolarda setImmediate yararlı olabilir. Büyük dizileri küçük parçalar halinde işleyip her parça sonrasında event loop'a fırsat vermek buna örnektir. Ancak setImmediate da CPU ağırlıklı işi ortadan kaldırmaz, yalnızca yürütmeyi bölmeye yardım eder. Gerçek paralellik gerekiyorsa Worker Threads değerlendirilmelidir.

Close Callbacks

Close callbacks aşaması socket veya benzeri kaynakların kapanış callback'lerinin işlenmesiyle ilgilidir. Kaynak yaşam döngüsünü doğru kapatmak özellikle uzun süre çalışan servislerde önemlidir. Yarım kalan bağlantılar ve düzgün temizlenmeyen kaynaklar zamanla kapasite sorunlarına dönüşebilir. Graceful shutdown sırasında bu kapanış davranışları ayrıca dikkate alınmalıdır. Uygulama yalnızca iş üretmeyi değil, kaynakları güvenli biçimde bırakmayı da tasarlamalıdır.

Promise Microtasks

Promise çözümlemelerinden sonra çalışan then, catch ve await devamları microtask mekanizması üzerinden yürütülür. Bunlar event loop'un normal faz callback'lerinden farklı öncelik davranışına sahiptir. Küçük ve kısa devam işlemlerinde bu yapı oldukça etkilidir. Buna karşılık binlerce ardışık Promise çözümlemesi event loop'un diğer olaylara dönmesini geciktirebilir. Promise kullanımında performans açısından önemli olan yalnızca Promise sayısı değil, her devam adımının yaptığı iştir.

process.nextTick() Queue

process.nextTick, mevcut operasyon tamamlandıktan sonra mümkün olan en erken noktada callback çalıştırmak için kullanılan Node.js mekanizmasıdır. nextTick queue, normal event loop fazlarına kıyasla çok yüksek önceliğe sahiptir. Bu nedenle tekrar tekrar nextTick planlamak I/O görevlerini uzun süre bekletebilir. API tasarımında bazen senkron ve asenkron davranışı tutarlı hale getirmek için yararlıdır. Genel uygulama kodunda ise kontrolsüz nextTick zincirlerinden uzak durmak daha güvenlidir.

process.nextTick, Promise ve setImmediate Arasındaki Fark

Bu üç mekanizma benzer biçimde daha sonra çalışacak bir görev planlıyor gibi görünse de yürütme sıraları ve kullanım amaçları farklıdır. process.nextTick çok erken çalışır, Promise devamları microtask sırasına girer ve setImmediate check fazında yürütülür. Yanlış mekanizmayı seçmek çoğu uygulamada hata yaratmasa bile yoğun yük altında starvation veya beklenmeyen zamanlama davranışı oluşturabilir. Ben pratikte iş mantığını zamanlama ayrıntılarına bağımlı hale getirmemeyi tercih ediyorum. Zamanlama mekanizması seçimi kodun açık amacıyla bağlantılı olmalıdır.

process.nextTick()

process.nextTick mevcut call stack tamamlandıktan sonra callback'i çok erken çalıştırır. Bu özellik bazı Node.js API tasarımlarında tutarlı asenkron davranış sağlamak için kullanılabilir. Fakat callback içinde tekrar nextTick çağrılması kolayca uzun bir zincir oluşturabilir. Böyle bir zincir poll fazına dönüşü geciktirerek I/O işlemlerinin görünür latency'sini artırır. Bu nedenle nextTick genel amaçlı bir iş kuyruğu olarak kullanılmamalıdır.

Promise Microtask

Promise microtask'leri resolved veya rejected Promise devamlarını yürütür. async/await kullanan modern Node.js kodunun önemli bölümü doğal olarak bu mekanizmaya dayanır. İşlemlerin küçük ve bağımsız devam adımları için oldukça uygun bir modeldir. Ancak yüksek sayıda CPU yapan microtask peş peşe çalıştırıldığında event loop üzerinde baskı oluşabilir. await ifadesinin işlem maliyetini ortadan kaldırmadığını burada tekrar hatırlamak gerekir.

queueMicrotask()

queueMicrotask, doğrudan microtask queue'ya iş eklemeyi sağlayan standart bir JavaScript API'sidir. Promise oluşturmadan kısa bir microtask planlamak gereken durumlarda kullanılabilir. Kullanım amacı çoğunlukla yürütme sırasını standart microtask semantiğine taşımaktır. Ağır veri işleme veya uzun döngüler için uygun bir çözüm değildir. Sürekli yeni microtask oluşturulması diğer event loop işlerinin gecikmesine neden olabilir.

setImmediate()

setImmediate callback'i event loop'un check fazında çalışacak biçimde planlar. Özellikle I/O sonrasında veya uzun bir işi parçalara bölerek event loop'a kontrol vermek istediğimizde yararlı olabilir. Büyük bir array üzerinde her birkaç bin kayıttan sonra setImmediate ile yield etmek uygulamanın yanıt verebilirliğini iyileştirebilir. Buna rağmen toplam CPU maliyeti değişmez. İşlem sürekli yüksek CPU tüketiyorsa Worker Thread kullanmak daha doğru bir mimari olabilir.

setTimeout(..., 0)

setTimeout ile sıfır gecikme vermek callback'in hemen çalışacağı anlamına gelmez. Callback timers mekanizmasına göre çalışmaya uygun hale gelir ve event loop uygun olduğunda yürütülür. Bu yöntem zaman zaman işi bir sonraki tur benzeri bir noktaya bırakmak için kullanılsa da kesin sıralama varsayımlarından kaçınmak gerekir. setImmediate ile davranışı özellikle I/O bağlamında farklı olabilir. Kodun doğruluğunu küçük zamanlama farklarına bağlı hale getirmemek daha sürdürülebilir bir yaklaşımdır.

Hangi Mekanizma Ne Zaman Kullanılmalı?

Promise ve async/await genel uygulama akışında asenkron sonuçları yönetmek için birincil seçenek olmalıdır. queueMicrotask kısa ve açık biçimde microtask planlamak gereken düşük seviyeli durumlara uygundur. process.nextTick yalnızca Node.js'e özel erken çalıştırma ihtiyacı gerçekten varsa tercih edilmelidir. setImmediate uzun bir senkron işi parçalarken veya event loop'a kontrol vermek isterken işlevsel olabilir. Seçimi performans tahminiyle değil, ölçüm ve açık yürütme ihtiyacıyla yapmak gerekir.

Microtask Starvation Nedir?

Microtask starvation, sürekli yeni microtask üretildiği için event loop'un diğer fazlara yeterince hızlı dönememesi durumudur. Uygulama tamamen kilitlenmiş görünmeyebilir fakat I/O callback'leri ve timer'lar belirgin biçimde gecikebilir. Promise ve nextTick mekanizmalarının yüksek öncelikli davranışı bu sorunun temel nedenlerinden biridir. Sorun özellikle kendi kendini yeniden planlayan zincirlerde daha görünür hale gelir. Çözüm çoğu zaman işi sınırlamak, parçalamak ve event loop'a düzenli olarak kontrol vermektir.

Sonsuz process.nextTick() Zinciri

Bir nextTick callback'i her çalıştığında yeni bir nextTick planlarsa event loop normal fazlarına dönemeyebilir. Böyle bir desen düşük trafikte bile beklenmeyen kilitlenme benzeri davranış oluşturabilir. I/O tamamlanmış olsa bile callback'ler çalışmak için fırsat bulamaz. Bu nedenle recursive nextTick kullanımı çok dikkatli tasarlanmalıdır. Tekrarlanan işler için queue, timer veya kontrollü bir scheduling modeli daha güvenlidir.

Uzun Promise Zincirleri

Uzun Promise zincirleri tek başına her zaman sorun değildir, çünkü çoğu zincir gerçek I/O beklemeleri içerir. Sorun, Promise devamlarının sürekli senkron CPU işi yapması ve hemen yeni resolved Promise üretmesidir. Bu durumda microtask queue uzun süre boşalmayabilir. Özellikle büyük veri dönüşümlerinin Promise içine sarılması yanıltıcı bir asenkronluk hissi yaratır. CPU işi gerekiyorsa parçalama veya worker stratejisi değerlendirilmelidir.

I/O Callback'lerinin Gecikmesi

I/O operasyonunun işletim sistemi tarafında tamamlanması, JavaScript callback'inin aynı anda çalışacağı anlamına gelmez. Event loop yoğun microtask veya senkron hesaplama altındaysa callback sırada bekler. Bu fark uygulamanın dış servisini gereksiz yere suçlamasına yol açabilir. Gerçek teşhis için dış servis süresiyle event loop delay metriğini birlikte incelemek gerekir. Böylece ağ gecikmesi ile uygulama içi scheduling gecikmesi ayrılabilir.

Event Loop'a Kontrol Vermek

Uzun bir görevi küçük parçalara bölerek event loop'a düzenli kontrol vermek latency açısından etkili olabilir. setImmediate bu amaçla kullanılabilecek araçlardan biridir. Her parçanın süresi kısa tutulduğunda socket ve timer callback'leri aralarda çalışabilir. Bu yöntem toplam hesaplama maliyetini azaltmaz fakat sistemin responsive kalmasına yardımcı olur. CPU kullanımı hâlâ yüksekse işi Worker Threads tarafına taşımak daha iyi sonuç verebilir.

Async Kod Her Zaman Non-Blocking midir?

Hayır, async fonksiyon yazmak bir işlemi otomatik olarak non-blocking hale getirmez. async keyword yalnızca fonksiyonun Promise tabanlı davranışını ve await kullanımını düzenler. Fonksiyonun içinde 500 milisaniye süren senkron bir hesaplama varsa ana JavaScript thread'i yine 500 milisaniye meşgul olur. Bu ayrım Node.js async await Promise stream ve worker threads karşılaştırması yapılırken en kritik noktalardan biridir. Kodun sözdizimine değil, gerçek çalışma biçimine bakmak gerekir.

async Keyword'ünün Gerçekte Yaptığı

async olarak tanımlanan bir fonksiyon her zaman Promise döndürür. Fonksiyon içinde await kullanılabilir ve Promise sonuçları daha okunabilir bir akışla ele alınabilir. Ancak async keyword fonksiyon gövdesindeki senkron JavaScript'i farklı bir thread'e taşımaz. await noktasına ulaşmadan önce yapılan ağır işlem doğrudan ana thread üzerinde çalışır. Bu nedenle async etiketi performans garantisi olarak görülmemelidir.

Senkron İşlemi Async Fonksiyona Koymak

Bir senkron fonksiyonu async fonksiyon içine koymak işlem modelini değiştirmez. Örneğin büyük bir döngü async fonksiyon içinde çalıştırıldığında event loop yine döngü tamamlanana kadar bekler. Promise döndürülmesi yalnızca çağıran kodun sonuçla nasıl etkileştiğini değiştirir. Bu hata üretim servislerinde özellikle büyük payload'lar geldikçe görünür hale gelir. Gerçek çözüm işi küçültmek, başka sisteme devretmek veya worker üzerinde yürütmektir.

Büyük Döngüler

Milyonlarca eleman üzerinde çalışan senkron döngü event loop'u uzun süre meşgul edebilir. Döngünün her adımı ucuz görünse bile toplam süre önemli hale gelir. API isteği sırasında böyle bir iş yapılırsa diğer kullanıcıların callback'leri de gecikebilir. İş parçalara bölünebilir, veritabanına devredilebilir veya Worker Thread üzerinde yürütülebilir. Hangi yaklaşımın uygun olduğu veri büyüklüğü ve latency hedefiyle belirlenmelidir.

Büyük JSON Parse/Stringify

JSON.parse ve JSON.stringify senkron çalışan işlemlerdir. Çok büyük payload'larda bu işlemler CPU ve bellek üzerinde ciddi baskı oluşturabilir. Özellikle onlarca veya yüzlerce megabayt veriyi tek seferde parse etmek event loop gecikmesini yükseltir. Mümkünse veri formatı, payload sınırı veya streaming yaklaşımı yeniden düşünülmelidir. Büyük dönüşüm kaçınılmazsa worker kullanımı benchmark ile değerlendirilmelidir.

CPU-Intensive İşlemler

CPU-intensive işlemler zamanının büyük bölümünü hesaplama yaparak geçirir. Karmaşık veri dönüşümleri, görüntü işleme, bazı şifreleme operasyonları ve matematiksel hesaplamalar buna örnek olabilir. Bunları ana event loop üzerinde çalıştırmak concurrent I/O kapasitesini düşürür. Worker Thread havuzu gerçek paralellik sağlayarak ana thread'i daha erişilebilir tutabilir. Ancak worker iletişim ve serialization maliyeti de ölçülmelidir.

Sync API Kullanımı

Node.js'in birçok modülünde async API'lerin yanında sync alternatifleri de bulunur. Sunucu çalışırken yoğun istek yolu üzerinde readFileSync gibi çağrılar event loop'u doğrudan bloke eder. Başlangıç aşamasında küçük konfigürasyon dosyaları okumak gibi kontrollü durumlar farklı değerlendirilebilir. İstek işleme sırasında ise mümkün olduğunca asenkron API kullanmak gerekir. Profiling sırasında sync çağrıları kolay kazanım sağlayan optimizasyon alanlarından biri olabilir.

I/O-Bound ve CPU-Bound İş Yükleri

Asenkron işleme mimarisinin ilk sorusu hangi kütüphaneyi kullanacağımız değil, iş yükünün nerede zaman harcadığı olmalıdır. I/O-bound görevler çoğunlukla dış kaynak beklerken CPU-bound görevler hesaplama yaparken süre tüketir. Bu iki kategorinin concurrency davranışı ve ölçeklendirme yöntemi farklıdır. Yanlış sınıflandırma gereksiz worker thread, aşırı Promise concurrency veya event loop bloklanması ile sonuçlanabilir. Bu nedenle metriklerden yararlanarak gerçek darboğazı görmek önemlidir.

I/O-Bound Nedir?

I/O-bound işlerde toplam sürenin önemli bölümü ağ, disk, veritabanı veya başka dış kaynaklardan cevap bekleyerek geçer. CPU bu bekleme sırasında çoğu zaman başka işleri ilerletebilir. Node.js'in event-driven modeli tam olarak bu senaryolarda güçlüdür. Concurrency artırılarak bekleme süreleri üst üste bindirilebilir ve throughput yükseltilebilir. Yine de aşağıdaki sistemlerin kapasitesi nedeniyle concurrency mutlaka sınırlanmalıdır.

Database Query

Veritabanı sorguları çoğu Node.js uygulamasında I/O-bound iş yüküdür. JavaScript tarafı sorguyu gönderir ve sonuç dönene kadar başka görevleri ilerletebilir. Buradaki gerçek sınır çoğu zaman connection pool kapasitesi, sorgu maliyeti ve veritabanı kaynaklarıdır. Yüzlerce sorguyu sınırsız Promise.all ile göndermek daha hızlı sonuç yerine saturation oluşturabilir. Concurrency değeri pool büyüklüğü ve gerçek benchmark verileriyle uyumlu seçilmelidir.

HTTP API

Harici HTTP API çağrıları tipik I/O-bound işlemlerdir. Birden fazla bağımsız çağrıyı concurrent başlatmak toplam latency'yi ciddi biçimde azaltabilir. Ancak rate limit, socket sayısı ve karşı servisin kapasitesi hesaba katılmalıdır. Timeout ve cancellation olmadan yapılan istekler kaynakları gereksiz yere açık tutabilir. Production sistemlerinde bounded concurrency ve retry politikası birlikte tasarlanmalıdır.

Dosya I/O

Dosya okuma ve yazma işlemlerinin büyük bölümü asenkron API'lerle event loop'u uzun süre bloklamadan yürütülebilir. Bununla birlikte çok sayıda eş zamanlı dosya operasyonu libuv thread pool üzerinde baskı oluşturabilir. Büyük dosyalarda readFile yerine stream kullanmak bellek tüketimini azaltır. Dosya sistemi performansı disk türü ve ortam özelliklerinden de etkilenir. Bu nedenle yalnızca JavaScript seviyesindeki süreleri ölçmek yeterli değildir.

Network Socket

Network socket işlemleri Node.js'in event-driven modelinin güçlü olduğu alanlardan biridir. Binlerce bağlantının her biri için ayrı JavaScript thread'i oluşturmak gerekmez. İşletim sistemi olay bildirimleri ve event loop bu bağlantıları verimli biçimde yönetebilir. Buna rağmen her bağlantının tuttuğu buffer ve uygulama state'i bellek tüketmeye devam eder. Yük testleri bağlantı sayısını, throughput'u ve event loop delay'i birlikte değerlendirmelidir.

CPU-Bound Nedir?

CPU-bound görevlerde süre büyük ölçüde işlemcinin hesaplama yapmasıyla geçer. Bu görevleri yalnızca daha fazla Promise ile başlatmak gerçek paralellik sağlamaz. Ana thread yoğun hesaplamayla meşgulse I/O callback'leri gecikir ve istek latency'si yükselir. Worker Threads, native çözümler veya ayrı servisler bu tür işler için değerlendirilebilir. Görevin ne kadar uzun sürdüğü ve ne sıklıkta çalıştığı mimari kararı belirler.

Image Processing

Görüntü yeniden boyutlandırma, filtreleme veya format dönüştürme işlemleri CPU tüketebilir. Kullanılan kütüphane işlemi native thread'lere taşıyorsa gerçek davranışı ayrıca incelemek gerekir. Saf JavaScript hesaplaması ana thread'de yoğun çalışıyorsa Worker Thread havuzu yararlı olabilir. Büyük görseller aynı zamanda yüksek bellek tüketimine neden olabilir. Bu yüzden benchmark yalnızca süreyi değil memory peak değerlerini de içermelidir.

PDF Generation

PDF üretimi kullanılan kütüphaneye ve belge içeriğine göre CPU, bellek ve I/O tüketebilir. Karmaşık raporları HTTP request içinde üretmek kullanıcıyı uzun süre bekletebilir. Bu nedenle çoğu kurumsal senaryoda işi queue üzerinden background worker'a taşımak daha sağlıklıdır. Kullanıcıya job ID verilerek sonuç daha sonra sunulabilir. Büyük üretim hacminde worker concurrency ayrıca sınırlandırılmalıdır.

Büyük JSON Parsing

Büyük JSON verilerini parse etmek doğrudan CPU ve bellek tüketen senkron bir işlemdir. Payload büyüdükçe event loop üzerinde oluşturduğu kesinti daha görünür olur. Mümkünse payload küçültülmeli veya stream edilebilir bir veri formatı tercih edilmelidir. Çok büyük verinin zorunlu olarak parse edilmesi gerekiyorsa ayrı worker seçeneği ölçülebilir. Her durumda giriş boyutu için güvenli limitler tanımlamak önemlidir.

Compression

Sıkıştırma CPU kullanan bir işlemdir fakat Node.js'teki bazı compression API'leri libuv thread pool üzerinden çalışabilir. Bu nedenle ana thread boş görünürken thread pool saturation oluşabilir. Aynı anda çok sayıda compression işi başlatıldığında diğer pool kullanıcıları da bekleyebilir. Concurrency sınırı ve UV_THREADPOOL_SIZE değerlendirmesi ölçüme dayanmalıdır. Büyük dosyalarda stream tabanlı compression bellek kullanımını daha kontrollü tutar.

Matematiksel Hesaplamalar

Yoğun matematiksel hesaplamalar saf JavaScript içinde yürütüldüğünde event loop'u bloke edebilir. Özellikle yüzlerce milisaniye veya saniyeler süren algoritmalar HTTP servislerinde belirgin latency üretir. Worker Threads bu tür görevleri farklı CPU çekirdeklerinde yürütebilir. Küçük hesaplamalarda worker iletişim maliyeti kazançtan fazla olabilir. Karar vermeden önce gerçek veri setiyle benchmark yapılmalıdır.

İş Yükünü Yanlış Sınıflandırmanın Sonuçları

I/O-bound bir işi gereksiz yere Worker Thread'e taşımak thread ve iletişim maliyeti ekler. CPU-bound bir işi async fonksiyon içine koyup çözüldüğünü düşünmek ise event loop'u bloke etmeye devam eder. Benzer biçimde büyük dosyayı tek seferde belleğe almak stream avantajını ortadan kaldırır. Yanlış sınıflandırma kapasite problemlerini kod seviyesinde gizleyebilir ve sistem yük altında çökmeye başlayabilir. Doğru strateji her zaman gerçek darboğazın ölçülmesiyle başlar.

Hangi Asenkron İşleme Stratejisini Seçmeliyiz?

Tek bir Node.js asenkron işleme yöntemi tüm iş yükleri için ideal değildir. Kısa I/O görevlerinde async/await yeterliyken büyük veri akışlarında stream, CPU işlerinde Worker Threads ve uzun süreli işlemlerde durable queue daha doğru olabilir. Mimari seçim latency hedefi, veri boyutu, hata toleransı ve kaynak limitleriyle birlikte yapılmalıdır. Ben karar verirken önce işin yaşam süresini, sonra kaynak türünü ve son olarak dayanıklılık ihtiyacını değerlendiriyorum. Bu yaklaşım gereksiz altyapı eklemeden doğru aracın seçilmesini kolaylaştırır.

Kısa I/O İşlemi → async/await

Kısa veritabanı sorguları ve HTTP istekleri için async/await genellikle en okunabilir çözümdür. Kod akışı senkron görünüme yakın olduğu için hata yönetimi ve bakım kolaylaşır. Burada await edilen API'nin gerçekten asenkron olması gerekir. Bağımsız operasyonlar gereksiz yere ardışık await edilmemelidir. Timeout ve AbortSignal gibi kontrol mekanizmaları da akışa dahil edilmelidir.

Çoklu Bağımsız I/O → Bounded Concurrency

Birden fazla bağımsız I/O işlemi varsa hepsini sırayla beklemek gereksiz latency yaratabilir. Buna karşılık binlerce işi aynı anda başlatmak da downstream sistemleri aşırı yükleyebilir. Bounded concurrency iki uç arasında kontrollü bir denge sağlar. Örneğin aynı anda yalnızca 20 API isteği çalıştırılıp biri tamamlandıkça yeni görev başlatılabilir. Limit gerçek servis kapasitesi ve hata oranlarına göre ayarlanmalıdır.

Büyük Veri Akışı → Streams

Yüzlerce megabayt veya gigabayt büyüklüğündeki veriyi tek seferde RAM'e almak çoğu zaman gereksizdir. Stream modeli veriyi küçük parçalar halinde okumaya, dönüştürmeye ve hedefe yazmaya izin verir. Bu yaklaşım memory kullanımını sınırlar ve ilk sonuçların daha erken üretilmesini sağlar. Backpressure mekanizması hızlı producer ile yavaş consumer arasındaki akışı kontrol eder. Dosya aktarımı, ETL ve export işlemlerinde stream ilk değerlendireceğim seçeneklerden biridir.

Lazy Veri Kaynağı → Async Iterator

Sayfalı API, cursor veya stream gibi kaynaklarda tüm veriyi baştan yüklemek gerekmez. Async iterator veriyi ihtiyaç oldukça üretme modelini sunar. for await...of ile tüketici sade ve okunabilir bir döngü kullanabilir. Bu yaklaşım doğal olarak lazy consumption davranışına uygundur. Cancellation ve cleanup kuralları ayrıca tasarlanarak kaynak sızıntıları önlenmelidir.

CPU-Intensive İş → Worker Threads

Uzun hesaplamalar ana event loop üzerinde yürütülmemelidir. Worker Threads aynı process içinde farklı JavaScript thread'leri kullanarak CPU paralelliği sağlayabilir. Üretimde her iş için yeni worker açmak yerine havuz kullanmak daha verimlidir. Veri aktarımı büyükse structured clone maliyeti veya transferable object seçenekleri incelenmelidir. Worker sayısı CPU çekirdeği ve HTTP servisinin ihtiyaç duyduğu headroom düşünülerek belirlenmelidir.

Harici Program → Child Process

FFmpeg benzeri harici executable çalıştırılması gerektiğinde child process daha doğal bir seçenek olabilir. Child process ayrı proses izolasyonu sağlar ve farklı runtime ya da binary çalıştırılmasına izin verir. Worker Thread ile karşılaştırıldığında memory paylaşımı ve startup davranışı farklıdır. Güvenilmeyen kodların daha güçlü izolasyonu gereken bazı senaryolarda da proses sınırı değerlidir. Süreç yaşam döngüsü, timeout ve stdout boyutu mutlaka kontrol edilmelidir.

Uzun Süren İş → Background Queue

Dakikalar sürebilen bir görevi HTTP isteği açıkken tamamlamaya çalışmak dayanıklılık sorunları yaratır. Bunun yerine istek doğrulanıp bir job oluşturulabilir ve kullanıcıya 202 Accepted ile job kimliği döndürülebilir. Worker görevi kuyruktan alarak bağımsız şekilde işler. Retry, idempotency ve DLQ davranışı böylece açık biçimde yönetilebilir. Deployment sırasında yarım kalan görevlerin yeniden alınması da queue altyapısıyla daha güvenli hale gelir.

Yüksek Hacimli Event Akışı → Message/Event Broker

Çok sayıda event farklı consumer grupları tarafından işlenecekse message veya event broker kullanımı önem kazanır. RabbitMQ mesaj yönlendirme ve work queue senaryolarında güçlü seçenekler sunarken Kafka durable event log ve replay odaklı yapısıyla farklı ihtiyaçlara hitap eder. Broker seçimi yalnızca throughput değerine bakılarak yapılmamalıdır. Ordering, retry, replay ve tüketici semantiği birlikte değerlendirilmelidir. Event-driven sistemlerde sözleşme ve idempotency tasarımı broker seçiminden bile daha kritik olabilir.

Callback'ten async/await'e Node.js Asenkron Programlama

Node.js'in erken dönem API'leri callback tabanlı bir modele büyük ölçüde dayanıyordu. Promise ve async/await desteği geliştikçe uygulama kodu daha doğrusal ve okunabilir hale geldi. Buna rağmen callback davranışını anlamak event loop ve eski kütüphanelerle çalışırken hâlâ değerlidir. Modern projelerde yeni kod için Promise tabanlı API'leri tercih etmek hata yönetimini sadeleştirir. Geçiş yapılırken yalnızca sözdizimini değil concurrency davranışını da korumak gerekir.

Callback Pattern

Callback pattern, bir işlemin tamamlanmasından sonra çalışacak fonksiyonun argüman olarak verilmesine dayanır. Node.js'in birçok eski API'si bu biçimde tasarlanmıştır. Basit kullanımda etkili olsa da iç içe bağımlı işlemler okunabilirliği hızla düşürebilir. Hata akışlarının her seviyede ayrı yönetilmesi de kodu zorlaştırabilir. Promise dönüşümü kontrol akışını daha belirgin hale getirir.

Error-First Callback

Error-first callback modelinde callback'in ilk argümanı hata, sonraki argümanları sonuç değerleridir. Bu Node.js ekosisteminde uzun süre standart bir desen olarak kullanılmıştır. Hata yoksa ilk argüman genellikle null olur. Promise tabanlı API'lerde aynı fikir resolve ve reject kanallarıyla ifade edilir. Eski modülleri modernleştirirken bu semantiğin doğru korunması önemlidir.

Promise

Promise gelecekte tamamlanacak bir asenkron işlemin başarılı veya başarısız sonucunu temsil eder. then, catch ve finally ile sonuç akışı yönetilebilir. Promise yapısı birden fazla görevi Promise.all veya allSettled gibi yardımcılarla birleştirmeyi de kolaylaştırır. Ancak Promise oluşturmak işlemi otomatik olarak paralel thread'de çalıştırmaz. Bu ayrım özellikle CPU ağırlıklı fonksiyonlarda unutulmamalıdır.

async/await

async/await Promise tabanlı kodu daha doğrusal bir biçimde yazmayı sağlar. try/catch kullanımı hata yönetimini birçok durumda sadeleştirir. Buna rağmen her satıra sırayla await eklemek bağımsız I/O görevlerinde gereksiz waterfall latency yaratabilir. Bağımlı işlemler sıralı, bağımsız işlemler kontrollü concurrent biçimde yürütülmelidir. Okunabilirlik ve performans birlikte düşünülmelidir.

util.promisify

util.promisify callback tabanlı uygun API'leri Promise döndüren fonksiyonlara dönüştürmek için kullanılabilir. Bu araç özellikle eski Node.js modülleriyle modern async/await kodunu bir araya getirirken faydalıdır. Her callback API'sinin promisify ile doğru çalışacağı varsayılmamalıdır. Özel callback imzaları veya birden fazla sonuç değeri ek düzenleme gerektirebilir. Mümkün olduğunda kütüphanenin sunduğu yerleşik Promise API'si tercih edilmelidir.

Modern Node.js Kodlarında Promise API'lerini Tercih Etmek

Yeni Node.js projelerinde yerleşik Promise tabanlı API'ler okunabilirlik ve hata yönetimi açısından iyi bir varsayılan seçimdir. fs/promises bunun bilinen örneklerinden biridir. Promise API kullanmak callback sarmalama kodunu azaltır ve TypeScript ile daha açık sözleşmeler oluşturabilir. Yine de performans davranışı kullanılan alt sistem tarafından belirlenir. Promise arayüzü, concurrency ve kaynak sınırı kararlarının yerine geçmez.

Sequential Await Nedir?

Sequential await, bir asenkron görevin tamamlanmasını bekledikten sonra diğerinin başlatılmasıdır. İşler birbirine bağımlıysa bu davranış doğrudur ve gerekli veri sırasını korur. Bağımsız işlemlerde ise her bekleme toplam latency'ye eklenebilir. Bu durum bazen kod okunabilir olduğu için performans incelemelerinde gözden kaçar. Bağımsızlık tespit edildiğinde görevleri birlikte başlatmak önemli hız kazancı sağlayabilir.

Birbirine Bağımlı İşlemler

İkinci işlemin girdisi birinci işlemin sonucuna bağlıysa sequential await mantıklıdır. Örneğin önce kullanıcı kaydını bulup ardından kullanıcı kimliğiyle siparişleri sorgulamak gerekebilir. Burada işlemleri aynı anda başlatmak mümkün değildir. Kodun bağımlılığı açık biçimde göstermesi bakım kolaylığı sağlar. Gereksiz optimizasyon uğruna veri akışını yapay biçimde zorlaştırmamak gerekir.

Gereksiz Sequential Await

Kullanıcı bilgisi, ayarları ve bildirim tercihleri birbirinden bağımsız servislerden geliyorsa bunları sırayla beklemek gereksiz olabilir. Her istek 100 milisaniye sürerse üç sequential await yaklaşık 300 milisaniyelik bekleme üretebilir. Aynı işler uygun concurrency ile yaklaşık en yavaş isteğin süresine yaklaşabilir. Bununla birlikte tüm isteklerin aynı downstream sisteme gitmesi durumunda kapasite sınırı kontrol edilmelidir. Paralel başlatmak her zaman sınırsız başlatmak anlamına gelmez.

Waterfall Latency

Waterfall latency, bağımsız olabilecek işlemlerin birbirini gereksiz yere beklemesi sonucu toplam sürenin üst üste eklenmesidir. Dağıtık servislerde birkaç küçük bekleme birleşerek kullanıcı açısından büyük gecikme oluşturabilir. Tracing araçları bu zincirleri görmeyi kolaylaştırır. Bağımlılık grafiği çıkarılarak hangi operasyonların aynı anda başlatılabileceği belirlenebilir. Bu yöntem performans optimizasyonunda çoğu zaman kod seviyesindeki mikro iyileştirmelerden daha fazla katkı sağlar.

Bağımsız I/O İşlerini Paralel Başlatmak

Bağımsız I/O işlemleri aynı anda başlatıldığında bekleme süreleri üst üste bindirilebilir. Promise.all küçük ve kontrollü gruplarda bunun için pratik bir yöntemdir. Büyük listelerde ise concurrency limiter kullanmak gerekir. Downstream servisin rate limit'i veya connection pool kapasitesi üst sınır olarak düşünülmelidir. Performans hedefi yalnızca en hızlı süre değil, sürdürülebilir throughput olmalıdır.

Promise.all() Nasıl Kullanılır?

Promise.all birden fazla Promise'i birlikte takip eder ve tümü başarılı olduğunda sonuçları tek Promise üzerinden döndürür. Bağımsız birkaç I/O işlemi için oldukça kullanışlıdır. Sonuç sırası Promise'lerin tamamlanma sırasına değil, giriş listesindeki sıraya göre korunur. Herhangi bir Promise reject olduğunda Promise.all da reject olur. Bu fail-fast davranışı iş gereksinimine uygun değilse allSettled gibi alternatifler düşünülmelidir.

Bağımsız Async İşlemler

Promise.all en anlamlı kullanımını birbirinden bağımsız asenkron operasyonlarda bulur. Profil, ayarlar ve abonelik bilgileri farklı kaynaklardan okunuyorsa görevler birlikte başlatılabilir. Böylece her isteğin bekleme süresi toplam süreye ayrı ayrı eklenmez. İşlemler aynı sınırlı kaynağı kullanıyorsa concurrency yine göz önünde bulundurulmalıdır. Birkaç görev ile binlerce görevi aynı yöntemle değerlendirmek doğru değildir.

Concurrent I/O

Promise.all içindeki I/O görevleri uygun API'lerle başlatıldığında concurrent ilerleyebilir. Bu durum gerçek CPU paralelliğiyle karıştırılmamalıdır. Ağ istekleri büyük ölçüde bekleme halinde olduğu için concurrency verimlidir. Çok fazla concurrent bağlantı ise socket ve downstream kapasitesini tüketebilir. Büyük veri listelerinde Promise pool daha kontrollü bir tasarım sunar.

Fail-Fast Davranışı

Promise.all içindeki Promise'lerden biri reject olduğunda dış Promise hemen reject edilir. Diğer işlemler otomatik olarak iptal edilmez ve arka planda devam edebilir. Bu ayrıntı özellikle pahalı veya yan etkili operasyonlarda önemlidir. Gerçek cancellation isteniyorsa AbortController gibi mekanizmalar ayrıca tasarlanmalıdır. Fail-fast davranışı yalnızca sonuç bekleme semantiğini ifade eder.

Sonuç Sırasının Korunması

Promise.all sonuç dizisini giriş Promise'lerinin sırasına göre oluşturur. Birinci görev en son tamamlansa bile sonucu yine ilk indekste yer alır. Bu özellik listeler üzerinde concurrent işlem yaparken eşleşmeyi kolaylaştırır. Bununla birlikte çok büyük listelerin tüm sonuçlarını bellekte tutmak memory baskısı oluşturabilir. Streaming veya incremental processing gerekebilecek durumlarda farklı desenler kullanılmalıdır.

Promise.all Ne Zaman Kullanılmalı?

Promise.all az sayıda, bağımsız ve hata açısından birlikte ele alınabilen görevlerde iyi bir seçenektir. İşlerin toplam sayısı ve downstream kapasitesi önceden biliniyorsa kullanımı basittir. Binlerce görevi doğrudan tek Promise.all içine koymak yerine sınırlı concurrency düşünülmelidir. Kısmi başarı gerekiyorsa allSettled daha uygun olabilir. Seçim iş semantiğine göre yapılmalıdır.

Promise.all() Ne Zaman Tehlikelidir?

Promise.all özellikle büyük listelerde tüm görevleri anında başlatabildiği için kaynak kullanımını hızla yükseltebilir. Uygulama tarafındaki işlem kısa olsa bile downstream sistemlerin kapasitesi aşılabilir. Connection pool, socket, bellek ve API rate limit gibi sınırlar çoğu zaman ilk darboğazdır. Bu nedenle yüksek hacimli sistemlerde Promise.all yerine bounded concurrency güçlü bir varsayılan yaklaşımdır. Node.js asenkron işlemlerde event loop queue concurrency ve backpressure yönetimi birlikte düşünülmeden yalnızca Promise sayısını artırmak sürdürülebilir değildir.

10.000 HTTP Request'i Aynı Anda Başlatmak

On bin HTTP isteğini aynı anda başlatmak genellikle iyi bir performans stratejisi değildir. Socket sayısı, DNS, uzak servis kapasitesi ve yerel memory kullanımı hızla yükselebilir. Başlangıçta throughput artıyor gibi görünse bile timeout ve hata oranları kısa sürede büyüyebilir. Sınırlı sayıda concurrent worker ile görevleri sıradan almak daha güvenlidir. En uygun limit yük testiyle belirlenmelidir.

Database Connection Pool Saturation

Veritabanı pool'u örneğin 20 bağlantı sunuyorsa bin sorguyu aynı anda Promise.all ile başlatmak veritabanında bin paralel bağlantı oluşturmaz. İşlerin büyük kısmı pool içinde bekler ve uygulama ekstra Promise state'i tutar. Ayrıca sorgu timeout'ları ve lock davranışı daha zor yönetilebilir. Concurrency limitini pool kapasitesiyle uyumlu seçmek daha öngörülebilir sonuç verir. Pool büyüklüğünü rastgele artırmak veritabanı tarafındaki yükü daha da yükseltebilir.

API Rate Limit

Harici API sağlayıcıları saniye veya dakika bazında çağrı limitleri uygulayabilir. Sınırsız Promise.all bu sınırı birkaç milisaniyede aşabilir. Sonuç olarak 429 yanıtları, retry dalgası ve daha kötü toplam throughput ortaya çıkar. Token bucket veya queue-level limiter gibi yöntemlerle istek hızı kontrol edilmelidir. Rate limit bilgisi concurrency ayarının önemli girdilerinden biridir.

Socket Exhaustion

Her outbound HTTP çağrısı socket ve ilişkili sistem kaynakları tüketebilir. Çok yüksek eş zamanlılık ephemeral port veya dosya descriptor sınırlarına kadar ulaşabilir. Keep-alive ve bağlantı havuzlama bu maliyeti azaltabilir fakat sınırsızlığı çözmez. Agent ayarları ile uygulama concurrency limitleri birlikte değerlendirilmelidir. İşletim sistemi limitlerini yükseltmeden önce neden bu kadar bağlantıya ihtiyaç duyulduğu sorgulanmalıdır.

Memory Pressure

Her aktif Promise, closure, request nesnesi ve response buffer belirli miktarda bellek kullanır. Binlerce görevin aynı anda başlatılması toplam memory tüketimini hızla artırabilir. Garbage collector daha sık çalıştıkça latency de olumsuz etkilenebilir. Bounded concurrency aktif görev sayısını sınırlayarak memory profilini daha öngörülebilir hale getirir. Büyük response gövdelerinde streaming ek avantaj sağlar.

Downstream Service Overload

Bir servisin kendi CPU'su düşük olduğu için downstream servise sınırsız istek göndermek doğru değildir. Dağıtık sistem performansı en zayıf bağlantının kapasitesinden etkilenir. Çok agresif concurrency karşı servisin latency'sini artırır ve timeout'larla birlikte zincirleme arızaya dönüşebilir. Rate limit, circuit breaker ve adaptive concurrency bu riski azaltabilir. İyi servis davranışı yalnızca kendi performansını değil, komşu sistemlerin sağlığını da korur.

Promise.allSettled() Ne Zaman Kullanılmalı?

Promise.allSettled tüm Promise'lerin tamamlanmasını bekler ve her birinin başarılı veya başarısız sonucunu ayrı biçimde döndürür. Bir görevin hatasının diğer bağımsız sonuçları geçersiz kılmaması gereken durumlarda faydalıdır. Toplu bildirim, farklı kaynaklardan veri toplama veya batch operasyonları buna örnektir. Yine de allSettled concurrency sınırı sağlamaz. Büyük listelerde limiter ile birlikte kullanılmalıdır.

Partial Success

Kısmi başarının kabul edildiği işlerde tüm sonuçları görmek önemlidir. Örneğin on bağımsız entegrasyondan sekizi başarılı olurken ikisinin hatası ayrıca raporlanabilir. Promise.all ilk hatada dış sonucu reject edeceği için bu senaryoda daha az uygun olabilir. allSettled her işin durumunu ayrı döndürür. İş katmanı başarılı ve başarısız sonuçları kendi kurallarına göre değerlendirebilir.

Batch Processing

Batch işlerinde her öğenin sonucu bağımsız tutulmak istenebilir. Örneğin 100 kaydın 95'i başarıyla işlenirken beş kaydın retry listesine alınması mümkündür. allSettled bu ayrımı kolaylaştırır. Batch boyutu yine memory ve downstream kapasitesine göre sınırlandırılmalıdır. Büyük veri setlerinde batch'ler sırayla veya bounded concurrency ile ilerletilebilir.

Bağımsız İşler

Bir işin başarısızlığının diğer işi durdurmaması gerekiyorsa allSettled doğal bir seçenektir. Bildirim kanalları buna iyi örnektir çünkü e-posta başarısız olsa bile başka kanal başarılı olabilir. Sonuçların iş açısından gerçekten bağımsız olması gerekir. Ortak transaction gerektiren görevlerde bu yaklaşım yanlış semantik üretebilir. Teknik API seçimi iş kuralıyla uyumlu olmalıdır.

Failed Result'ları Ayrı İşlemek

allSettled sonucu fulfilled ve rejected durumlarını ayrı incelemeye izin verir. Başarısız öğeler loglanabilir, metriğe eklenebilir veya tekrar işleme kuyruğuna gönderilebilir. Hata nesnelerinin güvenli biçimde serialize edilmesi gerekebilir. Kullanıcıya ham iç hata detaylarını döndürmekten kaçınılmalıdır. Hata sınıflandırması retry politikasının da temel girdisidir.

Retry İçin Başarısızları Seçmek

Başarısız sonuçların tümünü otomatik retry etmek doğru değildir. Timeout veya geçici 503 hataları tekrar denenebilirken validation hataları genellikle tekrar denenmemelidir. allSettled ile başarısız kayıtlar seçildikten sonra hata türüne göre filtre uygulanabilir. Retry sayısı ve backoff sınırları açık biçimde tanımlanmalıdır. Böylece batch sistemi sonsuz tekrar döngüsüne girmez.

Promise.race ve Promise.any

Promise.race ve Promise.any benzer görünse de farklı tamamlanma semantiğine sahiptir. race ilk tamamlanan Promise'in başarılı veya başarısız sonucunu döndürür. any ise ilk başarılı sonucu arar ve önceki rejection'ları tolere eder. Timeout veya birden fazla kaynaktan veri alma gibi senaryolarda bu fark önemlidir. Her iki API de başlatılmış diğer işleri otomatik olarak iptal etmez.

Promise.race()

Promise.race verilen Promise'lerden ilk settle olanın sonucunu döndürür. İlk tamamlanan işlem reject olursa race de reject olur. Timeout Promise'i ile gerçek operasyonu yarıştırmak sık kullanılan örneklerden biridir. Ancak timeout kazanırsa gerçek operasyon arka planda çalışmaya devam edebilir. Gerçek kaynak iptali için AbortSignal gibi mekanizmalar ayrıca kullanılmalıdır.

Timeout Pattern

Timeout pattern, bir operasyonun belirli süreden fazla beklenmesini önler. Basit Promise.race yaklaşımı dışarıya hızlı hata döndürse bile alt operasyonu iptal etmeyebilir. Bu nedenle desteklenen API'lerde AbortController tercih edilmelidir. Timeout süresi downstream servis SLO değerleri ve üst request deadline ile uyumlu olmalıdır. Her katmanın birbirinden bağımsız uzun timeout kullanması toplam latency'yi büyütebilir.

Promise.any()

Promise.any ilk başarılı Promise sonucu geldiğinde resolve olur. Önceki Promise'lerin reject olması başarılı bir alternatif varsa sonucu bozmaz. Aynı verinin birden fazla güvenilir kaynaktan alınabildiği bazı senaryolarda yararlı olabilir. Tüm Promise'ler başarısız olduğunda AggregateError üretir. Gereksiz kaynak tüketimini önlemek için kalan operasyonların iptali ayrıca değerlendirilmelidir.

Birden Fazla Kaynaktan İlk Başarılı Sonuç

Aynı içeriğin birincil ve yedek kaynaklardan alınması gerektiğinde ilk başarılı yanıt faydalı olabilir. Promise.any bu semantiği açık biçimde ifade eder. Ancak tüm kaynaklara aynı anda istek göndermek maliyet veya rate limit problemi yaratabilir. Hedged request gibi daha kontrollü yöntemlerde ikinci istek belirli gecikmeden sonra başlatılabilir. Bu optimizasyon yalnızca gerçek latency verileriyle gerekçelendirilmelidir.

AggregateError

Promise.any içindeki tüm Promise'ler başarısız olduğunda AggregateError ile karşılaşılır. Bu hata birden fazla başarısızlığın bilgisini taşıyabilir. Loglama sırasında hassas verilerin dışarı sızmaması için hata içerikleri filtrelenmelidir. Kullanıcıya genellikle sade bir domain hatası dönmek daha uygundur. İç hatalar observability sisteminde correlation ID ile ayrıntılı izlenebilir.

Bounded Concurrency Nedir?

Bounded concurrency aynı anda çalışan iş sayısını bilinçli bir üst sınırla kısıtlamaktır. Bu yaklaşım yüksek throughput elde etmeye çalışırken veritabanı, API, socket ve memory kaynaklarını korur. Büyük bir görev listesinin tamamını bir anda başlatmak yerine sınırlı sayıda worker görevleri sırayla alır. Bir worker tamamlandıkça bekleyen yeni iş başlatılır. Üretim Node.js servislerinde performans ve dayanıklılık için en değerli desenlerden biridir.

Sınırsız Concurrent İşlemin Sorunu

Sınırsız concurrency başlangıçta benchmark sonuçlarını iyi gösterebilir fakat kaynak limitlerine yaklaşıldığında davranış hızla bozulur. Timeout oranı artabilir, queue'lar büyüyebilir ve downstream servis saturation yaşayabilir. Memory kullanımı da aktif Promise sayısıyla birlikte yükselir. Bu sorunların tümü aynı anda görüldüğünde teşhis zorlaşır. Concurrency sınırı sistemi daha tahmin edilebilir hale getirir.

Concurrency Limiti

Concurrency limiti aynı anda en fazla kaç görevin aktif olacağını belirler. Örneğin limit 10 ise on birinci görev önceki görevlerden biri tamamlanana kadar bekler. Bu değer sabit veya dinamik olabilir. Sabit limit başlangıç için kolaydır fakat gerçek kapasite zaman içinde değişebilir. Ölçekli sistemlerde latency ve hata oranına göre adaptive concurrency değerlendirilebilir.

Promise Pool

Promise pool, belirli sayıda worker'ın ortak görev listesinden iş aldığı bir concurrency modelidir. Her worker bir görevi tamamladığında sıradaki görevi alır. Böylece aktif Promise sayısı kontrollü tutulur. Sonuç sırasının korunması gerekiyorsa görev indeksleriyle eşleme yapılabilir. Hata politikasının fail-fast mi yoksa partial success mı olacağı açıkça belirlenmelidir.

Worker Pool Mantığı

Worker pool kavramı yalnızca Worker Threads için kullanılmaz. Promise tabanlı I/O görevlerinde de sabit sayıda mantıksal worker queue'dan iş çekebilir. Bu yapı concurrency kontrolünü merkezi hale getirir. Her worker'ın boş kaldığında yeni görev alması kaynak kullanımını dengeler. Queue büyüklüğü de ayrı metrik olarak izlenmelidir.

Dinamik Concurrency

Dinamik concurrency sabit limit yerine sistem davranışına göre eş zamanlı görev sayısını değiştirmeyi amaçlar. Downstream latency artarsa concurrency azaltılabilir ve servis toparlandığında yeniden yükseltilebilir. Bu yaklaşım özellikle kapasitesi değişken dış sistemlerle çalışırken yararlı olabilir. Algoritmanın aşırı hızlı tepki vermesi salınım oluşturabileceği için dikkatli tasarım gerekir. P95 latency, hata oranı ve queue depth gibi sinyaller birlikte kullanılabilir.

Promise Pool Nasıl Çalışır?

Promise pool büyük görev listesini kontrollü sayıda eş zamanlı iş üzerinden tüketir. Havuz başlatıldığında belirli sayıda worker görev kuyruğundan iş alır. Her görev tamamlandığında aynı worker bir sonraki işi üstlenir. Böylece binlerce kayıt olsa bile yalnızca belirlenen kadar Promise aktif tutulabilir. Bu desen hem I/O kapasitesini hem bellek kullanımını yönetmek için oldukça etkilidir.

Sabit Worker Sayısı

En basit Promise pool modelinde worker sayısı önceden belirlenir. Örneğin beş worker aynı anda en fazla beş API isteği yürütür. Bu sayı ilgili servisin kapasitesine göre seçilmelidir. Çok düşük değer throughput'u sınırlar, çok yüksek değer ise saturation yaratabilir. Benchmark sonucu en uygun başlangıç değerini verir.

Shared Task Queue

Tüm worker'lar ortak bir task queue üzerinden sıradaki işi alabilir. Böylece görev süreleri farklı olsa bile yük dengesi doğal olarak oluşur. Uzun süren bir görev yalnızca onu alan worker'ı meşgul eder. Diğer worker'lar kısa görevleri işlemeye devam eder. Queue erişiminin doğru senkronizasyonu JavaScript içinde basit indeks yönetimiyle sağlanabilir.

Worker Boşalınca Yeni İş Alma

Pool verimliliğinin temel noktası worker'ın boş kalmamasıdır. Bir Promise settle olduğunda worker sıradaki görevi hemen başlatabilir. Böylece concurrency limiti aktif işler varken mümkün olduğunca dolu tutulur. Queue bittiğinde worker döngüsü sona erer. Cancellation varsa worker'ın yeni görev almadan önce signal durumunu kontrol etmesi yararlı olabilir.

Sonuçları Koruma

Concurrent işlerde tamamlanma sırası giriş sırasından farklı olabilir. Sonuçların orijinal sırayla döndürülmesi gerekiyorsa görev indeksleri saklanmalıdır. Her worker kendi sonucunu ilgili indekse yazar. Büyük sonuçların tamamını bellekte tutmak gerekmiyorsa stream veya callback tabanlı tüketim düşünülebilir. İş gereksinimi veri tutma stratejisini belirlemelidir.

Hata Yönetimi

Promise pool hata davranışını açıkça tanımlamalıdır. İlk hatada tüm yeni işleri durdurmak, hatalı görevleri kaydedip devam etmek veya retry uygulamak farklı seçeneklerdir. Aktif görevlerin iptal edilip edilmeyeceği de ayrı karardır. AbortController destekleniyorsa fail-fast senaryolarında faydalı olabilir. Hata politikası teknik kolaylığa değil iş semantiğine dayanmalıdır.

Node.js'te Concurrency Limiting Araçları

Concurrency sınırı sıfırdan yazılabileceği gibi küçük ve olgun yardımcı kütüphanelerle de yönetilebilir. p-limit ve p-map gibi araçlar Promise tabanlı görevlerde yaygın desenleri sadeleştirir. Daha özel ihtiyaçlarda kendi worker pool yapınızı yazmak mümkündür. Queue sistemleri ise process sınırını aşan ve durable olması gereken görevler için farklı bir katman sunar. Araç seçerken dependency maliyeti, observability ve hata davranışı birlikte değerlendirilmelidir.

p-limit

p-limit aynı anda kaç Promise tabanlı fonksiyonun çalışacağını sınırlandırmak için kullanılabilir. API'si küçük olduğu için basit servislerde anlaşılması kolaydır. Büyük bir liste üzerinde concurrency değerini merkezi biçimde kontrol etmenizi sağlar. Bununla birlikte durable queue veya dağıtık worker özellikleri sunmaz. İşler proses yaşam süresinden bağımsız olmalıysa farklı bir çözüm gerekir.

p-map

p-map bir veri listesini async mapper ile işlerken concurrency sınırı uygulamayı kolaylaştırır. map mantığını koruduğu için dönüşüm ağırlıklı işlemlerde okunabilir bir yapı sağlar. Hata yönetimi ve stop-on-error davranışı kullanılan sürümün API'sine göre kontrol edilmelidir. Çok büyük veya sonsuz veri kaynaklarında iterator tabanlı yaklaşım daha uygun olabilir. Bellek profilini veri büyüklüğüne göre ölçmek gerekir.

Kendi Promise Pool'unu Yazmak

Kendi Promise pool'unu yazmak temel ihtiyaçlarda oldukça kısa kodla mümkün olabilir. Bu yöntem dependency eklemeden özel error handling ve sonuç sırası davranışı oluşturmanızı sağlar. Ancak cancellation, retry, observability ve adaptive concurrency eklendikçe kod hızla büyür. Aynı problemi tekrar tekrar çözmek bakım maliyetini artırır. Basit gereksinimle platform özelliği arasındaki sınırı baştan belirlemek yararlıdır.

Queue Tabanlı Yaklaşım

Görevlerin proses kapanmasına rağmen korunması gerekiyorsa bellek içi Promise pool yeterli değildir. Durable queue işi harici depoda saklayarak worker'ların bağımsız biçimde tüketmesini sağlar. Retry, scheduling, priority ve monitoring gibi ihtiyaçlar da daha kolay yönetilir. Buna karşılık ekstra altyapı ve operasyon maliyeti eklenir. Kısa ömürlü I/O listesiyle uzun süren business job aynı araçla çözülmemelidir.

Framework Kullanmak mı Custom Çözüm mü?

Basit concurrency sınırı için küçük bir kütüphane veya birkaç satırlık pool yeterli olabilir. Dağıtık retry, job persistence ve dashboard gibi ihtiyaçlarda olgun queue altyapısı daha doğru seçimdir. Custom çözüm ilk aşamada kolay görünse de hata senaryoları arttıkça bakım yükü büyür. Ekibin operasyon deneyimi de seçim üzerinde etkilidir. Mimaride en az hareketli parçayla gereksinimi karşılamak iyi bir ilkedir.

Optimum Concurrency Değeri Nasıl Belirlenir?

Optimum concurrency değeri sabit bir Node.js formülüyle hesaplanamaz. Veritabanı pool büyüklüğü, dış API sınırları, ortalama latency, memory ve CPU davranışı birlikte etkili olur. En doğru yöntem gerçekçi yük altında farklı concurrency seviyelerini benchmark etmektir. Amaç en yüksek ham throughput değil, kabul edilebilir latency ve hata oranıyla sürdürülebilir kapasite bulmaktır. Özellikle p99 latency kötüleşmeye başladığında daha fazla concurrency fayda yerine zarar veriyor olabilir.

Database Pool Size

Veritabanı connection pool büyüklüğü concurrency için doğal üst sınırlardan biridir. Pool 20 bağlantı sunarken aynı anda yüzlerce sorgu başlatmak görevlerin yalnızca uygulama tarafında beklemesine neden olur. Bazı sorgular uzun sürüyorsa efektif kapasite daha da düşer. Concurrency değeri sorgu tiplerine göre ayrılabilir. Okuma ve ağır raporlama işlerini aynı pool davranışıyla değerlendirmemek gerekebilir.

External API Rate Limit

Dış API'nin saniyede 50 istek sınırı varsa uygulama concurrency ayarı bu gerçekle uyumlu olmalıdır. Yalnızca aynı anda kaç istek çalıştığı değil, zaman içindeki toplam istek hızı da önemlidir. Rate limiter bu nedenle concurrency limiter'dan farklı bir kontrol sağlar. İki mekanizma gerektiğinde birlikte kullanılabilir. 429 oranı yükseliyorsa mevcut strateji tekrar değerlendirilmelidir.

CPU Kullanımı

I/O ağırlıklı uygulamada concurrency artışı CPU kullanımını da yükseltebilir. Response parsing, serialization ve uygulama mantığı her tamamlanan I/O sonrasında CPU tüketir. CPU sürekli doygunluğa yaklaşıyorsa daha fazla concurrency latency'yi kötüleştirebilir. Event loop utilization bu noktada ek sinyal sağlar. Horizontal scaling veya görev ayrıştırma alternatifleri de düşünülmelidir.

Memory

Her aktif görev belirli miktarda bellek tüketir. Büyük response gövdeleri veya closure'lar concurrency yükseldikçe memory kullanımını doğrusal hatta bazı durumlarda daha hızlı artırabilir. Heap baskısı garbage collection süresini uzatır. Benchmark sırasında yalnızca ortalama memory değil peak değerler de izlenmelidir. Güvenli limit, üretim ortamındaki container sınırlarının altında headroom bırakmalıdır.

Downstream Latency

Downstream latency yükseldikçe aynı throughput'u korumak için daha fazla concurrent istek gerekebilir. Fakat latency artışı downstream saturation belirtisiyse concurrency artırmak sorunu büyütebilir. Bu nedenle sebep ve sonuç dikkatle ayrılmalıdır. Hata oranı, queue wait ve servis tarafı metrikleri birlikte incelenmelidir. Adaptive concurrency bu geri bildirimleri otomatik kullanabilir.

Benchmark

Concurrency seçiminin en güvenilir yolu gerçekçi benchmark yapmaktır. Test veri seti, request pattern ve downstream servis davranışı üretime benzemelidir. Concurrency 5, 10, 20 ve 40 gibi kontrollü adımlarla artırılıp throughput ile p95 ve p99 latency izlenebilir. Hata oranının yükseldiği kırılma noktası önemlidir. En yüksek rakam yerine güvenli çalışma bölgesi seçilmelidir.

Adaptive Concurrency

Adaptive concurrency sistem geri bildirimlerine göre eş zamanlı iş sayısını değiştirir. Latency veya hata oranı yükseldiğinde limit azaltılabilir. Sistem sağlıklı olduğunda concurrency kontrollü biçimde artırılabilir. Bu yaklaşım değişken kapasiteli downstream servislerde yararlı olabilir. Algoritmanın kararlı çalışması için ölçüm penceresi ve minimum maksimum sınırlar belirlenmelidir.

Batch Processing Nedir?

Batch processing kayıtları tek tek bağımsız işlemler yerine kontrollü gruplar halinde işlemeyi ifade eder. Database insert, API gönderimi veya dosya dönüştürme gibi işlemlerde round-trip maliyetini azaltabilir. Batch boyutu büyüdükçe throughput artabilir fakat memory ve hata etkisi de genişler. Çok büyük batch tek hata durumunda daha fazla işin yeniden yapılmasına neden olabilir. Bu nedenle batch size ölçümle seçilmeli ve retry sınırı açık biçimde tanımlanmalıdır.

Tek Tek İşleme

Her kaydı ayrı işlemek basit ve hata izolasyonu güçlü bir modeldir. Buna karşılık her kayıt için ayrı network round-trip gerekiyorsa throughput düşebilir. Küçük veri hacminde bu maliyet kabul edilebilir olabilir. Hacim büyüdüğünde batch API veya bulk database işlemleri ciddi avantaj sağlar. Önce gerçek darboğazı ölçmek gerekir.

Batch Size

Batch size aynı işlemde kaç kaydın ele alınacağını belirler. Çok küçük batch gereksiz round-trip üretirken çok büyük batch memory ve transaction süresini artırabilir. İdeal değer veri boyutuna ve hedef sistemin kapasitesine göre değişir. 100 kayıtla başlayan bir benchmark daha sonra 500 veya 1000 ile karşılaştırılabilir. Tek bir evrensel sayı yoktur.

Chunking

Chunking büyük veri listesini daha küçük parçalara bölmektir. Her chunk ayrı işlem, transaction veya concurrency grubu olarak ele alınabilir. Bu yaklaşım memory profilini daha kontrollü tutar. Hata durumunda tüm veri seti yerine yalnızca ilgili chunk tekrar çalıştırılabilir. Chunk sınırlarının domain semantiğini bozmadığından emin olunmalıdır.

Batch Başına Transaction

Her batch'i ayrı transaction içinde işlemek atomiklik ile performans arasında denge sağlayabilir. Çok büyük tek transaction lock süresini ve rollback maliyetini artırır. Çok küçük transaction ise commit overhead oluşturabilir. İş kuralları bir batch içindeki kayıtların birlikte başarılı olmasını gerektiriyorsa transaction sınırı buna göre belirlenmelidir. Veritabanı davranışı gerçek yükle test edilmelidir.

Batch Başına Retry

Batch başarısız olduğunda tüm batch'i tekrar denemek kolay fakat pahalı olabilir. Hatanın hangi kayıttan kaynaklandığı tespit edilebiliyorsa problemli kayıt ayrıştırılabilir. Geçici database timeout gibi hatalarda tüm batch retry mantıklı olabilir. Validation hatalarında ise aynı batch'i tekrar denemek fayda sağlamaz. Retry politikası hata sınıfına göre değişmelidir.

Throughput ve Latency Dengesi

Daha büyük batch çoğu zaman throughput'u artırırken tek kaydın tamamlanma latency'sini yükseltebilir. Sistem önce batch'in dolmasını bekliyorsa düşük trafikte ekstra gecikme oluşur. Zaman veya boyut tabanlı flush stratejisi bu dengeyi yönetebilir. Örneğin 500 kayıt veya 200 milisaniye dolduğunda batch gönderilebilir. Hedef değerler ürünün latency gereksinimine göre belirlenmelidir.

Büyük Array'leri Event Loop'u Bloke Etmeden İşlemek

Büyük JavaScript array'leri üzerinde uzun senkron dönüşümler event loop'u yüzlerce milisaniye meşgul edebilir. Bu süre boyunca yeni socket callback'leri ve timer görevleri çalışmak için bekler. Çözüm bazen işi küçük chunk'lara bölmek, bazen veritabanına devretmek, bazen de Worker Thread kullanmaktır. Hangi yöntem seçilirse seçilsin amaç ana thread üzerinde uzun kesintisiz çalışma süresini azaltmaktır. Event loop delay metriği iyileşmenin gerçekten oluşup oluşmadığını gösterir.

Synchronous Loop Problemi

for veya while döngüsü uzun sürdüğünde JavaScript call stack boşalmaz. Bu nedenle event loop başka callback'leri çalıştıramaz. Döngü yalnızca 300 milisaniye sürse bile düşük latency hedefli API için ciddi bir kesintidir. Kullanıcılar bu sorunu tüm endpoint'lerde hissedebilir. İş miktarı arttıkça çözümün mimari olması gerekir.

Chunk Size

Chunk size bir turda kaç öğenin işleneceğini belirler. Çok büyük chunk event loop'a yeterince sık kontrol vermez. Çok küçük chunk ise scheduling overhead oluşturabilir. En iyi değer öğe başına işlem maliyetine göre değişir. Milisaniye bazlı event loop gecikmesi ölçülerek uygun aralık bulunabilir.

Chunk Sonrası Yield

Her chunk tamamlandığında event loop'a kontrol vermek diğer I/O callback'lerinin çalışmasına fırsat tanır. setImmediate bu amaçla kullanılabilir. Bu yöntem tek thread üzerindeki toplam CPU işini azaltmaz. Fakat işi daha küçük zaman dilimlerine yayarak responsiveness sağlayabilir. CPU kapasitesi hâlâ yetersizse worker yaklaşımına geçmek gerekir.

setImmediate()

setImmediate büyük döngüler arasında planlı yield noktası oluşturmak için pratiktir. Örneğin her 5.000 kayıttan sonra Promise ile sarılmış setImmediate beklenebilir. Böylece event loop poll ve diğer fazlara dönebilir. Bu yöntem latency'yi iyileştirirken toplam işlem süresini biraz artırabilir. Performans hedefleri bu trade-off ile birlikte değerlendirilmelidir.

CPU İşini Worker'a Taşımak

Döngünün kendisi yoğun hesaplama yapıyorsa chunking yalnızca event loop etkisini dağıtır. Gerçek CPU paralelliği için Worker Thread daha uygun olabilir. Görevler worker pool'a gönderilerek ana thread'in HTTP isteklerine yanıt vermesi sağlanabilir. Büyük veri transferlerinde serialization maliyeti hesaba katılmalıdır. Worker kullanımının faydası benchmark ile doğrulanmalıdır.

Database'e Delegasyon

Filtreleme, aggregation veya sıralama gibi işler zaten veritabanının güçlü olduğu operasyonlardır. Milyonlarca kaydı Node.js'e çekip JavaScript ile filtrelemek gereksiz network ve memory maliyeti oluşturabilir. SQL veya uygun sorgu motoru işlemi verinin bulunduğu yerde daha verimli gerçekleştirebilir. Uygulama yalnızca gereken sonucu alır. Mimari optimizasyon bazen Node.js kodunu hızlandırmak değil, işi Node.js'ten kaldırmaktır.

Veriyi Node.js Yerine Database'de İşlemek Ne Zaman Daha İyidir?

Veritabanları filtreleme, sıralama, aggregation ve indeks kullanımı konusunda özel olarak tasarlanmış sistemlerdir. Büyük veri setini uygulamaya taşıyıp aynı işlemi JavaScript üzerinde yapmak çoğu zaman daha pahalıdır. Network transferi, memory kullanımı ve event loop üzerindeki CPU yükü birlikte artar. Sorgu planı iyi tasarlandığında işlemi veriye yakın yerde yapmak daha verimli olabilir. Uygulama ve database sorumluluğu performans kadar domain sınırları düşünülerek ayrılmalıdır.

Filtering

Filtreleme mümkün olduğunda sorgu katmanında yapılmalıdır. Gereksiz satırları uygulamaya göndermek hem network hem serialization maliyeti oluşturur. Uygun indeksler filtre performansını ciddi biçimde artırabilir. Node.js yalnızca iş için gerçekten gereken kayıtları almalıdır. Yetkilendirme filtreleri de mümkün olduğunda güvenli sorgu koşullarına yansıtılmalıdır.

Sorting

Büyük veri setini uygulamaya çekip JavaScript Array.sort kullanmak CPU ve memory tüketebilir. Veritabanı uygun indeks veya sorgu planıyla sıralamayı daha verimli gerçekleştirebilir. Pagination ile birlikte doğru ORDER BY stratejisi ayrıca önemlidir. Çok büyük offset değerleri bazı sistemlerde pahalı hale gelebilir. Keyset pagination bu durumda değerlendirilebilir.

Aggregation

Aggregation işlemleri veritabanlarının temel güçlü yönlerinden biridir. Toplam, ortalama, sayım veya gruplama için ham verinin tamamını Node.js'e taşımak gerekmez. Veritabanı yalnızca özet sonucu döndürebilir. Bu yaklaşım özellikle analytics ve raporlama endpoint'lerinde önemli kapasite kazancı sağlar. Sorgunun üretim indeksleriyle nasıl çalıştığı açıklama planıyla incelenmelidir.

SUM

SUM gibi toplama işlemlerini veritabanında yapmak çoğu durumda doğal seçimdir. Milyonlarca sayıyı uygulamaya taşımak gereksiz I/O maliyeti üretir. Veritabanı aggregate fonksiyonunu kendi yürütme motorunda gerçekleştirebilir. Sonuç birkaç byte olabilirken ham veri megabaytlarca olabilir. Bu fark hem latency hem memory açısından önemlidir.

GROUP BY

GROUP BY büyük veri setlerini kategori veya anahtar bazında özetlemek için kullanılabilir. Aynı işi JavaScript map ve reduce ile yapmak için önce tüm kayıtların uygulamaya ulaşması gerekir. Bu durum yüksek hacimde pahalıdır. Doğru indeks ve sorgu modeliyle database tarafı çoğu zaman daha iyi sonuç verir. Çok ağır analytics işleri ayrı veri platformuna taşınabilecek kadar büyüyebilir.

Pagination

Pagination uygulamaya bir kerede taşınan kayıt sayısını sınırlar. Bu hem response boyutunu hem memory tüketimini azaltır. Büyük offset değerlerinde keyset veya cursor tabanlı pagination daha verimli olabilir. Pagination tek başına tutarlı sıralama olmadan güvenli değildir. API sözleşmesi cursor yaşam süresini ve sıralama kriterini açıkça tanımlamalıdır.

Gereksiz Network Transferini Azaltmak

Network üzerinden taşınan her byte serialization, bandwidth ve memory maliyeti oluşturur. Sorgu yalnızca gerekli kolonları ve satırları döndürmelidir. SELECT * alışkanlığı büyük tablolarda gereksiz payload yaratabilir. Response compression bazı durumlarda yardımcı olsa da gereksiz veriyi üretmemek daha iyi çözümdür. Uçtan uca performans optimizasyonu veri hareketini azaltmayı da kapsar.

Node.js Streams Nedir?

Node.js Streams veriyi tamamen hazır olmasını beklemeden parça parça okuyup yazmayı sağlayan soyutlamalardır. Büyük dosya, HTTP body ve dönüşüm pipeline'larında memory kullanımını kontrol altında tutarlar. Readable, Writable, Duplex ve Transform temel stream türlerini oluşturur. Stream mimarisinin en önemli avantajlarından biri backpressure desteğidir. Büyük veriyi tek Buffer içinde toplamak yerine akış halinde işlemek production sistemlerinde çok daha güvenli olabilir.

Readable

Readable stream veri üreten kaynağı temsil eder. Dosya okuma akışı, HTTP request body veya özel veri üreticisi buna örnek olabilir. Tüketici veriyi chunk'lar halinde alır. Kaynak tüketiciden daha hızlıysa internal buffer belirli sınıra kadar devreye girer. Akışın tüketim biçimi backpressure davranışını etkiler.

Writable

Writable stream verinin yazıldığı hedefi temsil eder. Dosya yazma, HTTP response veya özel sink buna örnek olabilir. write çağrısı buffer kapasitesi aşıldığında false döndürebilir. Bu sinyal üreticinin yavaşlaması gerektiğini bildirir. drain event'i gelmeden yazmaya devam etmek memory kullanımını yükseltebilir.

Duplex

Duplex stream hem okunabilir hem yazılabilir bir arayüz sunar. Network socket bu yapıya doğal bir örnektir. Okuma ve yazma taraflarının buffer davranışları birbirinden bağımsız olabilir. Bu nedenle highWaterMark ayarları her yön için farklı değerlendirilebilir. Duplex model iki yönlü protokol tasarımlarında faydalıdır.

Transform

Transform stream gelen veriyi okuyup dönüştürerek yeni veri üreten özel bir Duplex türüdür. Compression, CSV parsing veya veri temizleme işlemleri buna örnektir. Pipeline içinde küçük ve bağımsız dönüşüm aşamaları oluşturmak kodun bakımını kolaylaştırır. Her transform backpressure zincirine dahil olabilir. CPU ağırlıklı transform adımlarında ana thread etkisi ayrıca ölçülmelidir.

Streaming ile Buffering Arasındaki Fark

Buffering tüm veya büyük bölümdeki verinin bellekte biriktirilmesini ifade eder. Streaming ise veriyi küçük parçalar halinde ilerletir. 5 GB dosyayı Buffer olarak okumaya çalışmak memory açısından riskliyken stream çok daha düşük sabit bellek profili sağlayabilir. Streaming ayrıca ilk chunk'ın işlenmesini tüm dosyanın gelmesini beklemeden başlatır. Bu nedenle büyük veri akışlarında varsayılan olarak stream düşünülmelidir.

Büyük Veriler Neden Stream ile İşlenmelidir?

Büyük veri işleme sırasında ana risklerden biri verinin tamamını RAM'e almaktır. Stream yaklaşımı işlenen veri miktarını küçük buffer'lar halinde sınırlandırarak memory baskısını azaltır. Ayrıca dönüşüm ve yazma işlemleri veri gelirken başlayabilir. Bu özellik time-to-first-byte ve toplam pipeline verimliliğini iyileştirebilir. Özellikle multi-GB dosya işleme sistemlerinde stream kullanımı kapasite planlamasını daha öngörülebilir hale getirir.

Tüm Veriyi RAM'e Almamak

Bir dosyanın tamamını readFile ile okumak dosya boyutu kadar veya daha fazla memory kullanabilir. Birden fazla istek bunu eş zamanlı yaptığında container limiti hızla aşılabilir. Stream yalnızca sınırlı miktarda chunk'ı bellekte tutar. Bu fark yüksek concurrency altında çok daha önemlidir. Memory tasarımında dosya boyutunun maksimum değeri mutlaka bilinmelidir.

Daha Düşük Memory Kullanımı

Streaming bellek kullanımını toplam veri büyüklüğünden büyük ölçüde bağımsız hale getirebilir. Aktif buffer büyüklüğü highWaterMark ve pipeline davranışıyla kontrol edilir. Bu durum garbage collector üzerindeki baskıyı da azaltabilir. Daha düşük memory tüketimi aynı instance üzerinde daha fazla concurrent işi güvenle yürütmeye yardımcı olur. Yine de transform aşamalarının kendi içlerinde veri biriktirmediği kontrol edilmelidir.

Time-to-First-Byte

Streaming tüm sonucu üretmeden önce ilk verinin istemciye gönderilmesine izin verebilir. Bu özellik büyük export veya proxy senaryolarında algılanan performansı iyileştirir. Kullanıcı sonucu daha erken almaya başlar. Ancak stream sırasında hata oluşması durumunda response kısmen gönderilmiş olabilir. API protokolü bu hata semantiğini dikkate almalıdır.

Incremental Processing

Incremental processing veriyi geldikçe doğrulamak, dönüştürmek ve yazmak anlamına gelir. Böylece tüm veri setinin hazır olması beklenmez. ETL pipeline'larında bu yaklaşım işlem süresini ve memory kullanımını azaltabilir. Her aşama backpressure sinyaline uymalıdır. Batch database write ile stream birlikte kullanıldığında iyi bir throughput dengesi sağlanabilir.

Multi-GB Dosyalar

Multi-GB dosyaları tek Buffer içine almak çoğu Node.js servisi için uygun değildir. Stream dosyayı küçük parçalar halinde okuyarak teorik olarak çok daha büyük veri setlerinin işlenmesini sağlar. Parsing kütüphanesinin de gerçek streaming desteği sunması gerekir. Bir parser içeride tüm dosyayı biriktiriyorsa dışarıda stream kullanmak yeterli olmaz. Uçtan uca memory profili incelenmelidir.

Backpressure Nedir?

Backpressure, veri üreticisi tüketiciden daha hızlı olduğunda üretim hızını kontrol etmeye yarayan akış mekanizmasıdır. Stream sistemlerinin sağlıklı çalışması için producer ve consumer hızlarının uyumlu olması gerekir. Tüketici yavaşladığında üretici sınırsız veri göndermeye devam ederse buffer büyür. Bunun sonucu memory baskısı, garbage collection artışı ve hatta process crash olabilir. Backpressure bu yüzden yalnızca stream ayrıntısı değil, yüksek hacimli sistem tasarımının temel kavramıdır.

Hızlı Producer

Hızlı producer veriyi tüketicinin işleyebildiğinden daha yüksek hızda üretir. Dosya okuma hızı database write hızından yüksek olabilir. İlk anda bu durum performans avantajı gibi görünse de aradaki fark buffer'da birikir. Buffer sınırları yoksa memory sürekli yükselir. Producer'ın tüketici sinyallerine göre yavaşlaması gerekir.

Yavaş Consumer

Yavaş consumer downstream database, ağ bağlantısı veya ağır transform olabilir. Consumer kapasitesi pipeline'ın gerçek throughput sınırını belirler. Producer'ı hızlandırmak bu sınırı ortadan kaldırmaz. Aksine kontrolsüz veri birikimi oluşturabilir. Sistem en yavaş aşamanın kapasitesine göre akışı düzenlemelidir.

Internal Buffer

Node.js stream'leri producer ve consumer arasındaki kısa hız farklarını internal buffer ile dengeler. Buffer boyutu highWaterMark gibi ayarlarla ilişkilidir. Bu buffer performans için faydalıdır fakat sınırsız değildir. Sınır aşıldığında backpressure sinyali devreye girmelidir. Uygulama bu sinyali görmezden gelmemelidir.

Kontrolsüz Buffer Growth

Producer tüketici kapasitesini sürekli aşarsa buffer büyümesi sistemin memory profilini bozar. Node.js stream API'sindeki backpressure mekanizmalarına uymamak bu sorunu kolayca yaratabilir. Özellikle writable.write false döndüğü halde yazmaya devam etmek risklidir. Queue sistemlerinde de benzer davranış queue depth büyümesi olarak görülür. Akış kontrolü farklı katmanlarda aynı temel probleme cevap verir.

Memory Pressure

Memory pressure heap kullanımının yükselmesi ve garbage collector'ın daha fazla çalışmasıyla kendini gösterebilir. Latency dalgalanmaları çoğu zaman yalnızca CPU kullanımına bakıldığında açıklanamaz. Stream buffer'ları, aktif Promise'ler ve büyük batch'ler birlikte memory tüketebilir. Backpressure aktif veri miktarını sınırlandırarak bu baskıyı azaltır. Container OOM olayları üretimde ciddi job kaybına yol açabileceği için bellek metriği yakından izlenmelidir.

highWaterMark Nasıl Çalışır?

highWaterMark stream buffer'ının veri akışını ne zaman yavaşlatması gerektiğine ilişkin önemli bir eşik değeridir. Kesin bir sert memory limiti olarak düşünülmemelidir. Readable ve Writable stream tarafında anlamı kullanım moduna göre değişir. Byte mode ile object mode davranışları aynı birimi kullanmaz. Değer yükseltildiğinde throughput iyileşebileceği gibi memory tüketimi de artabilir, bu nedenle benchmark gerekir.

Writable Buffer

Writable stream yazma hızına yetişemediğinde verileri internal buffer içinde bekletebilir. write çağrısının false dönmesi buffer'ın belirli eşiğe ulaştığını gösterir. Uygulama bu durumda yeni yazmayı durdurmalıdır. drain event'i geldiğinde yazma yeniden başlatılabilir. Bu mekanizma tüketicinin gerçek hızını üreticiye iletir.

Readable Buffer

Readable stream tüketici okumadan önce belirli miktarda veriyi buffer içinde tutabilir. highWaterMark bu davranışın önemli parametrelerinden biridir. Çok yüksek değer gereksiz memory tüketimi oluşturabilir. Çok düşük değer bazı I/O senaryolarında throughput'u sınırlandırabilir. Varsayılanları değiştirmeden önce ölçüm yapmak daha güvenlidir.

Byte Mode

Byte mode stream'lerde highWaterMark byte tabanlı veri miktarıyla ilişkilidir. Binary dosya veya Buffer akışlarında bu mod yaygındır. Chunk boyutları tam olarak highWaterMark ile aynı olmak zorunda değildir. Değer performans davranışını yönlendiren eşik olarak düşünülmelidir. Disk, ağ ve transform maliyeti birlikte benchmark edilmelidir.

Object Mode

Object mode stream'lerde buffer büyüklüğü byte yerine nesne sayısıyla değerlendirilir. Bu ayrım çok önemlidir çünkü tek bir nesne birkaç byte da birkaç megabayt da olabilir. highWaterMark 16 olduğunda teorik olarak 16 büyük nesne ciddi memory kullanabilir. Object boyutu uygulama tasarımının parçası olmalıdır. Büyük payload taşıyan object mode pipeline'ları özellikle izlenmelidir.

Throughput vs Memory

Daha büyük buffer bazı workloads altında disk veya network verimliliğini yükseltebilir. Bunun bedeli daha fazla memory kullanımı ve potansiyel olarak daha uzun kuyruk süresidir. Küçük buffer memory profilini iyileştirirken fazla scheduling maliyeti oluşturabilir. En uygun değer kullanılan depolama ve transform adımlarına göre değişir. Tek bir highWaterMark değerini tüm stream türlerine uygulamak doğru değildir.

Değeri Benchmark ile Ayarlamak

highWaterMark değerini tahmine göre yükseltmek yerine kontrollü benchmark yapmak gerekir. Test boyunca throughput, memory peak, event loop delay ve toplam işlem süresi izlenmelidir. Farklı dosya boyutları da test edilmelidir. Bir ayarın küçük örnekte iyi görünmesi multi-GB veride aynı sonucu vermeyebilir. Üretim instance limitleri test ortamında mümkün olduğunca temsil edilmelidir.

.write() False Döndüğünde Ne Yapılmalıdır?

Writable stream üzerinde write çağrısı false döndürüyorsa üretici geçici olarak yazmayı durdurmalıdır. Bu sinyal internal buffer'ın mevcut tüketici hızına göre dolduğunu gösterir. drain event'i alındığında yazma güvenli biçimde devam ettirilebilir. False sonucunu yok sayarak sürekli write çağırmak backpressure mekanizmasını fiilen devre dışı bırakır. Büyük veri sistemlerinde bu davranış ciddi memory artışına neden olabilir.

Yazmayı Durdurmak

write false döndüğünde yeni chunk üretimini veya yazımını bekletmek gerekir. Producer veri kaynağı kontrol edilebiliyorsa pause edilebilir. Manuel döngüde ise drain Promise'i beklenebilir. Amaç buffer'ın tüketici tarafından boşaltılmasına fırsat tanımaktır. Bu davranış throughput'u düşürmekten çok sürdürülebilir hale getirir.

'drain' Event

drain event writable buffer yeterince boşaldığında yazmanın devam edebileceğini bildirir. Manuel stream kodunda bu event backpressure yönetiminin temel parçalarından biridir. Listener yönetimi dikkatli yapılmalı ve hata durumları unutulmamalıdır. pipeline API'si birçok akış senaryosunda bu ayrıntıları daha güvenli yönetir. Event listener sızıntıları uzun süre çalışan proseslerde ayrıca risk oluşturabilir.

Backpressure'a Uymak

Backpressure sinyaline uymak producer hızını tüketicinin gerçek kapasitesine bağlar. Bu davranış sistemin aktif veri miktarını sınırlı tutar. Queue ve stream mimarilerinde benzer ilke geçerlidir. Tüketici yavaşladığında üretici ya yavaşlamalı ya da kontrollü şekilde yük reddetmelidir. Sınırsız buffer sürdürülebilir kapasite stratejisi değildir.

Backpressure'ı Yok Saymanın Memory Etkisi

Backpressure yok sayıldığında veri tüketiciden hızlı biçimde bellekte birikir. Heap büyüdükçe garbage collection daha pahalı hale gelir. Latency dalgalanabilir ve process memory limitine ulaşabilir. Container ortamında bu durum OOM kill ile sonuçlanabilir. Sorun yalnızca memory limitini yükselterek çözülmemelidir.

stream.pipeline() Neden Kullanılmalı?

stream.pipeline birden fazla stream aşamasını güvenli biçimde bağlamak için tercih edilen yüksek seviyeli API'lerden biridir. Backpressure zincir boyunca doğal biçimde korunur ve hata yayılımı daha kolay yönetilir. Bir aşama başarısız olduğunda ilgili kaynakların temizlenmesine yardımcı olur. Promise tabanlı sürümü async/await akışına rahatça entegre edilebilir. Manuel .pipe zincirine kıyasla error handling açısından daha sağlam bir temel sunar.

Backpressure

pipeline bağlı stream'ler arasındaki backpressure mekanizmasının çalışmasını sağlar. Hızlı source, yavaş destination karşısında sınırsız veri üretmeye devam etmez. Bu özellik özellikle büyük dosya dönüşümlerinde memory güvenliği için kritiktir. Her özel Transform implementasyonunun da kurallara uygun davranması gerekir. İçeride tüm veriyi biriktiren transform pipeline avantajını azaltabilir.

Error Propagation

Bir stream zincirinde herhangi bir aşamada hata oluşabilir. Manuel .pipe kullanımlarında her stream için error listener yönetimi kolayca eksik kalabilir. pipeline hatayı merkezi biçimde yakalamayı kolaylaştırır. Promise sürümünde try/catch ile bütün akış ele alınabilir. Kullanıcıya gönderilecek hata ile iç teknik hata ayrıştırılmalıdır.

Resource Cleanup

Stream başarısız olduğunda file descriptor, socket veya diğer kaynakların açık kalmaması gerekir. pipeline ilgili stream'lerin kapatılmasına yardımcı olur. Bu davranış uzun süre çalışan backend servislerinde önemlidir. Küçük kaynak sızıntıları zaman içinde büyük kapasite problemine dönüşebilir. Cleanup davranışı iptal senaryolarında da test edilmelidir.

Promise-Based Pipeline

node:stream/promises üzerinden Promise tabanlı pipeline kullanılabilir. Bu yapı await ile akışın tamamlanmasını beklemeyi kolaylaştırır. Hata durumunda Promise reject olduğu için try/catch kullanılabilir. Kod callback zincirine göre daha okunabilir hale gelir. Yine de stream lifecycle ve cancellation mantığını anlamak önemini korur.

Cancellation

Uzun stream işlemleri client bağlantısı kapandığında veya deadline dolduğunda iptal edilmelidir. Desteklenen API'lerde AbortSignal bu kontrolü merkezi hale getirebilir. İptal yalnızca Promise'i reject etmek değil, alttaki kaynakları da durdurmak anlamına gelmelidir. Açık dosya ve ağ bağlantıları temizlenmelidir. Cancellation üretim testlerinin bir parçası olmalıdır.

.pipe() ile Farkı

.pipe iki stream'i bağlamak için basit ve kullanışlıdır. Ancak birden fazla aşamalı zincirde hata ve cleanup yönetimi manuel olarak daha fazla dikkat gerektirir. pipeline bu sorunları daha bütünlüklü ele alır. Yeni üretim kodunda çok aşamalı akışlar için pipeline genellikle daha iyi varsayılandır. Küçük örneklerde .pipe yine anlaşılır ve geçerli olabilir.

Object Mode Stream Nedir?

Object mode, stream'in Buffer veya string yerine JavaScript nesneleri taşımasına izin verir. CSV parser'ın her satırı obje olarak üretmesi buna iyi bir örnektir. Bu model business transformation kodunu okunabilir hale getirebilir. Ancak buffer sınırı byte yerine nesne sayısıyla ilişkili olduğu için nesne boyutları önem kazanır. Büyük nesneler object mode stream'de beklenenden yüksek memory tüketebilir.

Buffer Yerine JavaScript Object

Object mode kullanıldığında her chunk bir JavaScript object olabilir. Bu davranış satır bazlı veya kayıt bazlı pipeline'ları sadeleştirir. Transform aşamaları alan adları üzerinden doğal biçimde çalışabilir. Nesnelerin boyutu büyükse serialization ve memory maliyeti artar. Her stream aşaması yalnızca ihtiyaç duyduğu alanları taşımayı düşünmelidir.

CSV Row Processing

CSV parser her satırı object mode stream içinde bir kayıt olarak üretebilir. Sonraki transform doğrulama ve normalization yapabilir. Ardından kayıtlar küçük batch'ler halinde database'e yazılabilir. Bu mimari tüm CSV dosyasını belleğe almadan ilerler. Hatalı satırlar ayrı reject dosyası veya DLQ benzeri kanala yönlendirilebilir.

Database Records

Database cursor sonuçları object mode akışına dönüştürülebilir. Her kayıt sırayla veya küçük batch'lerle işlenebilir. Bu yaklaşım milyonlarca sonucu tek array olarak almaktan daha güvenlidir. Connection yaşam süresinin stream tamamlanana kadar doğru yönetilmesi gerekir. Uzun transaction açmak gerekip gerekmediği ayrıca değerlendirilmelidir.

Object Mode highWaterMark

Object mode highWaterMark yaklaşık kaç nesnenin buffer içinde tutulacağını etkiler. Birim byte değildir. Bu yüzden nesnelerin ortalama ve maksimum boyutu bilinmeden yalnızca sayı üzerinden karar vermek yanıltıcıdır. Çok büyük nesnelerde daha düşük değer gerekebilir. Benchmark sırasında heap profili incelenmelidir.

Object Mode Memory Riskleri

Tek bir object içinde büyük Buffer veya uzun string bulunabilir. Böyle bir durumda birkaç düzine nesne bile önemli memory tüketebilir. Stream kullanılıyor olması otomatik olarak düşük bellek garantisi vermez. Transform aşamalarının nesneleri gereksiz yere kopyalaması da maliyeti artırır. Memory snapshot ve heap metrikleri gerçek davranışı göstermelidir.

Transform Stream ile Veri Pipeline'ı

Transform stream veri işleme sürecini küçük ve birleşebilir aşamalara bölmek için güçlü bir modeldir. Extract, parse, validate, transform, enrich ve write adımları ayrı sorumluluklar halinde tasarlanabilir. Her aşama yalnızca aldığı girdiyi işler ve bir sonraki aşamaya iletir. Bu yapı test edilebilirliği artırır ve backpressure zincirini korur. CPU ağırlıklı bir aşama ortaya çıktığında onu worker tabanlı tasarıma ayırmak daha kolay hale gelir.

Extract

Extract aşaması verinin dosya, HTTP kaynağı veya object storage gibi sistemden alınmasını temsil eder. Büyük veriler mümkünse stream olarak okunmalıdır. Kaynağın retry ve timeout davranışı açık biçimde tanımlanmalıdır. Authentication bilgileri loglara yazılmamalıdır. Kaynak kapanışında cleanup işlemleri garanti edilmelidir.

Parse

Parse aşaması ham byte veya text verisini anlamlı kayıtlara dönüştürür. CSV, NDJSON veya özel protokol parser'ları buna örnektir. Parser'ın streaming çalışıp çalışmadığı memory davranışını doğrudan etkiler. Hatalı kayıtların tüm pipeline'ı mı durduracağı yoksa ayrı mı tutulacağı belirlenmelidir. Giriş boyutu ve schema güvenliği kontrol edilmelidir.

Validate

Validate aşaması kayıtların beklenen schema ve business kurallarına uyup uymadığını kontrol eder. Hatalı verinin daha sonraki sistemlere taşınmasını önler. Validation sonucu satır numarası gibi bağlamla raporlanabilir. Çok pahalı validation işlemleri throughput'u sınırlayabilir. Bu nedenle gerekli kontrollerin sırası da performansı etkileyebilir.

Transform

Transform aşaması alanları normalize etmek, format değiştirmek veya yeni değer hesaplamak için kullanılır. Küçük senkron dönüşümler stream içinde rahatça yapılabilir. Ağır CPU dönüşümleri ana event loop'u bloke edebilir. Böyle durumda worker pool veya farklı servis sınırı düşünülmelidir. Transform fonksiyonları mümkün olduğunca yan etkisiz tutulursa test etmek kolaylaşır.

Enrich

Enrich aşaması kayda dış bir kaynaktan ek bilgi ekler. Veritabanı veya API çağrıları bu aşamada concurrency gerektirebilir. Her kayıt için sınırsız dış istek başlatmak stream backpressure'ını bozabilir. Async transform içinde bounded concurrency veya küçük batch lookup kullanılabilir. Cache de tekrar eden referans verilerinde yararlı olabilir.

Write

Write aşaması işlenmiş veriyi database, dosya veya başka servise gönderir. Çoğu pipeline'ın gerçek throughput sınırı bu aşamada ortaya çıkar. Bulk insert veya batch write round-trip maliyetini azaltabilir. Hata durumunda hangi kayıtların yazıldığı net biçimde izlenmelidir. Idempotent yazım tekrar işleme güvenliğini artırır.

Async Iterator Nedir?

Async iterator, bir veri kaynağından değerlerin zaman içinde asenkron olarak alınmasını sağlayan JavaScript protokolüdür. Her sonraki değer Promise üzerinden hazır olabilir. Bu model cursor, stream ve paginated API gibi lazy kaynaklarda oldukça doğaldır. for await...of sözdizimi tüketimi sadeleştirir. Büyük veri setlerinde tüm sonuçları array olarak oluşturmadan ilerleme imkanı verir.

Iterator

Iterator her çağrıda sıradaki değeri sağlayan next metoduna sahip bir yapıdır. Senkron iterator next çağrısına doğrudan sonuç döndürür. Sonuç genellikle value ve done alanlarını içerir. Array gibi birçok JavaScript yapısı iterable protokolünü kullanır. Async iterator aynı fikri Promise tabanlı veri kaynaklarına taşır.

Iterable

Iterable kendi iterator'ını sağlayabilen nesnedir. Symbol.iterator senkron iterable protokolünün merkezindedir. for...of bu protokolü kullanarak değerleri tüketebilir. Bu yapı veri üretim mekanizmasıyla tüketim kodunu birbirinden ayırır. Aynı fikir asenkron veri için Symbol.asyncIterator ile genişletilir.

AsyncIterator

AsyncIterator next çağrısının Promise döndürebildiği iterator türüdür. Böylece her sonraki değer ağ veya database gibi asenkron kaynaktan gelebilir. Tüketici tüm veri setinin hazır olmasını beklemez. Error ve cleanup davranışı iterator implementasyonunda ele alınmalıdır. Cursor tabanlı veri işleme bunun güçlü kullanım alanlarından biridir.

AsyncIterable

AsyncIterable Symbol.asyncIterator üzerinden AsyncIterator sağlar. Bu arayüz for await...of ile doğrudan kullanılabilir. Veri kaynağı sayfa sayfa veya chunk chunk sonuç üretebilir. Lazy yaklaşım memory kullanımını düşük tutar. İptal edildiğinde iterator'ın return veya cleanup davranışı kaynakları serbest bırakmalıdır.

for await...of

for await...of asenkron iterable içindeki değerleri sırayla tüketir. Her iteration gerekirse bir Promise'in tamamlanmasını bekler. Kod okunabilirliği callback tabanlı tüketimden genellikle daha yüksektir. Stream'ler de uygun durumlarda bu biçimde tüketilebilir. Döngü içinde ağır CPU işi yapılırsa event loop etkisi yine dikkate alınmalıdır.

Readable Stream'i Async Iterator Olarak Tüketmek

Node.js Readable stream'leri async iterator olarak tüketmek, event tabanlı kod yerine daha doğrusal bir kontrol akışı sunabilir. for await ile her chunk geldiğinde işleme yapılabilir. Tüketici bir sonraki chunk'a ancak mevcut iteration ilerlediğinde geçtiği için doğal akış kontrolü oluşur. Hata durumları try/catch ile yönetilebilir. Döngü erken sonlandırıldığında kaynak cleanup davranışı ayrıca doğrulanmalıdır.

for await (const chunk of stream)

Bu sözdizimi stream'den gelen her chunk'ı sırayla işler. Kod data event listener modeline göre daha kolay takip edilebilir. Her iteration içinde await kullanmak mümkündür. Ancak yavaş await işlemi stream throughput'unu doğrudan sınırlar. Bu sınır istenen backpressure davranışının bir parçası olabilir.

Lazy Consumption

Lazy consumption yalnızca ihtiyaç oldukça yeni veri alınmasını sağlar. Bu yaklaşım tüm dosya veya result set'in önceden yüklenmesini önler. Memory kullanımı böylece daha öngörülebilir kalır. Kullanıcı işlemi erken durdurduğunda kalan verinin okunması gerekmeyebilir. Büyük veri sistemlerinde bu özellik önemli kaynak tasarrufu sağlar.

Natural Backpressure

for await akışında consumer mevcut chunk'ı işlerken bir sonraki değer için ilerlemez. Bu davranış birçok senaryoda doğal backpressure sağlar. Özellikle her kayıt asenkron database işlemine tabi tutuluyorsa kontrol sadeleşir. Throughput gerektiğinde bounded concurrency katmanı eklenebilir. Sınırsız Promise oluşturmak doğal backpressure avantajını ortadan kaldırabilir.

Error Handling

Stream async iterator tüketimi try/catch ile hata yönetimini kolaylaştırır. Parser veya source tarafından fırlatılan hata döngüyü sonlandırabilir. Hatanın geçici mi kalıcı mı olduğu iş katmanında değerlendirilmelidir. Dosya veya network kaynağının kapatıldığından emin olunmalıdır. Hata logunda correlation ID ve veri konumu faydalı olabilir.

Cleanup

Döngü normal tamamlanmadığında stream kaynaklarının temizlenmesi gerekir. AbortSignal veya destroy mekanizmaları bu süreçte kullanılabilir. Açık file descriptor ve socket'ler serbest bırakılmalıdır. Cleanup kodu yalnızca başarılı senaryoda çalışacak biçimde tasarlanmamalıdır. finally blokları kaynak yaşam döngüsünü güvenli hale getirebilir.

Async Generator Nedir?

Async generator, asenkron biçimde birden fazla değer üretebilen fonksiyon modelidir. async function* sözdizimiyle tanımlanır ve yield üzerinden değer verir. Her değer üretilmeden önce await kullanılabilir. Bu özellik paginated API veya cursor tabanlı veri kaynaklarını soyutlamak için çok uygundur. Tüketici yalnızca for await ile değerleri alırken pagination ayrıntısını bilmek zorunda kalmaz.

async function*

async function* hem await hem yield kullanımına izin veren generator fonksiyon türüdür. Fonksiyon çağrıldığında doğrudan tüm veriyi üretmez. Bunun yerine AsyncIterable benzeri tüketilebilir bir nesne sağlar. Her next talebi yeni asenkron çalışma başlatabilir. Bu davranış lazy veri kaynakları için ideal olabilir.

yield

yield generator'ın bir değer üretip yürütmeyi geçici olarak durdurmasını sağlar. Tüketici bir sonraki değeri istediğinde fonksiyon kaldığı yerden devam eder. Async generator içinde yield öncesinde ağ isteği veya database sorgusu await edilebilir. Böylece veri ihtiyaç oldukça üretilir. Büyük listelerin tek seferde belleğe alınması önlenir.

Lazy Data Production

Lazy data production tüketici istemeden yeni veri üretmemeyi amaçlar. Paginated API'de ikinci sayfa yalnızca ilk sayfadaki veriler tüketildikten sonra istenebilir. Bu yaklaşım rate limit ve memory açısından faydalıdır. Tüketici erken durursa gereksiz sonraki sayfalar çağrılmaz. Kaynak kullanımını gerçek ihtiyaçla hizalar.

Promise + Iterator Modeli

Async generator Promise bekleme yeteneğini iterator protokolüyle birleştirir. Bu sayede zaman içinde gelen veri tek bir basit tüketim arayüzüne dönüşür. Kullanıcı her sayfanın Promise detaylarını yönetmek zorunda kalmaz. Hata generator içinden tüketiciye taşınabilir. Abstraction hem test hem yeniden kullanım açısından güçlüdür.

Composable Pipeline

Bir async generator başka async iterable'ı tüketip dönüştürülmüş değerler üretebilir. Böylece küçük generator fonksiyonları composable pipeline oluşturabilir. Filtreleme, enrichment ve pagination farklı katmanlarda tutulabilir. Her katmanın cancellation davranışı korunmalıdır. Çok ağır CPU dönüşümü varsa generator ana thread sorununu tek başına çözmez.

Async Generator ile Paginated API İşlemek

Paginated API'lerde tüm sayfaları tek fonksiyon içinde array'e toplamak bellek ve latency açısından gereksiz olabilir. Async generator her sayfayı gerektiği anda çağırıp kayıtları yield edebilir. Tüketici pagination token veya next URL ayrıntısını bilmez. Rate limit ve cancellation mantığı generator içinde merkezi hale getirilebilir. Bu model milyonlarca kaydı kademeli işleyen entegrasyonlarda oldukça kullanışlıdır.

İlk Sayfayı Getirmek

Generator başlangıçta API'nin ilk sayfasını çağırır. Response içindeki kayıtlar ve sonraki sayfa bilgisi parse edilir. Timeout ve AbortSignal ilk çağrıdan itibaren uygulanmalıdır. Authentication hatası gibi kalıcı hatalar retry edilmemelidir. İlk sayfa başarısızsa tüketiciye açık hata iletilmelidir.

yield

Her kayıt veya sayfa yield edilerek tüketiciye aktarılabilir. Kayıt bazlı yield tüketici kodunu sadeleştirir. Sayfa bazlı yield ise batch işlem için daha verimli olabilir. Hangi seviyenin seçileceği işleme maliyetine bağlıdır. Her durumda tüm sayfaları biriktirmek gerekmez.

Sonraki Sayfayı Talep Etmek

Mevcut sayfadaki değerler tüketildiğinde generator sonraki pagination token ile yeni istek yapabilir. Böylece veri talebi gerçek tüketim hızına bağlanır. Tüketici yavaşsa gereksiz sayfalar önceden çağrılmaz. Bu davranış harici API yükünü azaltır. Prefetch gerekirse yalnızca sınırlı sayıda sayfa önceden alınmalıdır.

Tüm Sayfaları RAM'e Almamak

Yüz binlerce kaydı array içinde tutmak heap kullanımını ciddi biçimde artırabilir. Async generator değerleri kademeli verdiği için aktif veri miktarı sınırlı tutulabilir. Consumer da sonuçları anında yazıyorsa memory kullanımı sabit seviyeye yaklaşabilir. Bu avantaj yalnızca ara katmanların veri biriktirmemesi halinde korunur. Testte heap profili gözlemlenmelidir.

Rate Limit

Paginated API'ler genellikle rate limit uygular. Generator her sayfa arasında gerekli bekleme veya limiter politikasını uygulayabilir. 429 yanıtında Retry-After benzeri bilgi varsa kullanılmalıdır. Sınırsız prefetch rate limit avantajını bozar. Kullanıcı iptal ettiğinde bekleyen timer ve request'ler de durdurulmalıdır.

Cancellation

Tüketici artık veriye ihtiyaç duymuyorsa generator sonraki sayfaları çağırmamalıdır. AbortController dış request'lerin iptal edilmesine yardımcı olur. Generator return veya finally bloğunda kaynak temizliği yapılabilir. Cancellation normal bir hata dışı kontrol akışı olarak tasarlanmalıdır. Bu yaklaşım kullanıcı bağlantısı koptuğunda gereksiz API maliyetini önler.

Database Cursor + Async Iterator

Database cursor büyük sorgu sonuçlarının parça parça okunmasını sağlar. Async iterator ile birleştirildiğinde milyonlarca kaydı tek array halinde belleğe almadan işlemek mümkündür. Cursor belirli batch'lerle veri getirirken tüketici for await üzerinden kayıtları alabilir. Connection ve transaction yaşam süresi bu modelde özellikle önemlidir. Uzun süren cursor işlemleri veritabanı kaynaklarını uzun süre tuttuğu için kapasite planlamasına dahil edilmelidir.

Büyük Query Result

Çok büyük query sonucu tek seferde alındığında network ve memory kullanımı yükselir. Cursor sonucu küçük parçalar halinde taşır. Uygulama her parçayı işleyip ilerleyebilir. Bu davranış export ve migration işlerinde yararlıdır. Kullanıcı endpoint'lerinde çok uzun cursor işlemleri yerine background job değerlendirmek gerekebilir.

Cursor

Cursor veritabanında sonuç seti üzerinde kademeli ilerlemeyi sağlayan mekanizmadır. Uygulama tüm satırları aynı anda almak zorunda kalmaz. Cursor'ın açık kaldığı süre boyunca bağlantı kaynağı kullanılabilir. Bu nedenle cleanup kritik öneme sahiptir. Hata veya cancellation durumunda cursor mutlaka kapatılmalıdır.

Batch Fetch

Cursor genellikle sonuçları belirli batch büyüklüğünde getirir. Batch çok küçükse fazla round-trip oluşabilir. Çok büyükse memory tüketimi artabilir. En iyi değer satır boyutu ve network latency ile ilişkilidir. Gerçek dataset üzerinde benchmark yapılmalıdır.

for await

for await cursor tüketimini okunabilir hale getirir. Her kayıt veya batch geldiğinde uygulama işleme devam eder. Consumer yavaşsa veri çekimi de doğal biçimde yavaşlayabilir. İşleme içinde sınırsız Promise başlatılmamalıdır. Gerekirse bounded concurrency ayrı katmanda uygulanmalıdır.

Connection Cleanup

Cursor kullanan kod connection lifecycle konusunda disiplinli olmalıdır. Normal tamamlanma, hata ve cancellation senaryolarının tümünde kaynak serbest bırakılmalıdır. finally blokları veya kütüphanenin resmi cleanup API'si kullanılabilir. Connection sızıntısı pool saturation'a dönüşebilir. Bu hata yüksek trafikte tüm uygulamayı etkileyebilir.

Transaction Lifetime

Cursor uzun süre transaction açık tutuyorsa lock ve version retention gibi veritabanı etkileri oluşabilir. Dakikalar süren processing sırasında transaction kapsamı dikkatle seçilmelidir. Her record için uzun business logic transaction içinde tutulmamalıdır. Bazı senaryolarda snapshot veya ayrı batch transaction daha uygun olabilir. Veritabanının izolasyon modeli dikkate alınmalıdır.

Node Streams ve Web Streams Arasındaki Fark

Node.js kendi Readable ve Writable stream API'lerine uzun süredir sahiptir. Web platformunda ise WHATWG ReadableStream ve WritableStream standartları kullanılır. Modern Node.js sürümleri Fetch API ve dönüşüm yardımcıları sayesinde iki ekosistem arasında daha kolay köprü kurabilir. Cross-runtime kütüphanelerde hangi stream tipinin public API'de kullanıldığı önem kazanır. Adaptasyon maliyeti ve backpressure davranışı test edilmelidir.

Node Readable/Writable

Node Readable ve Writable stream'leri Node.js ekosistemindeki dosya, socket ve process API'leriyle güçlü entegrasyona sahiptir. Event emitter davranışı ve pipeline gibi yardımcılar yaygın olarak kullanılır. Uzun yıllardır production sistemlerinde olgunlaşmış bir modeldir. Async iterator desteği modern kullanımını daha kolay hale getirmiştir. Node içi altyapıda çoğu zaman doğal seçimdir.

WHATWG ReadableStream

WHATWG ReadableStream web platformu standardına dayalı stream modelidir. Browser, Fetch ve farklı JavaScript runtime'larında ortak API sunmayı hedefler. Reader ve controller kavramları Node stream'lerinden farklıdır. Cross-runtime kod yazan ekiplerin bu ayrımı bilmesi gerekir. Dönüşüm adapter'ları iki model arasında geçiş sağlayabilir.

Fetch API

Fetch API response body için Web Streams tabanlı davranış sunabilir. Büyük HTTP response'larını tek seferde text veya arrayBuffer olarak almak yerine stream tüketmek mümkündür. Bu yaklaşım proxy ve veri aktarım işlerinde memory kullanımını azaltır. Timeout ve cancellation AbortSignal ile yönetilebilir. Response içeriğinin streaming parse edilebilir formatta olması ayrıca önemlidir.

Readable.fromWeb()

Readable.fromWeb Web ReadableStream'i Node.js Readable stream'e dönüştürmek için kullanılabilir. Böylece Fetch response body Node pipeline araçlarıyla entegre edilebilir. Dönüşüm yapılan Node sürümünün desteklediği API davranışı kontrol edilmelidir. TypeScript tipleri runtime sürümüyle uyumlu olmalıdır. Büyük veri pipeline'ında entegrasyon testi yapılması yararlıdır.

Readable.toWeb()

Readable.toWeb Node Readable stream'i Web Stream arayüzüne dönüştürmeyi sağlar. Web standardı bekleyen API veya framework katmanlarıyla entegrasyonda faydalıdır. Adapter eklemek veri akışını otomatik olarak hızlandırmaz. Backpressure davranışının uçtan uca korunduğu doğrulanmalıdır. Hata ve cancellation semantiği de test edilmelidir.

Cross-Runtime Kod

Node.js, browser ve edge benzeri farklı runtime'larda çalışan paketler Web Streams arayüzünü tercih edebilir. Yalnızca Node üzerinde çalışan servislerde Node stream API'si daha doğrudan olabilir. Public library API tasarımında runtime hedefleri baştan belirlenmelidir. Gereksiz adapter katmanları debugging yükünü artırabilir. Ortak standart faydalı olsa da kullanım bağlamı önemlidir.

Async Veri Pipeline Nasıl İptal Edilir?

Uzun süren asenkron pipeline'ların iptal edilebilir olması kaynak kullanımını ve kullanıcı deneyimini iyileştirir. Kullanıcı bağlantısı kapandığında, deadline dolduğunda veya üst işlem iptal edildiğinde alt görevlerin devam etmesi çoğu zaman gereksizdir. AbortController ve AbortSignal modern Node.js API'lerinde bu ihtiyacı standart biçimde ifade eder. Cancellation yalnızca dış Promise'i reddetmek değil, gerçek I/O veya stream kaynağını da durdurmak olmalıdır. Cleanup kodu iptal yolunda da çalışmalıdır.

AbortController

AbortController iptal sinyali üretmek için kullanılan standart API'dir. Controller üzerinden abort çağrıldığında bağlı AbortSignal iptal durumuna geçer. Birden fazla alt operasyona aynı signal verilerek ortak iptal zinciri kurulabilir. Kullanıcı bağlantısı kapandığında controller tetiklenebilir. Abort nedeni log ve metriklerde uygun biçimde sınıflandırılmalıdır.

AbortSignal

AbortSignal bir operasyonun iptal edilip edilmediğini bildirir. Fetch ve bazı Node.js API'leri signal parametresini destekler. Fonksiyonlar kendi uzun döngülerinde de signal.aborted durumunu kontrol edebilir. Signal'i alt servis katmanlarına geçirmek cancellation propagation sağlar. API sözleşmelerinde opsiyonel signal desteği yeniden kullanılabilirlik açısından yararlıdır.

Timeout

Timeout belirli süre dolduğunda operasyonun başarısız sayılmasını sağlar. En iyi durumda timeout aynı zamanda gerçek işi AbortSignal üzerinden iptal eder. Yalnızca Promise.race ile dış sonucu kapatmak alttaki işi devam ettirebilir. Bu da socket ve downstream kapasitesinin gereksiz tüketilmesine neden olur. Timeout süresi üst request deadline'dan uzun olmamalıdır.

Client Disconnect

Kullanıcı HTTP bağlantısını kapattığında arka taraftaki pahalı işlemin devam etmesi her zaman gerekli değildir. Request veya response lifecycle sinyali AbortController'a bağlanabilir. Böylece database veya harici API çağrıları destekliyorsa iptal edilir. Side effect başlamışsa idempotency ve transaction davranışı ayrıca düşünülmelidir. Cancellation her işlem için otomatik rollback anlamına gelmez.

Pipeline Cancellation

Stream pipeline'ları AbortSignal ile durdurulabilir. İptal sırasında source, transform ve destination kaynaklarının temizlenmesi gerekir. Kısmen yazılmış dosyanın ne yapılacağı iş kuralına bağlıdır. Temp dosya kullanıp başarılı tamamlanmada rename etmek güvenli bir desen olabilir. İptal davranışı yük testlerinden ayrı olarak test edilmelidir.

Cleanup

Cancellation sonrasında file handle, database connection, timer ve event listener gibi kaynaklar serbest bırakılmalıdır. finally blokları cleanup için güçlü bir araçtır. Aynı cleanup fonksiyonunun birden fazla kez çağrılması güvenli olmalıdır. Resource lifecycle ownership açık değilse sızıntılar kolayca oluşur. Kod review sırasında cancellation yolunu da başarılı yol kadar incelemek gerekir.

Timeout ile Cancellation Arasındaki Fark

Timeout ve cancellation birbirine yakın kavramlar olsa da aynı şeyi ifade etmez. Timeout süre sınırına ulaşıldığı için operasyonun durdurulması veya başarısız sayılmasıdır. Cancellation kullanıcı, parent request veya sistem kararıyla herhangi bir zamanda gerçekleşebilir. Her timeout bir cancellation tetikleyebilir fakat her cancellation timeout kaynaklı değildir. Bu fark hata sınıflandırması ve retry politikası açısından önemlidir.

Operation Deadline

Deadline bir operasyonun tamamlanması gereken mutlak son zamanı ifade eder. Dağıtık sistemlerde parent request'in deadline bilgisi alt servislere aktarılmalıdır. Aksi halde üst katman vazgeçtikten sonra alt servisler gereksiz çalışmaya devam eder. Deadline propagation kaynak tüketimini azaltır. Her alt servis kalan süreyi kendi timeout kararında kullanabilir.

Timeout

Timeout çoğunlukla belirli bir duration üzerinden tanımlanır. Örneğin harici API çağrısına üç saniye sınırı verilebilir. Süre dolduğunda operasyon iptal edilerek timeout hatası üretilir. Timeout'un retry edilebilir olup olmadığı servis davranışına göre değişir. Aynı timeout hatasını sınırsız tekrar etmek yükü artırabilir.

Kullanıcının İptali

Kullanıcı export işlemini iptal edebilir veya tarayıcı bağlantısını kapatabilir. Bu durumda operation deadline dolmamış olsa bile cancellation gerekebilir. Eğer işlem henüz yan etki yaratmadıysa hemen durdurmak kaynak tasarrufu sağlar. Yan etki başladıysa sistemin tutarlılık garantileri korunmalıdır. Kullanıcı iptalinin retry olarak değerlendirilmemesi gerekir.

Parent Request Cancellation

Bir üst servis isteği iptal ettiğinde alt servislerin çalışmaya devam etmesi gereksiz yük oluşturabilir. Trace ve request context içinde cancellation signal taşımak bu sorunu azaltır. Alt servis çağrıları aynı signal ile iptal edilebilir. Background job'a devredilmiş kalıcı işler ise parent request'ten bağımsız olabilir. İşin sahipliği net biçimde tanımlanmalıdır.

Deadline Propagation

Deadline propagation üst katmandaki zaman sınırının alt çağrılara taşınmasıdır. Her servis kendi tam timeout süresini yeniden başlatmamalıdır. Kalan süre hesaplanarak downstream request buna göre sınırlandırılabilir. Bu davranış zincirleme latency artışını azaltır. Trace context ve request metadata bu bilginin taşınmasında kullanılabilir.

CPU-Intensive İşlerde Worker Threads

Worker Threads CPU ağırlıklı JavaScript görevlerini ana event loop dışında çalıştırmak için kullanılan Node.js mekanizmasıdır. Her worker ayrı JavaScript yürütme thread'ine ve kendi event loop'una sahiptir. Bu sayede birden fazla CPU çekirdeğinden gerçek paralellik elde edilebilir. Ana thread HTTP isteklerine ve I/O callback'lerine yanıt vermeye devam eder. Worker kullanımı özellikle yüzlerce milisaniye süren hesaplamalarda değerlidir.

Worker Thread Nedir?

Worker Thread aynı Node.js process'i içinde ayrı JavaScript execution ortamı sağlayan thread'dir. Ana thread ile mesajlaşarak görev alabilir ve sonuç döndürebilir. Her worker'ın kendi V8 context'i bulunur. Veri aktarımı structured clone veya transferable object mekanizmalarıyla yapılabilir. Worker yaratma maliyeti nedeniyle sürekli yeniden oluşturmak yerine pool kullanmak daha uygundur.

Ayrı JavaScript Thread

Her worker JavaScript kodunu ana thread'den bağımsız yürütür. Bu sayede iki CPU-heavy fonksiyon farklı çekirdeklerde gerçekten aynı anda çalışabilir. Shared state varsayılan olarak doğrudan ortak değildir. Mesajlaşma modeli data race riskini azaltır. SharedArrayBuffer kullanıldığında ise explicit synchronization gerekir.

Ayrı Event Loop

Worker'ın kendi event loop'u vardır. Bu nedenle worker içindeki timer ve asenkron operasyonlar ana event loop'tan bağımsız ilerler. CPU işi worker event loop'unu meşgul etse bile ana HTTP thread'i doğrudan bloklanmaz. Buna rağmen çok fazla worker CPU'yu tamamen tüketirse tüm proses dolaylı olarak yavaşlayabilir. Worker sayısı kontrollü tutulmalıdır.

CPU Parallelism

Worker Threads Node.js içinde gerçek CPU parallelism sağlar. Dört çekirdekli bir makinede birkaç worker farklı görevleri eş zamanlı işleyebilir. Performans kazancı görevin büyüklüğüne ve veri aktarım maliyetine bağlıdır. Çok küçük işler worker scheduling overhead nedeniyle yavaşlayabilir. Benchmark olmadan worker eklemek doğru değildir.

Main Thread'i Responsive Tutmak

Worker kullanımının en önemli hedeflerinden biri ana event loop'u düşük latency ile çalışır halde tutmaktır. HTTP parsing, routing ve I/O callback'leri ana thread üzerinde ilerlemeye devam eder. Ağır hesaplama ayrı havuza gönderilir. Worker havuzu doygun olduğunda yeni görevler queue'da bekletilmelidir. Aksi halde memory baskısı başka bir katmana taşınmış olur.

Worker Threads Ne Zaman Kullanılmalı?

Worker Threads bekleme ağırlıklı her görev için değil, CPU üzerinde anlamlı süre harcayan JavaScript işlemleri için düşünülmelidir. Görüntü dönüşümü, ağır parsing ve matematiksel hesaplamalar uygun örneklerdir. İşin süresi worker dispatch maliyetinden yeterince büyük olmalıdır. Ayrıca native kütüphanenin zaten kendi thread mekanizmasını kullanıp kullanmadığı incelenmelidir. Amaç thread sayısını artırmak değil, event loop bloklanmasını azaltmaktır.

Image Processing

Saf JavaScript veya CPU tüketen image işlemleri Worker Thread üzerinde çalıştırılabilir. Görsel boyutu büyük olduğunda veri aktarım maliyeti de artar. ArrayBuffer transferi kopya maliyetini azaltabilir. Native image kütüphanesinin kendi thread davranışı varsa çift katmanlı paralellik dikkatle ölçülmelidir. Memory kullanımı worker başına takip edilmelidir.

PDF Generation

Yoğun PDF üretimi ana HTTP event loop'unu etkileyebilir. Görev kısa değilse worker veya background queue içinde worker pool kullanımı daha sağlıklıdır. Kullanıcıya uzun süre açık request tutmak yerine job sonucu bildirilebilir. Font ve template kaynaklarının worker içinde nasıl yüklendiği startup maliyetini etkiler. Worker reuse bu maliyeti azaltır.

Büyük JSON Parsing

Büyük JSON parse işlemi senkron olduğu için ana thread üzerinde bloklama yapabilir. Veri yüzlerce megabayta ulaşıyorsa önce veri formatını yeniden düşünmek daha iyi olabilir. Kaçınılmaz durumlarda parsing worker'a taşınabilir. Girdi worker'a gönderilirken kopyalama maliyeti ölçülmelidir. Stream edilebilir NDJSON gibi formatlar bazı işlerde daha verimli olabilir.

CPU-Heavy Transformation

Büyük veri üzerinde yoğun hashing, geometri veya kompleks dönüşüm yapan algoritmalar worker için uygundur. Görevler küçük parçalara ayrılıp worker pool'a dağıtılabilir. Parçaların çok küçük olması mesajlaşma overhead'ini artırır. Çok büyük olması ise yük dengesini bozabilir. Task granularity benchmark ile ayarlanmalıdır.

Cryptography

Kriptografik işlemlerin hangi API üzerinden yapıldığı önemlidir. Node.js'in bazı crypto operasyonları libuv thread pool kullanabilir. Saf JavaScript kriptografik hesaplama ise Worker Thread üzerinde değerlendirilebilir. Güvenlik açısından kendi kriptografik algoritmanızı yazmak yerine güvenilir standart API'ler kullanılmalıdır. Performance tuning güvenlik garantilerini bozmamalıdır.

Matematiksel Hesaplama

Simülasyon, istatistik veya büyük sayısal dönüşümler Worker Threads ile paralel hale getirilebilir. İş parçalarının birbirinden bağımsız olması ölçeklemeyi kolaylaştırır. Paylaşılan state gereksinimi arttıkça koordinasyon maliyeti yükselir. Sonuç birleştirme aşaması ayrıca CPU ve memory kullanabilir. Ölçek testleri tüm pipeline üzerinden yapılmalıdır.

Worker Threads Ne Zaman Kullanılmamalı?

Worker Threads güçlü olduğu kadar yanlış yerde kullanıldığında gereksiz maliyet ekleyen bir mekanizmadır. Normal HTTP isteği veya veritabanı sorgusu zaten I/O ağırlıklı olduğu için worker'a taşınması genellikle fayda sağlamaz. Her worker ek memory ve scheduling maliyeti oluşturur. Kısa işler için mesajlaşma overhead'i işin kendisinden büyük olabilir. Önce Node.js'in yerleşik asenkron API'leri değerlendirilmelidir.

HTTP Request

Outbound HTTP request bekleme ağırlıklı bir I/O işlemidir. Bunu Worker Thread'e taşımak network latency'yi azaltmaz. Ana event loop zaten çok sayıda ağ isteğini concurrent biçimde yönetebilir. Gerçek ihtiyaç concurrency limiting ve timeout olabilir. Worker eklemek yalnızca mimariyi gereksiz yere büyütür.

Database Query

Database sorgusu da çoğunlukla I/O-bound bir operasyondur. Sorguyu worker'da başlatmak database'in çalışma süresini kısaltmaz. Sorun yavaş query ise indeks, sorgu planı veya database kapasitesi incelenmelidir. Connection pool saturation için concurrency limiti daha doğru çözümdür. Worker yalnızca sorgu sonucundaki ağır CPU dönüşümünde anlamlı olabilir.

Normal File I/O

Node.js file system modülünün asenkron API'leri zaten bloklamayan uygulama akışı sunar. Normal dosya okuma için Worker Thread açmak gerekmez. Büyük dosyalarda stream kullanımı daha değerli olur. Çok yoğun filesystem operasyonlarında libuv thread pool davranışı incelenebilir. Problem doğru katmanda çözülmelidir.

Basit ve Çok Kısa İşlemler

Birkaç mikro veya milisaniye süren küçük hesaplamaları worker'a göndermek genellikle kazanç sağlamaz. Mesaj serialization ve scheduling maliyeti görevin kendisinden büyük olabilir. Ana thread kısa işleri rahatça çalıştırabilir. Gerçek ölçüm olmadan her CPU kullanımını worker'a taşımak yanlış olur. Threshold benchmark ile belirlenmelidir.

Built-In Async API Yeterliyken Worker Eklememek

Node.js veya kullanılan kütüphane zaten işi event-driven ya da thread pool destekli biçimde yürütüyorsa ek Worker Thread çoğu zaman gereksizdir. Mimari katman sayısı arttıkça hata ve observability yükü de büyür. Önce API'nin gerçek çalışma modelini incelemek gerekir. Sonra event loop ve CPU metrikleri ölçülmelidir. Yalnızca ölçülen sorun için worker eklenmelidir.

Her İş İçin Yeni Worker Oluşturmak Neden Yanlıştır?

Worker oluşturmak sıfır maliyetli değildir. Yeni V8 context'i, thread ve modül yükleme süreci CPU ile memory harcar. Yüksek trafik altında her request için worker açmak thread explosion yaratabilir. Daha iyi model sabit veya kontrollü sayıda worker'ı tekrar kullanan pool yapısıdır. Pool doygun olduğunda yeni işler queue'da beklemelidir.

Thread Startup Cost

Yeni worker oluşturulurken thread ve JavaScript execution ortamı hazırlanır. Modüllerin import edilmesi de başlangıç süresine eklenebilir. Kısa görevlerde bu startup maliyeti görevin süresini aşabilir. Worker reuse toplam overhead'i ciddi biçimde azaltır. Warm worker davranışı benchmark edilmelidir.

Memory Overhead

Her worker kendi V8 ortamı nedeniyle ek memory tüketir. Onlarca veya yüzlerce worker aynı process içinde memory limitini hızla aşabilir. Worker içinde yüklenen büyük modüller bu maliyeti artırır. Memory kullanımını thread sayısıyla birlikte ölçmek gerekir. Container limitleri havuz boyutuna dahil edilmelidir.

Thread Explosion

Her gelen iş için thread oluşturmak yoğun trafikte thread sayısının kontrolsüz büyümesine neden olabilir. İşletim sistemi scheduling maliyeti artar ve CPU cache verimliliği düşebilir. Sonuç daha fazla thread'e rağmen daha düşük throughput olabilir. Queue ile sabit pool kullanımı daha istikrarlıdır. Backpressure burada da önemlidir.

Worker Pool

Worker pool önceden oluşturulmuş worker'ları görevler arasında tekrar kullanır. Her worker boşaldığında sıradaki CPU görevi alır. Böylece startup maliyeti amorti edilir. Havuzun önünde bounded task queue bulunmalıdır. Queue büyüklüğü ve bekleme süresi metrik olarak izlenmelidir.

Worker Thread Pool Nasıl Çalışır?

Worker Thread pool belirli sayıda uzun ömürlü worker ile merkezi task queue arasında görev dağıtımı yapar. Ana thread CPU görevini havuza gönderir ve sonucu Promise benzeri bir arayüzle bekleyebilir. Boştaki worker görevi alır, hesaplamayı yürütür ve sonucu mesajla geri gönderir. Worker tekrar idle duruma geçerek yeni iş alır. Bu desen concurrency, memory ve CPU kullanımını sınırlandırır.

Sabit Worker Sayısı

Pool genellikle belirli sayıda worker ile başlatılır. Sayı CPU çekirdeği ve ana thread için bırakılacak kapasiteye göre seçilir. Her core için bir worker açmak otomatik doğru değildir. Main thread ve işletim sistemi de CPU'ya ihtiyaç duyar. Benchmark güvenli sınırı belirlemelidir.

Task Queue

Pool meşgulse gelen yeni CPU görevleri task queue içinde bekler. Bu queue sınırsız bırakılmamalıdır. Çok büyük backlog memory ve latency sorununa dönüşebilir. Queue limitine ulaşıldığında load shedding veya 429/503 benzeri kontrollü yanıt düşünülebilir. Background görevlerde ayrı durable queue kullanılabilir.

Idle Worker

Görevi olmayan worker idle durumda yeni iş bekler. Bir task geldiğinde dispatcher uygun worker'a görevi gönderir. Idle worker sayısı kapasite metriği olarak kullanılabilir. Sürekli sıfır idle worker pool saturation göstergesi olabilir. Bu durumda queue wait süresi de izlenmelidir.

Task Dispatch

Task dispatch görevin verisini ve kimliğini worker'a iletir. Kimlik sonuç ile doğru Promise'i eşleştirmek için kullanılabilir. Büyük veri transferlerinde structured clone maliyeti önemlidir. Transferable ArrayBuffer bazı durumlarda daha verimli olur. Görev timeout'u dispatcher seviyesinde izlenmelidir.

Worker Reuse

Worker reuse her görev için yeniden thread açma maliyetini ortadan kaldırır. Modüller bir kez yüklenir ve sonraki görevlerde tekrar kullanılabilir. Worker içinde sızıntı oluşmaması için state yönetimi kontrollü olmalıdır. Belirli görev sayısından sonra worker recycle etmek bazı uygulamalarda tercih edilebilir. Bu davranış ölçüm ve güvenilirlik ihtiyacına göre belirlenmelidir.

Pool Saturation

Tüm worker'lar uzun süre meşgulse pool saturation oluşur. Yeni görevler queue'da bekler ve toplam latency yükselir. Yalnızca worker sayısını artırmak her zaman çözüm değildir çünkü CPU zaten doygun olabilir. Horizontal scaling veya işin algoritmik optimizasyonu gerekebilir. Queue wait ve CPU utilization birlikte yorumlanmalıdır.

Worker Pool Boyutu Nasıl Seçilir?

Worker pool boyutu CPU çekirdeği kadar basit bir formülle belirlenmemelidir. Görev süresi, worker memory tüketimi, ana HTTP thread'inin ihtiyaçları ve deployment başına kaynak limitleri birlikte değerlendirilir. Çok küçük pool CPU kapasitesini kullanamazken çok büyük pool context switching ve memory baskısı oluşturabilir. Benchmark sırasında throughput ile p99 latency birlikte ölçülmelidir. En iyi değer güvenli headroom bırakan noktadır.

CPU Core Sayısı

Logical CPU sayısı başlangıç tahmini için yararlı bilgidir. Fakat container CPU limiti host makinedeki core sayısından farklı olabilir. Ana event loop'un da CPU zamanına ihtiyacı vardır. Bu yüzden tüm logical core'ları worker ile doldurmak her zaman iyi fikir değildir. Runtime'ın gerçekten gördüğü CPU kapasitesi dikkate alınmalıdır.

İşlem Süresi

Uzun görevler worker'ı daha uzun süre meşgul eder ve queue oluşturabilir. Çok kısa görevlerde ise dispatch overhead'i baskın hale gelir. Görev süresi dağılımı p50 kadar p95 ve p99 seviyesinde de ölçülmelidir. Büyük süre farklılıkları yük dengelemesini etkiler. Task chunking bazı senaryolarda fayda sağlayabilir.

Memory Kullanımı

Her worker'ın heap ve native memory kullanımı vardır. Worker sayısı artırılırken toplam process RSS değeri izlenmelidir. Büyük model veya veri seti worker başına kopyalanıyorsa memory maliyeti hızla büyüyebilir. Shared memory bazı özel durumlarda kopyayı azaltabilir fakat ek senkronizasyon riski getirir. Önce basit mesajlaşma modeli tercih edilmelidir.

HTTP Main Thread Headroom

Aynı process hem API hem CPU worker pool barındırıyorsa ana thread için CPU headroom bırakılmalıdır. Worker'lar tüm CPU kapasitesini tüketirse event loop doğrudan bloklanmasa bile scheduling nedeniyle latency artabilir. P99 API latency worker sayısı artırılırken gözlemlenmelidir. Bazı sistemlerde CPU worker'larını ayrı deployment'a ayırmak daha temiz olabilir. Böylece ölçekleme politikaları bağımsız yönetilir.

Benchmark

Worker pool boyutu gerçek CPU workload ile test edilmelidir. Bir, iki, dört ve daha fazla worker seviyesinde throughput ve latency karşılaştırılabilir. CPU steal, context switch ve memory değerleri de gözlenmelidir. Yük yalnızca tek task türünden oluşmamalıdır. Üretimde görülen dağılıma yakın veri seti kullanılmalıdır.

Daha Fazla Worker'ın Her Zaman Daha Hızlı Olmaması

CPU çekirdeği sayısından çok daha fazla worker çalıştırmak context switching maliyetini yükseltir. Cache verimliliği düşebilir ve toplam throughput gerileyebilir. Memory baskısı da garbage collection maliyetini artırabilir. Bu nedenle worker sayısı performans grafiğinde bir noktadan sonra negatif getiri oluşturur. Optimum nokta ancak benchmark ile görülür.

Worker Thread'lere Veri Nasıl Aktarılır?

Worker Threads ana thread ile doğrudan ortak JavaScript nesne heap'i kullanmaz. Veri workerData veya postMessage üzerinden aktarılabilir. Çoğu veri structured clone algoritmasıyla kopyalanır. Büyük binary payload'larda bu kopya maliyeti önemli olabilir. Transferable ArrayBuffer veya dikkatli SharedArrayBuffer kullanımı özel durumlarda performansı iyileştirebilir.

workerData

workerData worker oluşturulurken başlangıç verisini göndermek için kullanılır. Konfigürasyon veya ilk görev verisi buna örnek olabilir. Worker reuse modelinde her görev için yeni worker açılmadığı için dinamik işler genellikle postMessage üzerinden gönderilir. Büyük workerData startup maliyetini artırabilir. Sabit ayarlar için uygun bir mekanizmadır.

postMessage

postMessage ana thread ile worker arasında görev ve sonuç iletimi için temel yöntemdir. Mesajlara task ID eklemek concurrent sonuç eşleştirmesini kolaylaştırır. Veri structured clone ile aktarılabilir. Büyük nesnelerin sık kopyalanması CPU ve memory maliyeti üretir. Mesaj sözleşmesi TypeScript ile açık biçimde modellenebilir.

Structured Clone

Structured clone birçok JavaScript veri türünü iki context arasında güvenli biçimde kopyalar. Paylaşılan mutable object referansı oluşmadığı için concurrency yönetimi sadeleşir. Bunun bedeli özellikle büyük verilerde kopyalama maliyetidir. Payload büyüklüğü benchmark sırasında ölçülmelidir. Gereksiz alanlar worker mesajından çıkarılmalıdır.

MessageChannel

MessageChannel iki MessagePort oluşturarak ayrı iletişim kanalı sağlar. Karmaşık worker topolojilerinde veya doğrudan port aktarımında faydalı olabilir. Basit task pool için ana worker mesaj kanalı çoğu zaman yeterlidir. Ek kanallar lifecycle yönetimini zorlaştırabilir. Gereksinim olmadan iletişim katmanını büyütmemek gerekir.

MessagePort

MessagePort mesaj gönderme ve alma için kullanılan iletişim ucudur. Port başka worker'a transferable olarak aktarılabilir. Event listener ve port kapanışının doğru yönetilmesi gerekir. Açık kalan portlar proses yaşam döngüsünü etkileyebilir. Kullanım sonrasında close davranışı düşünülmelidir.

Transferable Object Kullanımı

Transferable object verinin kopyalanması yerine sahipliğinin başka execution context'e aktarılmasını sağlar. Büyük ArrayBuffer verilerinde structured clone maliyetini azaltabilir. Transfer sonrasında gönderici taraf ilgili buffer'ı normal biçimde kullanamaz. Bu nedenle ownership tasarımı açık olmalıdır. Yanlış kullanıldığında beklenmeyen veri erişim hataları oluşabilir.

ArrayBuffer

ArrayBuffer transferable olarak worker'a gönderilebilen temel binary veri türlerinden biridir. Büyük görüntü veya binary dataset'lerde kopya maliyetini azaltabilir. Transfer listesine eklenerek ownership worker'a geçirilebilir. Gönderici tarafta buffer detached hale gelir. Bu davranış kod sözleşmesinde net olmalıdır.

Copy Maliyetini Önlemek

Yüzlerce megabayt veriyi worker'a kopyalamak ciddi CPU ve memory bandwidth kullanabilir. Transferable yaklaşım bu kopyayı önleyebilir. Ancak tüm veri tipleri transferable değildir. Ayrıca verinin gönderen tarafta kullanılmaya devam etmesi gerekiyorsa ownership transferi uygun olmayabilir. Performans kazancı gerçek workload ile ölçülmelidir.

Ownership Transfer

Ownership transfer verinin kontrolünü bir execution context'ten diğerine geçirir. Bu yaklaşım shared mutable memory riskini azaltır. Gönderen kod transfer sonrasında veriyi kullanmamalıdır. API tasarımı bu yaşam döngüsünü açık göstermelidir. Çok sayıda el değiştiren buffer debugging açısından zorlaşabilir.

Büyük Binary Data

Büyük binary data worker kullanımında en önemli maliyet kaynaklarından biri olabilir. Görüntü, video frame veya binary parsing görevleri buna örnektir. Transferable ArrayBuffer kopyayı azaltarak worker faydasını artırabilir. Verinin storage'dan worker'a doğrudan daha verimli aktarılması da düşünülebilir. Gereksiz format dönüşümlerinden kaçınmak önemlidir.

SharedArrayBuffer Ne Zaman Kullanılmalı?

SharedArrayBuffer birden fazla thread'in aynı memory bölgesine erişmesine izin verir. Bu model kopya maliyetini azaltabilir fakat synchronization sorumluluğunu geliştiriciye bırakır. Race condition ve görünürlük problemlerini yönetmek için Atomics gerekebilir. Çoğu Worker Thread işi mesajlaşma modeliyle daha güvenli biçimde çözülebilir. Shared memory ancak ölçülmüş performans ihtiyacı varsa tercih edilmelidir.

Shared Memory

Shared memory aynı ArrayBuffer içeriğinin birden fazla thread tarafından görülmesini sağlar. Büyük ortak lookup table gibi özel durumlarda yararlı olabilir. Ancak mutable veri birden fazla thread tarafından değiştiriliyorsa yarış durumları oluşabilir. Ownership sınırı mesajlaşmaya göre daha belirsizdir. Tasarım çok iyi belgelenmelidir.

Atomics

Atomics SharedArrayBuffer üzerinde güvenli bazı eşzamanlama operasyonları sağlar. Counter artırma, compare-and-swap ve bekleme sinyalleri gibi işlemler yapılabilir. Bu API düşük seviyeli concurrency bilgisi gerektirir. Yanlış senkronizasyon deadlock veya performans problemi yaratabilir. Basit veri aktarımı için gerekli değildir.

Race Condition

Race condition birden fazla thread'in paylaşılan state üzerinde sıraya bağlı beklenmeyen sonuç üretmesidir. Sorun her çalıştırmada tekrar etmeyebilir ve test edilmesi zordur. Shared memory kullanan sistemlerde kritik bölümler açık biçimde belirlenmelidir. Immutable veri veya ownership modeli riski azaltır. Mümkün olduğunda mesajlaşma tercih edilmesi bakım yükünü düşürür.

Synchronization

Synchronization thread'lerin shared state üzerinde doğru sırayla çalışmasını sağlar. Atomics ve protokol düzeyi kurallar kullanılabilir. Fazla synchronization parallelism avantajını azaltabilir. Thread'ler birbirini çok bekliyorsa tasarım yeniden düşünülmelidir. En iyi paralel görevler mümkün olduğunca bağımsız olanlardır.

Shared Memory Karmaşıklığı

Shared memory veri kopyasını azaltırken concurrency yönetimini zorlaştırır. Hata ayıklama mesajlaşma modeline göre daha güç olabilir. Race condition yalnızca yüksek yük altında ortaya çıkabilir. Ekibin bu modeli sürdürebilecek deneyime sahip olması gerekir. Performans kazancı ölçülmeden shared memory tercih etmek genellikle gereksizdir.

Worker Threads vs Child Process

Worker Threads ve child process ana event loop'tan bağımsız çalışma sağlayabilir fakat izolasyon ve iletişim modelleri farklıdır. Worker aynı process içinde ayrı JavaScript thread'i çalıştırır. Child process ise işletim sistemi seviyesinde ayrı proses oluşturur. Harici executable, güçlü izolasyon veya farklı runtime gerektiğinde child process daha uygundur. CPU ağırlıklı JavaScript hesaplamasında Worker Threads daha düşük iletişim maliyeti sunabilir.

Aynı Process

Worker Threads aynı Node.js process'i içinde çalışır. Bu sayede MessagePort ve SharedArrayBuffer gibi daha yakın iletişim mekanizmaları kullanılabilir. Bir process crash'i tüm worker'ları etkileyebilir. Kaynak limitleri toplam process seviyesinde paylaşılır. İzolasyon ihtiyacı yüksekse bu özellik dezavantaja dönüşebilir.

Memory Sharing

Worker Threads gerektiğinde SharedArrayBuffer üzerinden memory paylaşabilir. Child process'lerde normal koşullarda aynı heap paylaşılmaz. IPC üzerinden veri taşınır. Shared memory performans avantajı sunsa da synchronization riski getirir. Uygulama gereksinimi olmadığı sürece basit mesajlaşma daha güvenlidir.

Process Isolation

Child process ayrı proses sınırı sayesinde daha güçlü izolasyon sağlar. Harici program çökse bile parent process uygun yönetimle çalışmaya devam edebilir. Worker hataları da yakalanabilir fakat aynı process kaynaklarını paylaşır. Güvenilmeyen kod veya native executable kullanımında process sınırı daha anlamlı olabilir. Güvenlik gereksinimi performans kadar önemlidir.

Startup Cost

Child process oluşturma maliyeti genel olarak worker thread'e göre daha yüksek olabilir. Yeni proses kendi memory alanını ve runtime başlangıcını gerektirir. Worker da ücretsiz değildir ve V8 context oluşturur. Kısa görevlerde her iki yöntemde de pool veya uzun ömürlü process tasarımı düşünülebilir. Gerçek startup süresi kullanılan modüllere bağlıdır.

External Executable

Harici binary çalıştırmak gerektiğinde child_process API'leri doğal seçimdir. FFmpeg veya sistem aracı buna örnek olabilir. stdout ve stderr boyutlarının sınırlandırılması gerekir. Timeout ve process kill davranışı açık olmalıdır. Shell üzerinden komut çalıştırırken kullanıcı girdisi güvenlik açısından doğrudan birleştirilmemelidir.

Untrusted Code

Güvenilmeyen kodu yalnızca Worker Thread içinde çalıştırmak güçlü güvenlik sandbox'ı sağlamaz. Aynı process içindeki güvenlik sınırları bu amaç için yeterli kabul edilmemelidir. Ayrı process, container veya özel sandbox teknolojileri gerekebilir. Resource limit ve syscall politikaları ayrıca düşünülmelidir. Güvenlik modelini yalnızca performans mekanizmasına bağlamamak gerekir.

Hangi Durumda Hangisi?

CPU-heavy JavaScript kodu için Worker Threads genellikle ilk seçeneklerden biridir. Harici executable veya daha güçlü process izolasyonu gerektiğinde child process daha uygundur. Uzun süreli business job için ise ikisinin önünde durable queue gerekebilir. Aynı görev queue worker'ı içinde Worker Thread kullanabilir. Katmanlar birbirinin alternatifi değil, farklı problemlerin çözümüdür.

Node.js libuv Thread Pool Nedir?

libuv thread pool bazı Node.js API'lerinin işletim sistemi üzerinde bloklayıcı olabilecek işlerini arka planda yürütmesine yardımcı olan yerel thread havuzudur. Bu mekanizma JavaScript Worker Threads ile aynı değildir. Dosya sistemi, bazı DNS, crypto ve compression işlemleri bu havuzu kullanabilir. Havuz doygun olduğunda ilgili operasyonlar birbirini beklemeye başlayabilir. Performans analizinde event loop boş olsa bile libuv pool saturation ihtimali düşünülmelidir.

JavaScript Worker Thread ile Farkı

Worker Thread geliştiricinin JavaScript kodunu ayrı thread üzerinde çalıştırmasını sağlar. libuv thread pool ise Node.js'in belirli native asenkron operasyonlarının altyapısında kullanılır. Uygulama normalde pool thread'lerine doğrudan JavaScript görev göndermez. İki sistemin boyutlandırma yöntemleri de farklıdır. Sorunu çözmeden önce hangi pool'un doygun olduğunu bilmek gerekir.

File System Operations

Birçok asenkron file system operasyonu libuv thread pool kullanabilir. Aynı anda çok sayıda ağır disk işi yapıldığında pool görevleri kuyrukta bekleyebilir. Bu durum file I/O latency'sini yükseltir. Stream kullanımı memory avantajı sağlasa da thread pool kapasitesi ayrı konudur. Diskin fiziksel throughput sınırı da göz önünde bulundurulmalıdır.

DNS

Node.js içindeki DNS API'lerinin çalışma mekanizması kullanılan fonksiyona göre değişebilir. Bazı lookup davranışları libuv thread pool üzerinden yürüyebilir. Yoğun DNS yükü diğer thread pool görevleriyle kaynak paylaşabilir. HTTP agent ve connection reuse DNS çağrı sayısını azaltabilir. Sorun gözleniyorsa doğru API'nin çalışma biçimi incelenmelidir.

Crypto

Bazı Node.js crypto operasyonları libuv thread pool kullanır. Yoğun password hashing veya benzeri işlerde havuz saturation görülebilir. Bu durumda diğer pool kullanıcılarının latency'si de artabilir. Concurrency sınırlama yalnızca Worker Threads için değil crypto işleri için de önemlidir. Güvenlik parametrelerini performans uğruna düşürmek doğru çözüm değildir.

Compression

Zlib tabanlı bazı compression işlemleri libuv thread pool kaynaklarını kullanabilir. Çok sayıda büyük sıkıştırma görevi pool'da bekleme yaratabilir. Stream kullanımı bellek profilini iyileştirir fakat CPU ve pool kapasitesini ortadan kaldırmaz. Compression seviyesi de işlem maliyetini etkileyebilir. Kullanıcı latency'si ve dosya boyutu arasında uygun denge kurulmalıdır.

Thread Pool Saturation

libuv thread pool içindeki tüm thread'ler uzun görevlerle meşgul olduğunda yeni işler sırada bekler. Bu durum event loop CPU kullanımından bağımsız bir latency kaynağıdır. Dosya sistemi ve crypto görevleri birbirini dolaylı etkileyebilir. Metrik ve profiling ile hangi operasyonların havuzu kullandığı belirlenmelidir. Sadece UV_THREADPOOL_SIZE artırmak sorunun kök nedenini çözmeyebilir.

UV_THREADPOOL_SIZE Ne Zaman Ayarlanmalıdır?

UV_THREADPOOL_SIZE libuv thread pool büyüklüğünü değiştirmek için kullanılan ortam ayarıdır. Bu değer ancak uygulamanın gerçekten thread pool kullanan operasyonlarda saturation yaşadığı ölçüldüğünde değerlendirilmelidir. Daha fazla thread bazı workloads altında throughput'u artırabilir. Ancak CPU, memory ve context switching maliyeti de yükselir. Değer değişikliği gerçekçi benchmark sonuçlarıyla doğrulanmalıdır.

Default Thread Pool

libuv varsayılan olarak sınırlı sayıda thread ile çalışır. Uygulamanın Node.js ve libuv sürümündeki güncel davranışı resmi dokümantasyon üzerinden kontrol edilmelidir. Varsayılan değer birçok normal I/O işi için yeterlidir. Rastgele artırmak gereksiz resource kullanımına yol açabilir. Önce saturation kanıtı aranmalıdır.

Crypto-Heavy Workload

Yoğun crypto workload aynı anda çok sayıda thread pool görevi oluşturabilir. Password hashing gibi pahalı işler request path üzerinde concurrency sınırı olmadan çalıştırılmamalıdır. Thread pool büyütmek sınırlı ölçüde yardımcı olabilir. CPU zaten doygunsa daha fazla thread performansı düşürebilir. Ayrı worker servisi veya queue da değerlendirilebilir.

File-System-Heavy Workload

Çok sayıda paralel file operation thread pool kapasitesini tüketebilir. Disk throughput sınırı aşılmışsa pool büyütmek fiziksel I/O hızını artırmaz. Hatta daha fazla eş zamanlı erişim disk performansını düşürebilir. Batch ve concurrency limitleri test edilmelidir. Storage mimarisi de çözümün parçasıdır.

Büyük Değerin Riskleri

Çok yüksek thread pool değeri context switching ve memory overhead oluşturabilir. CPU-bound pool görevleri tüm çekirdekleri tüketebilir. Böylece ana event loop scheduling açısından zarar görebilir. Uygulama yalnızca bir benchmark senaryosunda değil gerçek karma yük altında test edilmelidir. Daha büyük değer her zaman daha yüksek throughput demek değildir.

Benchmark

UV_THREADPOOL_SIZE değişikliği kontrollü performans testiyle değerlendirilmelidir. File I/O, crypto ve gerçek API yükü birlikte simüle edilebilir. p95 ve p99 latency, CPU, memory ve throughput izlenmelidir. Test environment CPU limiti production ile uyumlu olmalıdır. Kazanç görünmüyorsa varsayılan veya daha küçük değer tercih edilebilir.

Uzun İşlemler HTTP Request İçinde Çalıştırılmalı mı?

Dakikalar sürebilen veya retry gerektiren işler çoğu zaman HTTP request lifecycle içinde tutulmamalıdır. Proxy, load balancer veya client timeout süresi dolduğunda iş yarım kalabilir ya da kullanıcı sonucu göremez. Deployment sırasında process kapanması da iş kaybı oluşturabilir. Daha dayanıklı model request'in işi queue'ya bırakıp 202 Accepted ile job ID dönmesidir. Kullanıcı daha sonra status endpoint veya notification üzerinden sonucu alabilir.

Request Timeout

HTTP zincirindeki her katmanın farklı timeout sınırı olabilir. Uygulama 10 dakika beklemeye hazır olsa bile reverse proxy 60 saniyede bağlantıyı kapatabilir. Bu durumda işlem arka planda devam etse bile kullanıcı hata görür. Uzun task'ı request'ten ayırmak bu belirsizliği azaltır. Status API daha açık kullanıcı deneyimi sağlar.

Kullanıcı Bekleme Süresi

Kullanıcıyı birkaç dakika açık HTTP bağlantısında bekletmek çoğu ürün için iyi deneyim değildir. İşin kabul edildiğini hemen bildirmek daha güvenlidir. Progress bilgisi polling, SSE veya WebSocket ile sunulabilir. Sonuç hazır olduğunda notification gönderilebilir. Bu model mobil bağlantı kopmalarına karşı da daha dayanıklıdır.

Worker Crash

Request içindeki uzun işlem process crash durumunda tamamen kaybolabilir. Durable queue kullanıldığında tamamlanmamış job yeniden alınabilir. Bunun güvenli olabilmesi için iş idempotent tasarlanmalıdır. Visibility timeout veya lock mekanizması queue teknolojisine göre değişir. Crash senaryosu chaos test kapsamında doğrulanmalıdır.

Deployment Sırasında İş Kaybı

Yeni deployment sırasında eski pod veya process kapatılır. Request içinde uzun görev çalışıyorsa grace period yetmeden sonlandırılabilir. Durable queue'daki job ise lock süresi dolduktan sonra başka worker tarafından tekrar alınabilir. Graceful shutdown aktif işi bitirmek için ek koruma sağlar. Deployment stratejisi background processing davranışıyla birlikte tasarlanmalıdır.

Background Job'a Taşımak

Uzun işlem queue'ya taşındığında HTTP server ve worker bağımsız ölçeklenebilir. API düşük latency ile job kabul eder. Worker CPU veya I/O gereksinimine göre ayrı concurrency ayarı kullanır. Retry ve DLQ yalnızca worker katmanında yönetilebilir. Bu ayrım hem operasyon hem kod sorumluluğunu netleştirir.

Background Job Nedir?

Background job kullanıcı isteğinin dışında bağımsız şekilde işlenebilen kalıcı görev modelidir. Producer işi oluşturur, queue saklar ve worker uygun zamanda görevi alır. İş başarılı olduğunda sonuç veya durum kaydedilir. Geçici hata oluşursa retry uygulanabilir. Kalıcı başarısızlıklar DLQ veya failed job listesine taşınabilir.

Producer

Producer queue'ya yeni job ekleyen uygulama veya servis bileşenidir. Job payload mümkün olduğunca küçük tutulmalıdır. Büyük dosya yerine object storage referansı göndermek daha güvenlidir. Job ID idempotency veya deduplication amacıyla deterministik olabilir. Producer enqueue sonucunu doğru biçimde kontrol etmelidir.

Queue

Queue producer ile worker arasındaki zaman ve kapasite farkını tamponlar. Worker geçici olarak yavaşlasa bile işler kaybolmadan bekleyebilir. Queue depth büyümesi sistem kapasitesinin yetersiz olduğunu gösterebilir. Sınırsız backlog normal çalışma modeli olarak kabul edilmemelidir. Alert eşikleri oldest job age ile birlikte izlenmelidir.

Worker

Worker queue'dan job alır ve gerçek iş mantığını yürütür. Concurrency worker türüne göre ayarlanabilir. I/O işlerinde daha yüksek, CPU işlerinde daha düşük concurrency gerekebilir. Worker job tamamlandığında acknowledgement veya completion kaydı oluşturur. Shutdown sırasında yeni job almayı durdurup aktif işi güvenli kapatmalıdır.

Job

Job işlenecek görevin kimliğini ve gerekli minimum veriyi taşır. Payload schema'sı versionlanmalıdır. Job tekrar çalışabileceği için yan etkiler idempotent olmalıdır. Timeout ve maksimum deneme sayısı iş türüne göre belirlenebilir. Correlation ID observability için payload veya metadata içinde taşınabilir.

Result

Job sonucu kullanıcıya veya başka servise gerekebilir. Sonuç database, object storage veya kısa süreli cache içinde saklanabilir. Büyük sonuçları queue backend içinde tutmak pahalı olabilir. Status endpoint job state ile result referansını döndürebilir. Sonucun ne kadar süre saklanacağı lifecycle politikasında belirlenmelidir.

Failed Job

Job maksimum retry sayısını aştığında failed duruma geçebilir. Hatanın payload, dependency veya kod kaynaklı olduğu sınıflandırılmalıdır. Failed job görünür olmalı ve alarm üretmelidir. Manuel replay yapılacaksa idempotency garantisi korunmalıdır. Aynı hatalı job'ın sonsuz tekrar edilmesi önlenmelidir.

Hangi İşler Background Job Olmalıdır?

Uzun süren, retry gerektiren veya kullanıcı response süresinden bağımsız yürütülebilen işler background job için güçlü adaylardır. E-posta, rapor, media processing ve büyük import operasyonları buna örnektir. İşin durable olması gerekiyorsa bellek içi fire-and-forget Promise yeterli değildir. Queue görev yaşam döngüsünü process'ten ayırır. Her job için timeout, retry ve idempotency baştan tasarlanmalıdır.

E-posta

E-posta gönderimi kullanıcının ana transaction'ını çoğu zaman bekletmemelidir. API gerekli kaydı oluşturup e-posta job'ını queue'ya ekleyebilir. Provider geçici hata verirse retry uygulanabilir. Aynı e-postanın iki kez gönderilmesini önlemek için deduplication gerekebilir. Kritik bildirimlerde teslim durumu ayrıca izlenmelidir.

PDF/Rapor

Büyük rapor üretimi CPU ve database üzerinde uzun süre çalışabilir. Background worker bu yükü kullanıcı request'inden ayırır. Sonuç object storage'a yüklenip kullanıcıya link bildirilebilir. Aynı rapor isteği tekrar gelirse cache veya idempotent job ID kullanılabilir. Worker concurrency database kapasitesine göre sınırlandırılmalıdır.

Video/Image Processing

Media processing genellikle CPU ve disk açısından pahalıdır. Job queue farklı çözünürlük veya format dönüşümlerini worker'lara dağıtabilir. Her dosya için metadata database'de tutulabilir. Harici executable kullanılıyorsa child process lifecycle ayrıca yönetilmelidir. Büyük binary payload queue'ya doğrudan konulmamalıdır.

Data Import

Büyük data import işlemleri request içinde yürütülmemelidir. Dosya object storage'a yüklenip import job'ı oluşturulabilir. Worker stream ile dosyayı okuyup validate ve batch insert yapabilir. Progress ayrı tabloda saklanabilir. Hatalı satırlar kullanıcıya indirilebilir rapor halinde sunulabilir.

Export

Büyük export sorguları database ve memory üzerinde ciddi yük oluşturabilir. Background job sonucu stream ederek dosyaya veya object storage'a yazabilir. Kullanıcı 202 Accepted sonrası job durumunu takip eder. Hazır olduğunda download link oluşturulur. Expiration politikası eski export dosyalarını temizlemelidir.

Webhook Delivery

Webhook gönderimi harici sistemin availability durumuna bağlıdır. Request içinde senkron gönderim yapmak kullanıcı işlemini dış servise bağımlı hale getirir. Queue üzerinden delivery job oluşturmak retry ve backoff yönetimini kolaylaştırır. Her webhook event ID ile idempotent izlenebilir. Başarısız teslimatlar DLQ veya dashboard üzerinden görünür olmalıdır.

AI/LLM İşlemleri

Uzun sürebilen model çağrıları veya toplu içerik işlemleri background job olarak yürütülebilir. Provider rate limit ve maliyet sınırları concurrency politikasına dahil edilmelidir. Timeout ve kullanıcı iptali desteklenmelidir. Hassas veriler job payload içinde gereksiz yere saklanmamalıdır. Sonuçların doğrulanması business gereksinimine göre ayrı aşama olabilir.

Batch Analytics

Geniş veri setleri üzerinde çalışan analytics görevleri anlık HTTP response içinde tamamlanmak zorunda değildir. Scheduled veya event-triggered job olarak çalıştırılabilir. Database üzerindeki ağır sorgular düşük trafik zamanına planlanabilir. Sonuç cache veya raporlama tablosuna yazılabilir. Aynı görevin eş zamanlı iki kez çalışması gerekiyorsa locking politikası tanımlanmalıdır.

Cron ile Queue Arasındaki Fark

Cron belirli zamanlarda görev tetiklemek için kullanılan scheduling yaklaşımıdır. Queue ise görevleri güvenli biçimde saklama, dağıtma ve tekrar deneme ihtiyaçlarını çözer. Bir sistemde ikisi birlikte kullanılabilir ve cron yalnızca queue'ya job ekleyebilir. Multi-instance ortamında her instance'ın aynı cron'u çalıştırması duplicate iş oluşturabilir. Kritik periyodik görevlerde distributed scheduler veya queue tabanlı recurring job mekanizması daha güvenlidir.

Time-Triggered Task

Cron belirli saat veya periyot geldiğinde görev çalıştırır. Gece cleanup veya günlük rapor buna örnektir. Zamanlama job'ın başarıyla tamamlandığını garanti etmez. Process kapalıysa bazı cron çözümlerinde çalışma tamamen kaçırılabilir. Kritik işler persistence ve misfire handling gerektirir.

Event-Triggered Task

Queue görevleri çoğunlukla kullanıcı işlemi, domain event veya API çağrısı sonucunda oluşur. Tetikleyici zaman değil sistem olayıdır. Worker uygun kapasite olduğunda görevi işler. Event yoğunluğu artarsa queue backlog büyüyebilir. Bu yüzden producer ve consumer rate birlikte izlenmelidir.

Retry

Basit cron mekanizması retry politikasını kendiliğinden sağlamayabilir. Queue sistemleri attempts ve backoff özellikleri sunabilir. Kritik scheduled görev cron ile tetiklenip durable queue üzerinden işlenebilir. Böylece schedule ve execution sorumluluğu ayrılır. Hata yönetimi daha görünür hale gelir.

Multi-Instance

Uygulama üç replica çalıştırıyorsa her replica aynı cron'u tetikleyebilir. Bu durum aynı görevin üç kez çalışmasına neden olur. Leader election veya distributed lock duplicate çalışmayı önleyebilir. Alternatif olarak scheduler tek merkezi bileşen olabilir. Job'ın yine de idempotent tasarlanması ek koruma sağlar.

Deduplication

Scheduled job deterministik ID ile queue'ya eklenebilir. Aynı zaman dilimi için aynı ID kullanılarak duplicate enqueue önlenebilir. Bu yaklaşım scheduler hatalarında ek güvenlik sağlar. Deduplication süresi iş semantiğine göre seçilmelidir. Job tamamlandıktan sonra aynı ID'nin tekrar kullanım davranışı queue teknolojisine bağlıdır.

Visibility

Queue sistemleri waiting, active, completed ve failed job durumlarını görünür hale getirebilir. Basit cron ise yalnızca tetikleme kaydı bırakabilir. Operasyon ekibi için hangi işin ne zaman ve neden başarısız olduğu önemlidir. Metrics ve dashboard kritik scheduled görevlerde büyük değer sağlar. Gözlemlenemeyen otomasyon zamanla operasyon riski oluşturur.

Node.js'te Cron Ne Zaman Yeterlidir?

Basit, düşük riskli ve tek instance üzerinde çalışan görevlerde cron yeterli olabilir. Örneğin kritik olmayan cache temizliği veya geçici dosya temizliği buna örnektir. Görev kaçırıldığında ciddi iş kaybı oluşmuyorsa ek queue altyapısı gerekmeyebilir. Yine de hata loglama ve çalışma süresi metriği eklenmelidir. Sistem büyüdükçe scheduling gereksinimi yeniden değerlendirilmelidir.

Tek Process

Uygulama gerçekten tek process olarak çalışıyorsa duplicate cron riski daha düşüktür. Ancak deployment sırasında kısa süreli iki process overlap edebilir. Bu durum yine duplicate tetikleme yaratabilir. Kritik işlerde idempotency koruması faydalıdır. Gelecekte horizontal scaling planı varsa tasarım buna hazırlanmalıdır.

Düşük Risk

Görevin çalışmaması kullanıcı verisi veya finansal sonuç üretmiyorsa basit cron yeterli olabilir. Örneğin kullanılmayan temp dosyaları temizlemek buna yakındır. Buna rağmen görevin başarısızlığı sürekli hale gelmemelidir. Log ve basit alarm mekanizması eklenebilir. Kritik olmayan iş de kontrolsüz kaynak tüketmemelidir.

Periyodik Cleanup

Eski cache veya geçici dosyaları temizlemek cron için uygun kullanım alanıdır. Cleanup işlemi idempotent tasarlanmalıdır. Bir kez fazla çalışması veri kaybına neden olmamalıdır. Büyük dataset temizliği batch halinde yapılmalıdır. Uzun transaction veya event loop bloklanması önlenmelidir.

Cache Refresh

Kritik olmayan cache refresh periyodik cron ile yapılabilir. Refresh başarısız olduğunda eski cache kısa süre daha kullanılabiliyorsa risk düşüktür. Tüm cache'i aynı anda yenilemek thundering herd benzeri yük oluşturabilir. Veriler parçalara ayrılabilir veya jitter uygulanabilir. Refresh süresi metrik olarak izlenmelidir.

Kaçırılan Bir Çalışmanın Kritik Olmaması

Process kapalı olduğu için tek cron çalışmasının kaçırılması ciddi sonuç oluşturmuyorsa basit scheduler kabul edilebilir. Sonraki çalışma eksik işi telafi edebiliyorsa risk daha da düşer. Finansal settlement veya kritik bildirim gibi görevler bu kategoriye girmez. İş etkisi scheduling teknolojisini belirlemelidir. Gereksiz altyapı kadar eksik dayanıklılık da maliyetlidir.

Distributed Scheduler Ne Zaman Gereklidir?

Birden fazla application replica aynı scheduled görevi çalıştırabilecekse koordinasyon ihtiyacı doğar. Distributed scheduler job persistence, leader election veya merkezi scheduling mekanizmasıyla duplicate çalışmayı önleyebilir. Kritik görevlerde missed run davranışı da yönetilmelidir. Scheduler'ın kendisi yüksek erişilebilir tasarlanmalıdır. Recurring job'ların gerçek execution'ı yine worker queue üzerinden yapılabilir.

Birden Fazla App Replica

Horizontal scaling her replica'nın yerel cron çalıştırmasını riskli hale getirir. Aynı dakikada onlarca duplicate job oluşabilir. Distributed lock geçici çözüm sunabilir. Merkezi scheduler daha açık ownership sağlar. İş yine idempotent olmalıdır.

Leader Election

Leader election replica grubundan yalnızca bir instance'ın scheduler görevini yürütmesini sağlar. Leader kapanırsa başka instance görevi devralabilir. Bu mekanizmanın network partition durumları doğru ele alınmalıdır. Lease süresi ve clock davranışı önemlidir. Kritik görevlerde queue deduplication ek koruma sağlayabilir.

Job Persistence

Scheduler yalnızca memory içinde plan tutuyorsa restart sırasında görevler kaybolabilir. Job persistence planların güvenli storage üzerinde tutulmasını sağlar. Uygulama yeniden başladığında kaçırılan görevler tespit edilebilir. Persistence özellikle uzun vadeli scheduled işler için önemlidir. Veri modeli schema değişikliklerine dayanıklı olmalıdır.

Retry

Scheduler görevi tetiklediğinde execution başarısız olabilir. Retry scheduling ve job processing katmanlarından hangisinin sorumluluğu olduğu belirlenmelidir. Çoğu sistemde scheduler yalnızca job oluşturur ve retry queue worker tarafından yönetilir. Bu ayrım duplicate retry riskini azaltır. Maksimum deneme ve backoff merkezi biçimde tanımlanmalıdır.

Misfire Handling

Misfire planlanan zamanda çalışmayan scheduled görevi ifade eder. Sistem yeniden geldiğinde görevin hemen çalışması, atlanması veya bir sonraki zamana bırakılması gerekebilir. Bu karar iş semantiğine bağlıdır. Günlük finansal rapor ile cache refresh aynı davranışı kullanmamalıdır. Scheduler bu politikayı açık biçimde desteklemelidir.

BullMQ ile Asenkron Job Processing

BullMQ, Redis tabanlı Node.js job queue ihtiyaçlarında kullanılabilen bir araçtır. Job persistence, retry, delay, priority ve worker concurrency gibi özellikler sunar. Redis'in ayrı bir operasyonel bileşen olduğunu unutmamak gerekir. Queue verisinin dayanıklılık ayarları job kritikliğine göre yapılandırılmalıdır. BullMQ kullanmak idempotency ve business hata sınıflandırması ihtiyacını ortadan kaldırmaz.

Redis

BullMQ job state ve queue verisini Redis üzerinde tutar. Redis kapasitesi, persistence ve high availability ayarları sistemin dayanıklılığını etkiler. Büyük payload'ları Redis içine koymak memory maliyetini artırır. Dosya veya büyük obje yerine referans saklamak daha uygundur. Redis latency'si queue throughput metriğiyle birlikte izlenmelidir.

Queue

Queue bekleyen job'ların mantıksal grubudur. Farklı iş türleri ayrı queue'lara ayrılarak concurrency ve retry politikaları bağımsız yönetilebilir. Tüm görevleri tek queue'ya koymak uzun işlerin kısa işleri bekletmesine neden olabilir. Queue isimleri ve ownership açık olmalıdır. Monitoring dashboard her queue için temel metrikleri göstermelidir.

Worker

BullMQ Worker queue'dan job alıp işleyen bileşendir. Worker concurrency I/O veya CPU yapısına göre ayarlanmalıdır. CPU-heavy iş doğrudan yüksek concurrency ile aynı event loop üzerinde çalıştırılmamalıdır. Gerekirse Worker Thread pool ile entegre edilebilir. Graceful shutdown sırasında yeni job alınması durdurulmalıdır.

Job Data

Job data işlemek için gereken minimum bilgiyi taşımalıdır. Büyük dosya içeriği yerine storage key veya database ID tercih edilmelidir. Payload schema version alanı içerebilir. Gizli token veya gereksiz kişisel veri job içinde saklanmamalıdır. Worker input validation uygulamalıdır.

Attempts

Attempts bir job'ın başarısızlık sonrası en fazla kaç kez denenebileceğini belirler. Her hata aynı şekilde retry edilmemelidir. Geçici network hatası tekrar denenebilirken validation hatası kalıcı olabilir. Yüksek attempts değeri bozuk job'ın sistemi meşgul etmesine neden olabilir. Failed job sonunda görünür bir inceleme kanalına gitmelidir.

Backoff

Backoff retry denemeleri arasında bekleme süresi uygular. Sabit veya exponential yöntem kullanılabilir. Downstream servis çökmüşken anında retry yapmak recovery'yi zorlaştırır. Jitter birçok worker'ın aynı anda tekrar denemesini engeller. Backoff süresi işin kabul edilebilir tamamlanma süresiyle uyumlu olmalıdır.

Priority

Priority önemli job'ların normal görevlerden önce işlenmesini sağlayabilir. Ancak sürekli yüksek priority job gelirse düşük priority işler starvation yaşayabilir. Queue tasarımı farklı SLA sınıflarını ayrı queue'lara ayırmayı da değerlendirmelidir. Priority kullanımı müşteri veya tenant adaletiyle birlikte düşünülmelidir. Dashboard priority dağılımını görünür hale getirebilir.

Delay

Delayed job belirli süre sonra işlenmek üzere planlanır. Hatırlatma, geçici bekleme veya scheduled retry senaryolarında kullanılabilir. Çok uzun vadeli görevlerde tarih semantiği ve timezone dikkate alınmalıdır. Job'ın gecikme süresi dolduğunda worker kapasitesi yoksa daha sonra çalışabilir. Delay kesin gerçek zaman garantisi değildir.

Concurrency

BullMQ worker concurrency aynı worker içinde eş zamanlı işlenen job sayısını kontrol edebilir. I/O ağırlıklı job'larda concurrency artırmak throughput'u yükseltebilir. CPU-heavy JavaScript işlerinde aynı event loop üzerinde yüksek concurrency fayda sağlamaz. Downstream kapasite ve memory limiti dikkate alınmalıdır. Concurrency değeri load test ile belirlenmelidir.

pg-boss ile PostgreSQL Tabanlı Queue

pg-boss PostgreSQL üzerinde job queue kurmak isteyen Node.js ekipleri için değerlendirilebilen bir yaklaşımdır. Ayrı Redis altyapısı eklemeden mevcut PostgreSQL dayanıklılığından yararlanabilir. Transactional enqueue bazı kullanım durumlarında güçlü avantaj sağlar. Bununla birlikte queue yükü aynı database kaynaklarını business sorgularıyla paylaşabilir. Throughput, maintenance ve database kapasitesi birlikte değerlendirilmelidir.

PostgreSQL'i Queue Olarak Kullanmak

PostgreSQL dayanıklı veri saklama ve transaction özellikleri sayesinde job state tutmak için kullanılabilir. Bu yaklaşım operasyonel bileşen sayısını azaltabilir. Düşük ve orta hacimli job sistemlerinde pratik olabilir. Çok yüksek queue throughput'unda database yükü yakından ölçülmelidir. Business tablolarıyla aynı cluster'ın kaynak paylaşımı önemlidir.

Transactional Enqueue

Transactional enqueue business database değişikliği ile job kaydını aynı transaction içinde oluşturabilmeyi sağlar. Böylece database commit başarılı olup queue publish başarısız kalması gibi dual-write problemi azaltılabilir. Bu özellik PostgreSQL tabanlı queue yaklaşımının önemli avantajlarından biridir. Job worker tarafından daha sonra güvenli biçimde alınır. Consumer yine idempotent olmalıdır.

Retry

pg-boss başarısız job'ların tekrar denenmesini destekleyebilir. Retry politikası hata sınıfına göre uygulanmalıdır. Kalıcı validation hatası tekrar edilmemelidir. Backoff ile downstream recovery için zaman bırakılabilir. Maksimum deneme sonunda failed job görünür hale getirilmelidir.

Scheduling

Scheduled ve delayed işler database üzerinde kalıcı biçimde tutulabilir. Restart sonrasında planların korunması kritik scheduled görevlerde faydalıdır. Timezone ve DST kuralları uygulama seviyesinde açık olmalıdır. Worker gecikmesi planlanan zaman ile gerçek çalışma zamanı arasında fark oluşturabilir. Bu fark metriğe dönüştürülebilir.

Redis Eklememek

Zaten PostgreSQL kullanan küçük veya orta ölçekli sistemlerde ayrı Redis cluster eklememek operasyonel kolaylık sağlayabilir. Backup, monitoring ve erişim politikaları tek database platformunda kalabilir. Ancak queue yükü business database performansını etkilememelidir. İzolasyon gerektiğinde ayrı database veya cluster düşünülebilir. Altyapı sayısını azaltmak tek başına mimari kriter değildir.

BullMQ ile Farkı

BullMQ Redis üzerinde çalışırken pg-boss PostgreSQL'i temel alır. Bu fark throughput, transactional enqueue ve operasyon modelini etkiler. BullMQ Redis'in hızlı in-memory veri yapılarından yararlanırken pg-boss relational transaction özellikleriyle öne çıkabilir. Seçim mevcut altyapı ve job semantiğine göre yapılmalıdır. Benchmark gerçek iş yüküyle gerçekleştirilmelidir.

BullMQ vs pg-boss

BullMQ ve pg-boss aynı temel job processing problemini farklı altyapılar üzerinden çözer. BullMQ Redis gerektirirken pg-boss PostgreSQL kullanır. Redis zaten platformun merkezi bir bileşeniyse BullMQ doğal olabilir. Transactional enqueue ile business database değişikliğini aynı transaction içinde tutmak önemliyse PostgreSQL yaklaşımı avantajlıdır. Doğru seçim ekibin işletme kapasitesi ve beklenen workload ile belirlenmelidir.

Redis Gereksinimi

BullMQ için Redis erişimi ve işletimi gerekir. Redis persistence, replication ve failover stratejileri job kritikliğine göre ayarlanmalıdır. Managed Redis kullanımı operasyon yükünü azaltabilir. Büyük payload Redis memory maliyetini artırır. Queue kapasitesi ayrı olarak planlanmalıdır.

PostgreSQL Gereksinimi

pg-boss PostgreSQL üzerinde çalışır ve mevcut relational altyapıyla bütünleşebilir. Bu kullanım ayrı broker ihtiyacını azaltabilir. Fakat queue sorguları database CPU ve I/O kaynaklarını tüketir. Yoğun transactional sistemde bu ek yük dikkatle ölçülmelidir. Connection pool planı queue worker'larını da kapsamalıdır.

Throughput

Gerçek throughput job boyutu, database veya Redis latency'si ve worker hızına bağlıdır. Sadece broker benchmark rakamına bakmak yanıltıcıdır. Çoğu business job'da asıl sınır downstream API veya database olabilir. Test gerçek payload ve retry davranışını içermelidir. Queue seçimi beklenen peak yükle doğrulanmalıdır.

Transactional Enqueue

PostgreSQL tabanlı queue aynı database transaction içinde job oluşturma avantajı sağlayabilir. Redis tabanlı queue ile business database arasında doğrudan ortak transaction yoktur. Bu durumda transactional outbox gibi desenler kullanılabilir. Tutarlılık ihtiyacı mimari seçimde önemli kriterdir. Dual-write riski göz ardı edilmemelidir.

Monitoring

Her iki yaklaşımda da waiting, active, failed ve retry job'ları izlemek gerekir. Kullanılan dashboard ve metric entegrasyonu operasyon deneyimini etkiler. Queue backend sağlığı ile business job sağlığı ayrı metriklerdir. Redis veya PostgreSQL sağlıklı olsa bile worker hatalı olabilir. Uçtan uca job latency mutlaka izlenmelidir.

Job Graph

Bazı queue sistemleri parent-child veya flow benzeri job graph özellikleri sunabilir. Gereksinim gerçekten workflow düzeyindeyse araçların bu desteği karşılaştırılmalıdır. Basit job queue üzerine çok karmaşık DAG mantığı eklemek bakım yükünü artırabilir. Uzun workflow'larda özel workflow engine gerekebilir. Seçim yalnızca temel enqueue API'sine göre yapılmamalıdır.

Operasyonel Karmaşıklık

Yeni Redis cluster eklemek ayrı monitoring, backup ve erişim politikası gerektirebilir. PostgreSQL'i queue olarak kullanmak bileşen sayısını azaltırken mevcut database üzerindeki yükü artırabilir. Operasyon ekibinin deneyimi toplam maliyetin önemli parçasıdır. Daha az bileşen her zaman daha iyi performans anlamına gelmez. Sistem yaşam döngüsü boyunca bakım maliyeti değerlendirilmelidir.

RabbitMQ Node.js Asenkron İşlemede Nerede Kullanılır?

RabbitMQ mesaj yönlendirme, work queue ve farklı consumer'lara kontrollü mesaj dağıtımı gereken sistemlerde güçlü bir seçenektir. Producer mesajı exchange'e gönderir ve routing kuralları uygun queue'lara iletir. Consumer mesajı işleyip acknowledgement gönderir. Prefetch ile consumer başına aktif mesaj sayısı sınırlanabilir. Bu model Node.js worker sistemlerinde doğal backpressure ve routing kontrolü sağlar.

Producer

Producer RabbitMQ'ya mesaj yayınlayan bileşendir. Mesajın exchange, routing key ve persistence ayarları iş gereksinimine göre seçilir. Publish başarılı göründüğünde broker garantilerinin ne olduğu anlaşılmalıdır. Publisher confirm kritik mesajlarda değerlendirilebilir. Mesaj schema'sı versionlanmalıdır.

Exchange

Exchange producer'dan gelen mesajların hangi queue'lara yönlendirileceğini belirler. Direct, topic veya fanout gibi farklı routing modelleri kullanılabilir. Bu yapı producer'ın consumer queue isimlerini bilme zorunluluğunu azaltır. Routing tasarımı domain event yapısıyla uyumlu olmalıdır. Gereksiz exchange ve queue sayısı operasyon yükünü artırabilir.

Queue

RabbitMQ queue mesajları consumer hazır olana kadar saklar. Durable queue ve persistent message ayarları veri kaybı toleransına göre değerlendirilmelidir. Queue depth sürekli büyüyorsa consumer kapasitesi yetersiz olabilir. Tek çözüm daha fazla consumer açmak olmayabilir. Downstream servis kapasitesi ve mesaj maliyeti birlikte ölçülmelidir.

Consumer

Consumer queue'dan mesaj alıp iş mantığını yürütür. İş başarıyla tamamlandığında acknowledgement gönderilebilir. Consumer crash olursa ack verilmemiş mesaj yeniden teslim edilebilir. Bu nedenle handler idempotent olmalıdır. Concurrency ve prefetch değerleri worker kapasitesine göre ayarlanmalıdır.

Routing Key

Routing key mesajın exchange üzerinden hangi queue'ya gideceğini belirleyen anahtar olabilir. Topic exchange ile pattern tabanlı yönlendirme yapılabilir. Domain event isimlendirmesi routing tasarımını sadeleştirir. Anahtarların version ve ownership politikası olmalıdır. Tüm iş mantığını routing key içine gömmek bakım yükü yaratır.

Acknowledgement

Acknowledgement consumer'ın mesajı başarıyla tamamladığını broker'a bildirmesidir. Ack çok erken gönderilirse sonrasında process crash olduğunda mesaj kaybolabilir. Ack yalnızca gerekli side effect güvenli biçimde tamamlandıktan sonra verilmelidir. Redelivery olasılığı her zaman düşünülmelidir. Idempotency bu nedenle kritik bir tasarım unsurudur.

Prefetch

Prefetch consumer'ın acknowledgement vermeden önce aynı anda kaç mesaj alabileceğini sınırlar. Çok yüksek değer bir consumer'ın çok sayıda mesajı üzerinde tutmasına neden olabilir. Yavaş işler için düşük prefetch daha adil dağıtım sağlar. Hızlı I/O işlerinde daha yüksek değer throughput'u artırabilir. Doğru sayı benchmark ile bulunmalıdır.

RabbitMQ Prefetch ile Backpressure

RabbitMQ prefetch consumer tarafındaki kapasiteyi broker'a yansıtan etkili bir backpressure mekanizmasıdır. Consumer aynı anda sınırlı sayıda unacked mesaj alır. İşler yavaşladığında broker yeni mesajları diğer uygun consumer'lara yönlendirebilir veya queue'da bekletebilir. Böylece tek worker sınırsız mesajı belleğine çekmez. Prefetch worker concurrency ve işlem süresiyle uyumlu ayarlanmalıdır.

Consumer'a Aynı Anda Verilen Mesaj Sayısı

Prefetch değeri bir consumer'ın işlenmemiş olarak üzerinde tutabileceği mesaj sayısını sınırlar. Değer çok yüksekse memory ve redelivery maliyeti artabilir. Çok düşükse consumer I/O beklerken kapasite boş kalabilir. Workload ölçülerek uygun değer seçilmelidir. Büyük payload'larda daha düşük prefetch gerekebilir.

Slow Consumer

Slow consumer mesajları producer hızından daha yavaş işler. Queue depth ve oldest message age zamanla büyür. Prefetch bu consumer'ın sınırsız mesaj almasını önler. Ancak backlog problemini tek başına çözmez. Consumer kapasitesi veya upstream producer hızı da ayarlanmalıdır.

Memory

Consumer'ın aldığı her unacked mesaj memory üzerinde belirli alan tutabilir. Büyük payload ve yüksek prefetch kombinasyonu worker memory'sini hızla yükseltebilir. Mesaj boyutu ayrıca sınırlandırılmalıdır. Büyük veri object storage referansıyla taşınabilir. Memory metrikleri queue ayarlarıyla birlikte izlenmelidir.

Fair Dispatch

Düşük veya uygun prefetch farklı consumer'lar arasında daha dengeli iş dağıtımına yardımcı olabilir. Bir consumer çok uzun görev aldığında diğerleri kısa işleri işlemeye devam eder. Her işin süresi çok farklıysa tek yüksek prefetch adaleti bozabilir. Queue ayrımı veya priority de değerlendirilebilir. Fairness yalnızca worker sayısına bağlı değildir.

Worker Concurrency

Worker aynı process içinde birden fazla mesajı concurrent işleyebilir. Prefetch değeri bu concurrency ile uyumlu olmalıdır. Concurrency 10 iken prefetch 1000 vermek çoğu durumda gereksizdir. I/O ve CPU iş türleri farklı concurrency değerleri gerektirir. Worker memory ve downstream limitleri birlikte ölçülmelidir.

Kafka ile Node.js Event Processing

Kafka yüksek hacimli event akışlarını kalıcı log modeliyle saklamak ve birden fazla consumer grubunun bağımsız biçimde tüketmesini sağlamak için kullanılır. Event'ler topic ve partition yapısı içinde tutulur. Consumer group aynı iş yükünü üyeler arasında paylaşabilir. Offset sayesinde tüketim konumu izlenir ve gerektiğinde replay yapılabilir. Kafka'yı klasik job queue ile aynı şey olarak görmek tasarım hatalarına yol açabilir.

Topic

Topic belirli event kategorisinin saklandığı mantıksal log alanıdır. Domain event'ler anlamlı topic sınırlarına ayrılabilir. Çok fazla topic operasyon yükünü artırabilir. Çok geniş tek topic ise schema ve ownership sorunları oluşturabilir. Topic retention iş gereksinimine göre ayarlanmalıdır.

Partition

Partition Kafka throughput ve ordering modelinin temel parçasıdır. Aynı partition içindeki event sırası korunabilir. Consumer group içindeki parallelism partition sayısıyla sınırlıdır. Partition key yanlış seçilirse bir partition aşırı yüklenebilir. Tenant veya entity bazlı ordering ihtiyacı key tasarımını etkiler.

Producer

Producer event'i ilgili topic'e yayınlar. Partition key ve acknowledgement ayarları delivery garantilerini etkiler. Batch ve compression throughput'u artırabilir. Producer retry davranışı duplicate event ihtimalini hesaba katmalıdır. Event ID consumer idempotency için yararlıdır.

Consumer

Consumer topic event'lerini okuyup business işlemini yürütür. Offset'in ne zaman commit edildiği hata semantiğini etkiler. İş tamamlanmadan offset ilerletilirse crash durumunda event kaybı yaşanabilir. İş sonrası commit ise duplicate delivery ihtimali yaratabilir. Consumer idempotent olmalıdır.

Consumer Group

Consumer group aynı logical consumer'ın birden fazla instance arasında ölçeklenmesini sağlar. Bir partition aynı group içinde aynı anda tek consumer üyesine atanır. Instance sayısı partition sayısını aştığında bazı consumer'lar boşta kalabilir. Rebalance sırasında kısa süreli tüketim duraklamaları oluşabilir. Partition planı gelecekteki ölçek ihtiyacını düşünmelidir.

Offset

Offset consumer'ın partition içindeki konumunu ifade eder. Offset yönetimi tekrar işleme ve recovery davranışını belirler. Business side effect ile offset commit arasında atomic transaction her zaman mümkün değildir. Bu nedenle idempotent consumer tasarımı önemlidir. Replay senaryolarında başlangıç offset'i kontrollü seçilmelidir.

Event Replay

Kafka'nın güçlü yönlerinden biri geçmiş event'lerin retention süresi içinde yeniden tüketilebilmesidir. Yeni projection oluşturmak veya hatalı consumer sonucunu yeniden hesaplamak için replay kullanılabilir. Consumer replay'e dayanıklı olmalıdır. Harici yan etkiler tekrar gerçekleşmemelidir. Replay ayrı hız limitiyle production sistemleri korumalıdır.

Kafka ile Job Queue Aynı Şey midir?

Kafka ve klasik job queue aynı problem alanına kısmen dokunsa da temel semantikleri farklıdır. Kafka durable event log ve replay modeline odaklanır. Job queue ise belirli görevin bir worker tarafından tamamlanması ve gerektiğinde retry edilmesi modelini öne çıkarır. Kafka üzerinde task processing yapılabilir fakat iş completion davranışı ayrıca tasarlanmalıdır. Teknoloji seçimi event geçmişinin değerine ve görev semantiğine göre yapılmalıdır.

Durable Event Log

Kafka event'leri tüketildikten sonra hemen silmek yerine retention politikasına göre saklayabilir. Bu özellik geçmişin yeniden okunmasını sağlar. Audit, analytics ve yeni consumer oluşturma açısından değerlidir. Job queue'larda completion sonrası görev genellikle aktif iş listesinde tutulmaz. Veri yaşam döngüsü mimarinin önemli farkıdır.

Work Queue

Work queue belirli görevin consumer'lardan biri tarafından işlenmesine odaklanır. İş tamamlandığında acknowledgement veya completion state kaydedilir. Retry ve DLQ çoğu queue platformunda doğrudan kavram olarak bulunur. Kafka'da benzer davranış uygulama tasarımıyla oluşturulabilir. Basit job processing için özel queue daha kolay olabilir.

Replay

Kafka geçmiş event'i tekrar okumayı doğal bir kullanım modeli olarak destekler. Job queue'da completed job'ı yeniden çalıştırmak genellikle özel replay veya requeue işlemidir. Event-sourced veya analytics sistemlerde replay güçlü gereksinim olabilir. E-posta gönderme gibi yan etkili görevlerde replay istenmeyebilir. Semantik teknoloji seçimini belirlemelidir.

Ordering

Kafka partition içinde ordering sağlar. Job queue'larda ordering özellikleri teknoloji ve ayara göre değişebilir. Tam global ordering throughput'u sınırlayabilir. Çoğu sistem entity bazlı ordering ile daha iyi ölçeklenir. Partition key veya queue ayrımı buna göre tasarlanabilir.

Consumer Groups

Kafka consumer group aynı event stream workload'unu üyeler arasında dağıtır. Farklı group'lar aynı event'i bağımsız olarak tüketebilir. Bu davranış fan-out için güçlüdür. Job queue'da aynı queue'daki consumer'lar genellikle tek iş paylaşımı mantığıyla çalışır. Birden fazla bağımsız iş için ayrı queue veya binding kullanılabilir.

Task Completion Semantiği

Job queue dünyasında görev tamamlandı, retry edilecek veya failed gibi durumlar doğrudan önemlidir. Kafka daha çok event tüketim konumu ve log saklama semantiği sunar. Business task completion ayrıca database veya başka state ile modellenebilir. Bu ek tasarım maliyeti basit job sisteminde gereksiz olabilir. Araç problemin doğal kavramlarına ne kadar yaklaşıyorsa bakım kolaylaşır.

Hangi Durumda Hangisi?

E-posta, rapor veya webhook gibi görevlerde job queue çoğu zaman daha doğal seçimdir. Domain event'leri birçok consumer'ın tüketmesi ve geçmişin replay edilmesi gerekiyorsa Kafka daha uygun olabilir. Mesaj routing ihtiyacı ağırsa RabbitMQ güçlü bir alternatif sunar. Aynı platformda birden fazla araç kullanmak da mümkündür. Gereksiz altyapı çeşitliliği operasyon maliyetini artırdığı için her kullanım gerekçelendirilmelidir.

Redis Queue vs RabbitMQ vs Kafka

Redis tabanlı queue, RabbitMQ ve Kafka aynı kategoriye sıkça konulsa da güçlü oldukları kullanım alanları farklıdır. BullMQ job lifecycle ve Node.js entegrasyonunda pratik olabilir. RabbitMQ routing, acknowledgement ve work queue davranışında güçlüdür. Kafka ise yüksek hacimli durable event stream ve replay için tasarlanmıştır. Seçim yalnızca saniyedeki mesaj sayısına değil, iş semantiğine ve operasyon yetkinliğine dayanmalıdır.

BullMQ / Redis

BullMQ Node.js job processing için retry, delay ve worker concurrency gibi özellikler sunar. Redis altyapısı gerektirir. Webhook, e-posta ve background task gibi işler için kullanılabilir. Büyük payload'ları Redis'te saklamak pahalı olabilir. Job ID ve idempotency business katmanında düşünülmelidir.

RabbitMQ

RabbitMQ exchange ve routing key modeliyle esnek mesaj yönlendirme sağlar. Ack ve prefetch consumer kontrolünü güçlendirir. Work queue ve event distribution arasında çeşitli topolojiler kurulabilir. Broker operasyonu ayrı uzmanlık gerektirir. Queue depth ve consumer health yakından izlenmelidir.

Kafka

Kafka partitioned log yapısıyla yüksek hacimli event akışlarında güçlüdür. Event replay ve bağımsız consumer groups temel avantajlarıdır. Basit bir background e-posta job'ı için gereğinden ağır olabilir. Ordering partition seviyesinde yönetilir. Retention ve storage planı event hacmine göre yapılmalıdır.

Job Processing

Job processing belirli görevin tamamlanması ve retry edilmesiyle ilgilidir. BullMQ veya RabbitMQ bu semantiğe doğrudan yaklaşabilir. Kafka ile de yapılabilir fakat ek completion ve retry tasarımı gerekebilir. Task timeout ve DLQ davranışı baştan belirlenmelidir. Teknoloji iş modelini gereksiz yere zorlamamalıdır.

Message Routing

Bir mesajın farklı kriterlerle çeşitli consumer queue'larına yönlendirilmesi gerekiyorsa RabbitMQ exchange modeli güçlüdür. Topic veya header tabanlı routing seçenekleri kullanılabilir. Redis queue'da benzer davranış uygulama seviyesinde modellenebilir. Kafka routing daha çok topic ve partition mantığı üzerinden ilerler. Domain ihtiyaçları seçimde belirleyicidir.

Event Streaming

Event streaming sürekli event akışının saklanması ve birden fazla consumer tarafından işlenmesini ifade eder. Kafka bu model için güçlü bir mimari sunar. Consumer'lar farklı hızlarda ilerleyebilir ve kendi offset'lerini tutar. Yeni consumer geçmiş event'leri okuyabilir. Bu yetenek job queue'nun temel amacı değildir.

Replay

Replay geçmiş event'leri yeniden tüketebilme yeteneğidir. Kafka retention süresi içindeki log üzerinden bunu doğal biçimde destekler. Queue sistemlerinde completed job replay ayrı mekanizma gerektirebilir. Replay'in gerçekten ürün ihtiyacı olup olmadığı sorulmalıdır. Yan etkili consumer'lar replay sırasında özel koruma kullanmalıdır.

Throughput

Broker throughput tek başına sistem throughput'unu belirlemez. Worker'ın database veya API işlemi daha düşük kapasiteye sahip olabilir. Büyük mesaj, acknowledgement ve persistence ayarları performansı etkiler. Gerçek mesaj boyutu ve consumer iş yüküyle benchmark yapılmalıdır. En yüksek sentetik rakam mimari karar için yeterli değildir.

Operasyonel Maliyet

Her broker ayrı deployment, backup, monitoring ve upgrade ihtiyacı doğurur. Ekibin zaten işlettiği altyapı önemli avantaj sağlayabilir. Buna rağmen uygun olmayan teknolojiyi sırf mevcut diye kullanmak da uzun vadeli maliyet yaratır. Managed servis kullanımı bazı operasyon yüklerini azaltabilir. Toplam sahip olma maliyeti değerlendirilmelidir.

Message Delivery Semantics

Mesaj sistemlerinde delivery semantics bir event veya job'ın kaç kez görülebileceğine ilişkin garantileri açıklar. At-most-once, at-least-once ve exactly-once kavramları bu bağlamda kullanılır. Gerçek uygulamalarda en güvenli yaklaşım çoğu zaman duplicate mesaj beklemek ve consumer'ı idempotent tasarlamaktır. Broker garantileri database side effect'lerini otomatik olarak exactly-once yapmaz. Uçtan uca etki business transaction seviyesinde düşünülmelidir.

At-Most-Once

At-most-once mesajın en fazla bir kez teslim edilmesini hedefler. Yeniden teslim olmadığı için bazı hata durumlarında mesaj kaybı mümkündür. Telemetri gibi kaybın kabul edilebildiği özel senaryolarda uygun olabilir. Kritik finansal görevlerde genellikle yeterli değildir. İş etkisi garanti seçiminde temel kriterdir.

At-Least-Once

At-least-once mesajın en az bir kez işlenmeye çalışılmasını sağlar. Crash veya acknowledgement kaybı durumunda aynı mesaj tekrar teslim edilebilir. Bu nedenle duplicate normal sistem davranışı olarak kabul edilmelidir. Consumer idempotent tasarlanırsa tekrar teslim güvenli hale gelir. Birçok queue sisteminde bu model yaygındır.

Exactly-Once

Exactly-once ifadesi sistem sınırına göre dikkatle yorumlanmalıdır. Broker kendi log veya transaction alanında belirli garantiler sağlayabilir. Ancak consumer dış database'e veya API'ye yan etki yaptığında uçtan uca tek etki garanti etmek daha zordur. Idempotency key ve unique constraint gibi mekanizmalar burada önem kazanır. Pazarlama teriminden çok gerçek failure senaryolarına bakmak gerekir.

Uygulama Seviyesinde Exactly-Once Etki

Uygulama seviyesinde aynı event iki kez gelse bile business etkisinin bir kez oluşması hedeflenebilir. Event ID database unique constraint ile kaydedilebilir. Transaction içinde event'in daha önce işlendiği kontrol edilebilir. Aynı ödeme veya e-posta operasyonu böylece duplicate etkiden korunabilir. Bu tasarım broker redelivery davranışıyla birlikte test edilmelidir.

Duplicate Mesajlara Hazırlıklı Olmak

Dağıtık sistemlerde duplicate mesaj tamamen sıra dışı bir durum değildir. Consumer acknowledgement gönderdikten hemen önce bağlantı kopabilir ve broker mesajı tekrar teslim edebilir. Bu nedenle handler'ın ikinci çalışması veri bozmamalıdır. Idempotency key, processed event table veya business unique constraint kullanılabilir. Duplicate testi production öncesi senaryolara eklenmelidir.

Idempotency Nedir?

Idempotency aynı işlemin birden fazla kez uygulanmasının business sonucunu bir kez uygulanmış gibi tutmasını amaçlar. Queue ve event sistemlerinde retry nedeniyle aynı görev tekrar çalışabilir. Idempotent handler duplicate teslimi güvenli hale getirir. Bunun için deterministic key, database constraint veya processed event kaydı kullanılabilir. Her operasyon doğal olarak idempotent olmadığı için tasarım açık biçimde yapılmalıdır.

Aynı Job'ın Tekrar Çalışması

Worker job'ı tamamlayıp ack göndermeden crash olabilir. Queue aynı job'ı başka worker'a yeniden teslim edebilir. Bu nedenle sistem tekrar çalışmayı beklenen durum olarak görmelidir. Aynı invoice iki kez oluşturulmamalıdır. Job ID veya business key bu korumayı sağlayabilir.

Aynı Sonucun Üretilmesi

Idempotent operasyon tekrar çalıştığında state üzerinde ek yan etki oluşturmamalıdır. Örneğin kullanıcı ayarını belirli değere set etmek doğal olarak daha idempotenttir. Bakiye değerine 100 eklemek ise tekrar çalıştığında farklı sonuç üretir. Böyle işlemlerde transaction ve unique operation ID gerekir. Business semantiği teknik çözümü belirler.

Idempotency Key

Idempotency key bir business operasyonunu benzersiz biçimde tanımlar. Aynı key ile gelen tekrar istek veya job mevcut sonuca bağlanabilir. Key kullanıcıdan gelebilir veya sistem tarafından deterministik üretilebilir. Saklama süresi işlem türüne göre belirlenmelidir. Key tahmin edilebilir olsa bile authorization kontrolleri atlanmamalıdır.

Database Unique Constraint

Unique constraint duplicate işlemi application-level kontrolünden daha güçlü biçimde engelleyebilir. İki worker aynı anda aynı event'i işlemeye çalışsa bile database yalnızca bir kayda izin verir. Bu yaklaşım race condition riskini azaltır. Constraint error beklenen idempotency sonucu olarak yorumlanabilir. Business anahtarı doğru seçilmelidir.

Processed Event Table

Processed event table tüketilen event ID'lerini saklayabilir. Consumer transaction içinde önce event'in işlenip işlenmediğini kontrol eder. İşlem ve event kaydı aynı transaction içinde tutulursa güvenilirlik artar. Tablo büyüdükçe retention ve indeks stratejisi gerekir. Her event'i sonsuza kadar saklamak zorunlu değildir.

Job Deduplication Nasıl Yapılır?

Job deduplication aynı business görevinin queue'ya birden fazla kez eklenmesini veya işlenmesini azaltmayı amaçlar. Deterministic job ID veya event ID sık kullanılan yöntemlerdir. Queue seviyesindeki deduplication tek başına database side effect'lerini korumaya yetmeyebilir. Consumer yine idempotent olmalıdır. Deduplication penceresi ve completed job davranışı açıkça belirlenmelidir.

Deterministic Job ID

Job ID rastgele yerine business anahtarından türetilebilir. Örneğin belirli rapor tarihi ve müşteri ID'si aynı job kimliğini oluşturabilir. Queue aynı ID'yi ikinci kez kabul etmeyebilir veya mevcut job'a bağlayabilir. Bu davranış kullanılan teknolojinin kurallarına bağlıdır. Job tamamlandıktan sonra ID'nin ne kadar süre korunduğu önemlidir.

Event ID

Her domain event benzersiz event ID taşımalıdır. Consumer bu ID üzerinden duplicate teslimi tespit edebilir. Event ID log ve trace korelasyonu için de kullanışlıdır. Yeniden yayınlanan aynı business event yeni ID almamalıdır. Yeni olay gerçekten farklıysa yeni ID oluşturulmalıdır.

Database Constraint

Database unique constraint deduplication için yarış koşullarına dayanıklı mekanizmadır. Aynı job iki worker tarafından aynı anda işlenirse yalnızca bir business kayıt oluşturulabilir. Uygulama unique violation durumunu kontrollü biçimde ele almalıdır. Transaction sınırı diğer side effect'lerle uyumlu olmalıdır. Harici API çağrıları için ek idempotency key gerekebilir.

Deduplication Window

Bazı sistemlerde duplicate yalnızca belirli zaman penceresinde engellenmelidir. Örneğin aynı webhook beş dakika içinde tekrar gelirse duplicate kabul edilebilir. Pencere çok kısa olursa geç retry korunmaz. Çok uzun olursa geçerli yeni işlem yanlışlıkla engellenebilir. Business semantiği doğru süreyi belirler.

Completed Job Deduplication

Completed job kayıtları temizlendiğinde aynı job ID yeniden kullanılabilir hale gelebilir. Bu davranış queue teknolojisine göre değişir. Kritik idempotency yalnızca queue geçmişine bağlı bırakılmamalıdır. Business database üzerinde kalıcı veya yeterli süreli koruma bulunmalıdır. Cleanup politikası deduplication garantisini etkilememelidir.

Retry Stratejisi Nasıl Tasarlanır?

Retry geçici hatalardan otomatik toparlanmayı sağlar fakat yanlış kullanıldığında sistemi daha da zorlayabilir. İlk adım hatanın gerçekten retry edilebilir olup olmadığını sınıflandırmaktır. Network timeout veya geçici 503 tekrar denenebilirken validation hatası çoğu zaman tekrar edilmemelidir. Maksimum deneme sayısı ve backoff belirlenmelidir. Jitter eklemek çok sayıda worker'ın aynı anda retry yapmasını engeller.

Retry Edilebilir Hata

Geçici network kesintisi, rate limit veya kısa süreli database failover retry edilebilir hata olabilir. Bunun kesinliği kullanılan sistemin semantiğine bağlıdır. Timeout sonrası side effect gerçekleşmiş olabilir ve tekrar işlem duplicate yaratabilir. Idempotency bu nedenle retry ile birlikte tasarlanmalıdır. Retry öncesinde kalan deadline da kontrol edilmelidir.

Retry Edilmemesi Gereken Hata

Geçersiz input, unsupported schema veya kalıcı authorization hatası retry ile düzelmez. Bu job'ı yüz kez yeniden çalıştırmak kaynak israfıdır. Hata doğrudan failed veya DLQ durumuna taşınabilir. Operasyon ekibi nedenini inceleyebilir. Hata sınıflandırması error code üzerinden standartlaştırılabilir.

Maximum Attempts

Her retry politikasında üst sınır olmalıdır. Sonsuz retry poison message'ın queue'yu sürekli meşgul etmesine neden olur. Maksimum sayı işin önemine ve dış servisin recovery süresine göre seçilir. Son denemeden sonra DLQ veya manual review devreye girer. Kullanıcıya gerektiğinde başarısızlık bildirimi gönderilmelidir.

Fixed Backoff

Fixed backoff her retry arasında sabit süre bekler. Basit ve tahmin edilebilir bir modeldir. Kısa süreli nadir hatalarda yeterli olabilir. Büyük outage sırasında binlerce job aynı aralıkla tekrar deneyerek dalga oluşturabilir. Bu durumda exponential backoff ve jitter daha güvenli olabilir.

Exponential Backoff

Exponential backoff her başarısız denemeden sonra bekleme süresini artırır. Downstream sistemin toparlanması için daha fazla zaman verir. Aynı zamanda retry trafiğini kademeli azaltır. Maksimum backoff sınırı belirlenmelidir. Toplam job deadline'ı aşılmamalıdır.

Jitter

Jitter backoff süresine rastlantısal farklılık ekler. Böylece binlerce worker aynı saniyede yeniden istek göndermeye çalışmaz. Büyük outage sonrası recovery sırasında bu davranış özellikle değerlidir. Jitter aralığı çok genişse job latency gereksiz uzayabilir. Politikalar workload'a göre test edilmelidir.

Retry Storm Nedir?

Retry storm, downstream sistem arızalandığında çok sayıda job veya request'in benzer anda tekrar denenerek ek yük oluşturmasıdır. İlk hata dalgası ikinci bir retry dalgasına dönüşür ve servis toparlanmakta zorlanır. Exponential backoff, jitter ve circuit breaker bu davranışı azaltabilir. Producer hızı da gerektiğinde sınırlandırılmalıdır. Recovery süresi sistem dayanıklılığının önemli metriğidir.

Downstream Sistem Arızası

Database veya üçüncü taraf API tamamen erişilemez hale geldiğinde normal istekler zaten başarısız olur. Tüm başarısızlıkları saniyeler içinde tekrar denemek ek yük üretir. Hata oranı belirli eşiği geçtiğinde circuit breaker açılabilir. Queue yeni job'ları tutmaya devam edebilir. Consumer kontrollü hızda recovery denemesi yapmalıdır.

Binlerce Job'ın Aynı Anda Retry Etmesi

Aynı timeout değerine sahip binlerce job aynı anda başarısız olabilir. Sabit kısa backoff kullanıldığında hepsi birlikte tekrar başlar. Bu senkronizasyon downstream sistem üzerinde ani spike oluşturur. Jitter denemeleri zamana yayar. Queue limiter ek koruma sağlar.

Exponential Backoff

Her yeni hata sonrasında bekleme süresini büyütmek sürekli arızada istek yoğunluğunu düşürür. İlk denemeler hızlı, sonraki denemeler daha seyrek yapılabilir. Maksimum bekleme süresi belirlenmelidir. Kullanıcıya işin geciktiği bilgisi gösterilebilir. Kritik deadline aşıldığında retry sonlandırılmalıdır.

Jitter

Jitter worker'ların tekrar deneme zamanlarını birbirinden ayırır. Özellikle büyük fleet ortamında küçük rastgele farklar bile ciddi etki yaratır. Tam, eşit veya decorrelated jitter gibi farklı yöntemler kullanılabilir. Uygulama için en basit ve yeterli model tercih edilmelidir. Dağılım load test ile gözlemlenebilir.

Circuit Breaker

Circuit breaker sürekli başarısız downstream çağrılarını geçici olarak durdurur. Sistem belirli süre sonra sınırlı denemelerle recovery durumunu kontrol edebilir. Böylece zaten sorunlu servise sürekli yük gönderilmez. Queue job'ları bu sırada bekleyebilir. Circuit breaker state'i observability içinde görünür olmalıdır.

Rate Limit

Retry trafiği normal trafikle birlikte downstream rate limit'i aşabilir. Bu nedenle limiter retry isteklerini de kapsamalıdır. Bazı sistemlerde retry için ayrı daha düşük kota ayrılabilir. 429 yanıtındaki Retry-After bilgisi varsa dikkate alınmalıdır. Amaç en hızlı yeniden denemek değil, başarılı olma ihtimali yüksek zamanda denemektir.

Dead Letter Queue Nedir?

Dead Letter Queue, normal retry politikasını aşan veya işlenemeyen mesajların ayrı alanda tutulmasını sağlar. Bu mesajlar üretim queue'sunu sürekli meşgul etmek yerine inceleme için ayrılır. DLQ operasyon ekibine görünür olmalı ve alarm üretmelidir. Replay kontrollü yapılmalı ve aynı hatanın tekrar oluşmayacağı doğrulanmalıdır. DLQ hiçbir zaman başarısız mesajları unutma alanına dönüşmemelidir.

Retry Limitini Aşan Job

Job maksimum attempts sayısını tamamladığında normal queue'da tekrar denenmemelidir. DLQ veya failed state'e taşınabilir. Son hata, deneme sayısı ve correlation ID saklanmalıdır. Kullanıcı etkisi varsa ayrı bildirim mekanizması çalışabilir. Sorun çözüldükten sonra replay yapılabilir.

Poison Message

Poison message her denemede aynı şekilde başarısız olan mesajdır. Bozuk schema veya geçersiz business veri buna neden olabilir. Sonsuz retry diğer sağlıklı mesajların throughput'unu düşürür. Mesaj hızlı biçimde quarantine edilmelidir. Kök neden analizi sonrasında producer veya consumer düzeltilmelidir.

Failed Payload

DLQ kaydı başarısız payload hakkında inceleme için yeterli bilgi taşımalıdır. Ancak secret veya hassas kişisel veriler gereksiz yere saklanmamalıdır. Payload büyükse object storage referansı tutulabilir. Schema version debugging için değerlidir. Saklama süresi veri politikalarıyla uyumlu olmalıdır.

Manuel İnceleme

Bazı kalıcı hatalar otomatik olarak çözülemez. Operasyon ekibi DLQ mesajını, error code'u ve ilgili trace'i inceleyebilir. Dashboard yalnızca ham JSON göstermek yerine temel bağlamı sunmalıdır. Düzeltme sonrası kontrollü replay yapılabilir. Manuel işlem audit kaydı bırakmalıdır.

Replay

DLQ mesajını tekrar normal queue'ya göndermek replay olarak düşünülebilir. Replay öncesinde hatanın kök nedeni giderilmiş olmalıdır. Büyük DLQ'nun tamamını aynı anda göndermek yeni overload oluşturabilir. Batch ve rate limit kullanılmalıdır. Consumer idempotency replay güvenliği için zorunludur.

DLQ Alert

DLQ'ya ilk mesaj geldiğinde veya sayı belirli eşiği aştığında alarm üretilebilir. Sadece toplam sayı değil oldest message age de önemlidir. Haftalarca bekleyen mesaj operasyon borcuna dönüşür. Alert doğru ekip ownership'ine yönlendirilmelidir. Runbook inceleme ve replay adımlarını açıklamalıdır.

Poison Message Nedir?

Poison message yapısal veya business bir hata nedeniyle her işlendiğinde başarısız olan mesajdır. Retry sayısını artırmak bu mesajı düzeltmez. Aksine worker kapasitesini tüketir ve queue throughput'unu düşürür. Mesaj belirli denemeden sonra quarantine veya DLQ alanına taşınmalıdır. Producer validation ve schema versioning poison message sayısını azaltabilir.

Her Denemede Başarısız Olan Payload

Aynı payload deterministik biçimde aynı kod yolunda hata veriyorsa geçici hata değildir. Hata stack'i ve validation sonucu kaydedilmelidir. Retry hızlı biçimde durdurulmalıdır. Payload'ın kimliği duplicate analizine yardımcı olabilir. Düzeltme sonrası kontrollü replay yapılabilir.

Validation Error

Schema veya business validation hatası çoğu durumda retry edilmemelidir. Aynı veri tekrar gönderildiğinde sonuç değişmeyecektir. Producer tarafında validation eklemek hatayı daha erken yakalar. Consumer yine savunmacı validation yapmalıdır. Hatalı mesaj DLQ'ya açıklayıcı reason code ile gönderilebilir.

Unsupported Schema

Eski worker yeni producer'ın schema sürümünü anlayamayabilir. Bu durum rolling deployment sırasında ortaya çıkabilir. Backward compatible payload tasarımı riski azaltır. Unsupported version durumunda mesaj sonsuz retry edilmemelidir. Deployment sırası ve version negotiation planlanmalıdır.

Infinite Retry'ı Önlemek

Her queue işinde maksimum retry sayısı belirlenmelidir. Poison message aksi halde sürekli yeniden alınır ve CPU ile broker kapasitesi tüketir. Exponential backoff yalnızca sorunu yavaşlatır, çözmez. Kalıcı hata sınıflandırması yapılmalıdır. DLQ son durak değil, inceleme sürecinin başlangıcıdır.

Quarantine

Quarantine sorunlu mesajları normal iş akışından ayırır. Bu alan DLQ, özel database tablosu veya ayrı storage olabilir. Amaç sağlıklı mesajların etkilenmeden devam etmesini sağlamaktır. Quarantine verisi güvenli erişim politikasıyla korunmalıdır. Replay yalnızca yetkili operasyon akışı üzerinden yapılmalıdır.

Transactional Outbox Pattern

Transactional Outbox, business database değişikliği ile event yayınlama ihtiyacı arasındaki dual-write problemini azaltan bir desendir. Uygulama business kaydı ve outbox kaydını aynı database transaction içinde yazar. Ayrı publisher outbox kayıtlarını broker'a gönderir. Event başarılı yayımlandıktan sonra kayıt işlenmiş olarak işaretlenebilir. Consumer tarafında duplicate yayın ihtimali nedeniyle idempotency yine gereklidir.

Database Update

Business işlem önce database üzerinde gerekli değişikliği yapar. Sipariş durumu veya ödeme kaydı buna örnek olabilir. Event yayınlama aynı kod yolunda doğrudan broker'a yapılırsa iki sistem arasında atomiklik sorunu oluşur. Outbox bu event niyetini database'e kaydeder. Böylece commit ile event kaydı aynı transaction sınırında olur.

Outbox Record

Outbox record event type, event ID, payload ve oluşturulma zamanı gibi alanlar taşıyabilir. Payload schema version eklemek future compatibility sağlar. Kayıt producer tarafından daha sonra okunur. Büyük payload yerine minimal event bilgisi tercih edilmelidir. Outbox tablosu düzenli cleanup veya partition stratejisi gerektirebilir.

Aynı Transaction

Business değişikliği ile outbox insert aynı database transaction içinde yapılır. Transaction commit olursa ikisi de görünür hale gelir. Rollback olursa event kaydı da oluşmaz. Bu yaklaşım DB başarılı, event kayıp problemine karşı güçlü koruma sağlar. Broker publish işlemi transaction dışındaki ayrı aşamada yapılır.

Publisher

Publisher outbox tablosundaki gönderilmemiş kayıtları okuyup broker'a yayınlar. Publish başarıyla doğrulandıktan sonra kayıt sent olarak işaretlenebilir. Publisher crash anında duplicate publish oluşabilir. Bu nedenle event ID ve idempotent consumer gereklidir. Polling veya CDC tabanlı farklı publisher modelleri kullanılabilir.

Queue/Event Broker

Outbox publisher event'i RabbitMQ, Kafka veya başka broker'a gönderebilir. Pattern belirli broker teknolojisine bağlı değildir. Broker'ın acknowledgement davranışı publisher tasarımını etkiler. Publish başarılı olup database sent işareti yazılmadan crash oluşabilir. Duplicate event ihtimali bu yüzden normal kabul edilmelidir.

Event ID

Her outbox event benzersiz event ID taşımalıdır. Retry publish aynı ID'yi korumalıdır. Consumer processed event tablosu veya unique constraint ile duplicate'i tanıyabilir. Event ID tracing için de kullanılabilir. Yeni business olayı gerçekleşmedikçe ID değiştirilmemelidir.

Idempotent Consumer

Outbox pattern publish kaybını azaltır fakat duplicate publish ihtimalini tamamen ortadan kaldırmaz. Consumer aynı event'i ikinci kez güvenli biçimde işlemelidir. Unique constraint veya processed event kaydı bunun için kullanılabilir. Harici yan etkiler idempotency key destekliyorsa aynı event ID ile çağrılabilir. Uçtan uca tutarlılık producer ve consumer birlikte tasarlandığında sağlanır.

Database Commit ile Queue Publish Arasındaki Tutarlılık

Bir işlem hem database state değiştirecek hem de queue mesajı yayınlayacaksa iki farklı sistem arasında atomiklik problemi ortaya çıkar. Database commit olurken publish başarısız kalabilir veya tam tersi gerçekleşebilir. Bu durum dual-write problemi olarak bilinir. Transactional Outbox en yaygın çözüm desenlerinden biridir. Kritik event-driven sistemlerde bu hata senaryosu tasarım aşamasında mutlaka ele alınmalıdır.

DB Başarılı, Queue Başarısız

Database değişikliği commit olduktan sonra broker erişilemez hale gelebilir. Business state güncellenmiştir fakat diğer servisler event'i hiç alamaz. Basit retry process crash durumunda kaybolabilir. Outbox kaydı database içinde durduğu için publisher daha sonra tekrar deneyebilir. Bu şekilde event niyeti kalıcı hale gelir.

Queue Başarılı, DB Rollback

Mesaj önce broker'a yayınlanıp sonra database transaction rollback olursa consumer gerçekte oluşmamış state için event alabilir. Bu hata daha zor telafi edilebilir. Publish'i commit sonrasına bırakmak ilk problemi oluşturur. Bu nedenle doğrudan dual-write güvenli değildir. Outbox iki sonucu aynı database transaction altında kaydeder.

Dual-Write Problemi

Dual-write iki bağımsız storage sistemine tek business işleminde yazma ihtiyacıdır. Dağıtık transaction kullanılmadığında iki yazım arasında hata penceresi bulunur. Basit try/catch bu problemi tamamen çözmez. Crash her iki çağrı arasındaki herhangi bir anda olabilir. Outbox gibi desenler eventual publish garantisi sağlar.

Transactional Outbox

Transactional Outbox event kaydını business database değişikliğiyle aynı transaction içine alır. Broker publish daha sonra güvenilir worker tarafından yapılır. Duplicate publish idempotent consumer ile güvenli hale getirilir. Pattern ek tablo ve publisher operasyonu gerektirir. Kritik tutarlılık karşılığında bu maliyet çoğu sistemde kabul edilebilir.

Fan-Out Pattern

Fan-out tek bir event'in birden fazla bağımsız consumer veya iş akışını tetiklemesini sağlar. Sipariş oluşturulduğunda notification, analytics ve search index güncellemesi ayrı consumer'lar tarafından yapılabilir. Producer bu servislerin hepsini doğrudan çağırmak zorunda kalmaz. Event broker bağımlılığı gevşetir. Her consumer kendi retry ve idempotency politikasını yönetmelidir.

Bir Event

Fan-out tek bir domain event ile başlar. Event geçmişte gerçekleşmiş bir business gerçeğini ifade etmelidir. İsimlendirme ve schema açık olmalıdır. Event ID duplicate kontrolü sağlar. Producer hangi consumer'ların bulunduğunu bilmek zorunda değildir.

Birden Fazla Consumer

Farklı consumer'lar aynı event'i kendi amaçları için işleyebilir. Bir consumer başarısız olduğunda diğerlerinin başarı durumunu etkilememelidir. Kafka consumer groups veya RabbitMQ ayrı queue bindings bu modeli sağlayabilir. Her consumer bağımsız ölçeklenebilir. Ortak event schema backward compatible tutulmalıdır.

Bağımsız İşlemler

Fan-out görevleri gerçekten bağımsız olmalıdır. Bir consumer'ın çıktısı diğerinin zorunlu girdisiyse workflow veya dependency graph daha uygun olabilir. Bağımsızlık failure isolation sağlar. Analytics hatası kullanıcı notification'ını durdurmamalıdır. İş sınırları domain tasarımında netleştirilmelidir.

Notification

Notification consumer event üzerinden e-posta, push veya başka bildirim oluşturabilir. Gönderim başarısızsa kendi retry politikasını uygular. Ana business transaction bundan etkilenmez. Duplicate event iki bildirim oluşturmamalıdır. Event ID idempotency anahtarı olarak kullanılabilir.

Analytics

Analytics consumer business event'leri raporlama veya data platformuna taşıyabilir. Bu consumer genellikle kullanıcı response yolunda olmamalıdır. Event replay yeni model oluşturmak için yararlı olabilir. Hatalı analytics işlemi ana sipariş akışını durdurmaz. Schema değişiklikleri geçmiş event replay davranışıyla uyumlu olmalıdır.

Search Index

Search index consumer event ile ilgili dokümanı güncelleyebilir. Arama sistemi geçici olarak kapalıysa job retry edilebilir. Ana database source of truth olmaya devam eder. Gerekirse tüm indeks yeniden event veya database üzerinden oluşturulabilir. Consumer idempotent update kullanmalıdır.

Fan-In Pattern

Fan-in birden fazla bağımsız alt görevin sonuçlarının tek bir aggregate sonuç için birleştirilmesidir. Büyük raporu farklı parçalara bölüp paralel hesaplamak buna örnektir. Her alt görevin completion durumu izlenmelidir. Timeout ve partial failure davranışı önceden tanımlanmalıdır. Çok uzun ve çok aşamalı işlerde workflow engine daha uygun olabilir.

Birden Fazla Alt Görev

Ana iş paralel çalışabilecek alt görevlere ayrılır. Her alt görev benzersiz ID ve parent ID taşıyabilir. Worker'lar görevleri bağımsız işler. Tamamlanma sırası önemli olmayabilir. Parent sonucu tüm gerekli parçalar hazır olduğunda birleştirilir.

Completion Tracking

Fan-in sistemi hangi alt görevlerin tamamlandığını güvenilir biçimde takip etmelidir. Sadece memory state kullanmak process restart durumunda veri kaybı yaratabilir. Database veya durable workflow state tercih edilebilir. Duplicate completion idempotent işlenmelidir. Parent'ın tamamlanma koşulu atomic biçimde değerlendirilmelidir.

Aggregate Result

Tüm alt sonuçlar hazır olduğunda aggregate aşaması çalışabilir. Sonuçlar büyükse hepsini tek worker memory'sine yüklemek riskli olabilir. Object storage veya database üzerinden kademeli birleştirme yapılabilir. Aggregate işlemi de CPU-heavy ise worker pool kullanılabilir. Final state yalnızca bir kez complete olmalıdır.

Timeout

Bir alt görev hiç tamamlanmazsa parent sonsuza kadar beklememelidir. Workflow için genel deadline tanımlanmalıdır. Süre dolduğunda kalan görevler iptal edilebilir veya failed kabul edilebilir. Timeout sonrası geç sonuç gelirse nasıl davranılacağı belirlenmelidir. State transition idempotent olmalıdır.

Partial Failure

Bazı ürünlerde eksik alt sonuçlarla partial aggregate kabul edilebilir. Diğerlerinde tek görev hatası tüm workflow'u başarısız yapar. Bu karar teknik değil business gereksinimidir. Kullanıcıya eksik verinin açıkça gösterilmesi gerekebilir. Retry yalnızca başarısız alt görevler için uygulanabilir.

Job Dependency Graph

Job dependency graph bir işin birden fazla aşama ve bağımlılık üzerinden ilerlemesini modeller. Parent ve child job'lar paralel branch'ler oluşturabilir ve belirli noktada join yapılabilir. Basit iki aşamalı iş queue üzerinde rahatça yönetilebilir. Çok sayıda koşul, retry ve uzun süreli state varsa workflow engine ihtiyacı doğabilir. Graph tasarımı observability ve replay davranışını da kapsamalıdır.

Parent Job

Parent job genel workflow veya ana business görevi temsil eder. Alt işlerin kimliklerini ve aggregate durumunu takip edebilir. Parent'ı tek worker'ın sürekli açık tutması gerekmez. State durable storage içinde tutulabilir. Tüm child koşulları tamamlandığında sonraki aşama tetiklenir.

Child Jobs

Child job'lar ana görevin bağımsız veya bağımlı parçalarıdır. Her biri kendi retry politikasına sahip olabilir. Parent ID tracing ve completion tracking için taşınabilir. Bir child duplicate çalışırsa final state bozulmamalıdır. Sonuç boyutu büyükse referans olarak saklanabilir.

DAG

Directed Acyclic Graph görevlerin bağımlılık yönünü döngüsüz biçimde ifade eder. ETL ve data pipeline süreçlerinde sık kullanılır. Aynı seviyedeki işler paralel çalışabilir. Bağımlı aşama gerekli parent sonuçları gelmeden başlamamalıdır. Graph çok büyüdüğünde özel workflow platformu yönetimi kolaylaştırabilir.

Parallel Branches

Bir workflow içindeki bağımsız branch'ler aynı anda çalıştırılabilir. Bu toplam işlem süresini azaltır. Her branch farklı kaynak kullanıyorsa concurrency limitleri ayrı olabilir. Aynı database'e yük bindiren branch'ler merkezi limiter paylaşabilir. Join aşaması tamamlanma durumunu güvenilir şekilde izlemelidir.

Join

Join birden fazla branch tamamlandıktan sonra ortak sonraki adımın çalışmasıdır. Duplicate completion event'leri join'i iki kez tetiklememelidir. Atomic state transition bu riski azaltır. Partial failure politikası join koşuluna dahil edilmelidir. Timeout halinde fallback davranışı tanımlanabilir.

Workflow Engine Gerektiren Durumlar

Workflow günler sürüyor, çok sayıda branch içeriyor ve insan onayı gibi dış olaylar bekliyorsa basit queue kodu büyüyebilir. Durable timer, state machine ve replay ihtiyacı workflow engine kullanımını gerekçelendirebilir. Kısa birkaç job zinciri için bu altyapı gereksiz olabilir. İşin yaşam süresi ve failure state sayısı önemli göstergelerdir. En basit yeterli mekanizma tercih edilmelidir.

Queue Backpressure Nasıl Yönetilir?

Queue backpressure producer hızının consumer kapasitesini sürekli aşmasını önlemeye yönelik sistem tasarımıdır. Queue geçici burst'leri absorbe edebilir fakat sonsuz büyüyen backlog çözüm değildir. Queue depth, consumer lag ve oldest job age birlikte izlenmelidir. Consumer kapasitesi artırılabilir veya producer tarafında rate limit uygulanabilir. Sistem kritik olmayan yükte load shedding yapabilmelidir.

Queue Depth

Queue depth bekleyen toplam job sayısını gösterir. Tek başına yüksek sayı her zaman sorun değildir çünkü job'lar çok kısa olabilir. Oldest job age ve processing rate ile birlikte yorumlanmalıdır. Sürekli yükselen depth üretim ve tüketim hızlarının dengesiz olduğunu gösterir. Alarm eşiği business SLA ile ilişkilendirilmelidir.

Producer Rate

Producer rate zaman biriminde queue'ya eklenen job sayısıdır. Burst trafik kısa süreli olarak consumer rate'i aşabilir. Queue bu farkı tamponlar. Uzun süreli aşımda backlog büyümeye devam eder. Rate limiter veya admission control gerekebilir.

Consumer Rate

Consumer rate worker'ların zaman biriminde tamamladığı iş miktarıdır. Worker sayısı, concurrency ve downstream kapasitesi bu değeri belirler. Hata ve retry oranı efektif throughput'u düşürür. Sadece worker sayısını artırmak downstream saturation yaratabilir. İşlem süresi dağılımı izlenmelidir.

Consumer Lag

Consumer lag üretilen iş ile tüketilen iş arasındaki gecikmeyi gösterir. Kafka'da offset farkı üzerinden, job queue'da bekleme yaşı üzerinden değerlendirilebilir. Lag yükseliyorsa sistem gerçek zaman hedefinden uzaklaşıyor demektir. Burst sonrası lag'in ne kadar sürede toparlandığı önemlidir. Recovery time kapasite planlamasının parçasıdır.

Concurrency Artırmak

Downstream kapasitesi varsa consumer concurrency artırılarak queue daha hızlı boşaltılabilir. CPU-heavy işlerde process başına concurrency yerine daha fazla worker instance gerekebilir. Database pool ve API limitleri üst sınır oluşturur. Concurrency artışı kademeli yapılmalıdır. Hata oranı yükselmeye başladığında geri çekilmelidir.

Producer'ı Yavaşlatmak

Consumer kapasitesi artırılamıyorsa producer hızını sınırlamak gerekir. API seviyesinde rate limit veya queue admission control uygulanabilir. Batch producer daha küçük hızla ilerleyebilir. Harici event kaynağında pause mekanizması varsa kullanılabilir. Amaç backlog'un kontrol dışına çıkmasını önlemektir.

Load Shedding

Sistem kritik kapasite sınırına ulaştığında düşük öncelikli işleri geçici olarak reddetmek genel sağlığı koruyabilir. Her isteği kabul edip saatlerce queue'da bekletmek kullanıcı açısından daha kötü olabilir. 429 veya 503 gibi kontrollü cevaplar verilebilir. Kritik ve düşük öncelikli workload ayrılabilir. Load shedding politikası ürün ekibiyle birlikte belirlenmelidir.

Rate Limiting Asenkron Processing'de Neden Önemlidir?

Asenkron sistemler çok sayıda işi queue'ya alabildiği için downstream kapasitesini görünmez biçimde aşabilir. Rate limiting işlerin belirli zaman aralığında ne hızla çalışacağını sınırlar. Bu mekanizma concurrency limitinden farklıdır ve birlikte kullanılabilir. Third-party API, database veya tenant kotası rate limit ihtiyacı oluşturabilir. Token bucket ve leaky bucket yaygın algoritmik yaklaşımlardır.

Third-Party API Limitleri

Dış servis saniyede belirli çağrı sayısına izin verebilir. Worker sayısı artırıldığında bu limit kolayca aşılır. Queue-level limiter tüm worker'lar arasında ortak hız sınırı sağlayabilir. 429 yanıtı ayrıca dinamik sinyal olarak kullanılabilir. Rate limit sözleşmesi üretim konfigürasyonunda merkezi tutulmalıdır.

Database Capacity

Database teorik olarak daha fazla bağlantı kabul etse bile sorgu throughput sınırı vardır. Çok sayıda queue worker aynı tabloda ağır sorgu çalıştırabilir. Rate limit veya concurrency limit database'i korur. Connection pool tek başına yeterli admission control olmayabilir. Lock wait ve query latency de takip edilmelidir.

Tenant Quota

Multi-tenant sistemde her müşterinin kullanım kotası farklı olabilir. Tenant başına rate limit bir müşterinin tüm worker kapasitesini tüketmesini önler. Premium tenant için daha yüksek limit tanımlanabilir. Kota anahtarının güvenli tenant context'ten geldiği doğrulanmalıdır. Kullanıcının payload içinde gönderdiği tenant ID'ye tek başına güvenilmemelidir.

Queue-Level Limiter

Queue-level limiter job processing hızını worker instance'larından bağımsız kontrol edebilir. Horizontal scaling yapıldığında global limitin korunması önemlidir. Dağıtık counter veya broker özelliği kullanılabilir. Limiter arızası durumunda fail-open veya fail-closed davranışı belirlenmelidir. Kritik downstream sistemlerde fail-closed daha güvenli olabilir.

Token Bucket

Token bucket belirli hızda token üretir ve kısa burst'lere izin verebilir. Her iş bir veya daha fazla token tüketir. Bucket kapasitesi maksimum burst büyüklüğünü belirler. API rate limit'leri için esnek bir modeldir. Dağıtık uygulamada token state'in tutarlı saklanması gerekir.

Leaky Bucket

Leaky bucket işleri daha düzenli sabit hızla akıtmayı hedefler. Ani burst queue içinde birikir ve kontrollü hızda tüketilir. Downstream sistemin çok düzenli trafik istediği durumlarda faydalıdır. Queue uzunluğu için üst sınır gerekir. Aksi halde yüksek giriş hızı yalnızca gecikmeyi büyütür.

Multi-Tenant Async Processing

Multi-tenant background processing tek bir müşterinin diğerlerinin kapasitesini tüketmesini önleyecek adalet mekanizmaları gerektirir. Job payload güvenilir tenant context taşımalıdır. Tenant başına concurrency ve rate limit uygulanabilir. Büyük müşteriler için ayrı worker veya queue izolasyonu gerekebilir. Fair scheduling ve noisy neighbor kontrolleri platform davranışını daha öngörülebilir hale getirir.

Tenant ID Job Payload'ında

Job'ın hangi tenant'a ait olduğu güvenilir metadata ile belirlenmelidir. Tenant ID yalnızca kullanıcı tarafından gelen ham payload'dan alınmamalıdır. Producer authorization sonrası doğrulanmış context'i job'a eklemelidir. Worker tüm database sorgularında bu context'i korumalıdır. Log ve trace içinde tenant bilgisi hassasiyet kurallarına uygun kullanılmalıdır.

Tenant Context

Tenant context job boyunca authorization ve veri izolasyonunda kullanılır. AsyncLocalStorage gibi araçlar request context taşımada yardımcı olabilir. Background job yeni execution context başlattığı için context açıkça kurulmalıdır. Yanlış tenant ile sorgu çalıştırmak ciddi güvenlik problemidir. Testler tenant izolasyonunu özellikle doğrulamalıdır.

Tenant Başına Concurrency

Global concurrency limiti tek büyük tenant'ın tüm slotları doldurmasını engellemeyebilir. Tenant başına ek limit adalet sağlar. Örneğin toplam 100 worker slotunun tek tenant tarafından 100'ünün kullanılması sınırlandırılabilir. Premium planlar farklı limit alabilir. Scheduler fairness bu limitlerle uyumlu olmalıdır.

Tenant Başına Rate Limit

Rate limit tenant'ın belirli süre içinde ne kadar iş üretebileceğini veya işletebileceğini sınırlar. Bu yaklaşım kötü kullanım ve beklenmeyen spike riskini azaltır. Limitler ürün planlarıyla ilişkilendirilebilir. Dağıtık worker'lar ortak limiter state kullanmalıdır. Kullanıcıya limit aşıldığında açık geri bildirim verilmelidir.

Whale Tenant

Whale tenant sistemde diğer müşterilerden çok daha yüksek trafik üreten büyük müşteriyi ifade eder. Bu müşterinin workload'u genel queue SLA'sını bozabilir. Ayrı queue, dedicated worker veya weighted scheduling çözüm olabilir. İzolasyon maliyeti müşteri değeri ve sistem kapasitesine göre değerlendirilmelidir. Gözlem olmadan tüm tenant'ları aynı varsayımla yönetmek risklidir.

Fair Scheduling

Fair scheduling worker kapasitesinin tenant'lar arasında dengeli dağıtılmasını hedefler. Round-robin, weighted queue veya token tabanlı yaklaşımlar kullanılabilir. Amaç küçük müşterinin büyük backlog arkasında saatlerce beklememesidir. Priority politikası fairness ile çelişebilir. SLA sınıfları açık biçimde tanımlanmalıdır.

Noisy Neighbor

Noisy neighbor tek tenant'ın aşırı kaynak kullanımı nedeniyle diğer tenant'ların performansını düşürmesidir. CPU, database connection veya queue worker kapasitesi etkilenebilir. Tenant bazlı metrikler problemi görünür hale getirir. Rate limit ve concurrency isolation koruma sağlar. Kritik müşteriler için fiziksel veya mantıksal izolasyon gerekebilir.

Priority Queue Kullanımı

Priority queue kritik job'ların normal veya düşük öncelikli işlerden önce işlenmesini sağlayabilir. Bu mekanizma acil kullanıcı işlemleri ile toplu arka plan görevlerini aynı altyapıda ayırmaya yardımcı olur. Ancak sürekli yüksek priority trafik düşük priority işlerde starvation yaratabilir. Aging veya ayrı queue tasarımı bu sorunu azaltır. Öncelik business SLA ile ilişkilendirilmelidir.

High Priority

High priority job kullanıcıyı doğrudan etkileyen veya kısa SLA'ya sahip görev olabilir. Worker bu işleri daha önce alır. Her görevi high priority işaretlemek sistemi anlamsız hale getirir. Priority kullanımı merkezi kurallarla sınırlandırılmalıdır. Yüksek öncelikli queue için kapasite rezervi düşünülebilir.

Normal Priority

Normal priority sistemdeki standart background görevlerin varsayılan sınıfı olabilir. Çoğu iş burada çalışmalıdır. Böylece high priority gerçekten istisna olarak kalır. Normal queue için throughput ve oldest job age izlenir. SLA ihlali yaklaşınca otomatik kapasite artırılabilir.

Low Priority

Low priority analytics backfill veya cache warmup gibi bekleyebilen işler için uygundur. Sistem yüksek yükte bu işleri yavaşlatabilir veya geçici olarak durdurabilir. Bu yaklaşım kritik kapasiteyi korur. Ancak düşük öncelikli işler sonsuza kadar beklememelidir. Maksimum bekleme süresi tanımlanmalıdır.

Priority Starvation

High priority işlerin sürekli gelmesi düşük priority queue'nun hiç ilerleyememesine neden olabilir. Buna priority starvation denir. Ayrı worker kotası veya aging bu riski azaltır. Dashboard priority sınıfına göre oldest job age göstermelidir. Sistem adaleti performans kadar izlenmelidir.

Aging Strategy

Aging uzun süre bekleyen düşük priority job'ın zamanla daha yüksek öncelik kazanmasını sağlar. Böylece starvation azaltılır. Artış kuralı basit ve tahmin edilebilir olmalıdır. Çok agresif aging öncelik sınıflarını anlamsızlaştırabilir. Business SLA'lara göre eşikler belirlenebilir.

Scheduled ve Delayed Job'lar

Scheduled ve delayed job'lar görevin hemen değil, belirli zamanda veya gecikme sonrasında çalışmasını sağlar. Hatırlatma, retry ve periyodik görevler bu modele uyar. Distributed ortamda plan state'inin kalıcı tutulması önemlidir. Time zone ve DST değişimleri özellikle kullanıcı takvimine bağlı görevlerde dikkat gerektirir. Planlanan zaman ile gerçek çalışma zamanı arasındaki fark gözlemlenmelidir.

Belirli Zamanda Çalışma

Bir job belirli tarih ve saatte çalışacak biçimde planlanabilir. Gerçek çalışma zamanı worker kapasitesi nedeniyle biraz gecikebilir. Kesin zaman garantisi gerekiyorsa sistem SLA'sı ayrıca tasarlanmalıdır. Kullanıcının timezone bilgisi saklanabilir. UTC storage birçok durumda hesaplamayı sadeleştirir.

Delay

Delay mevcut andan belirli süre sonra job'ın hazır hale gelmesini sağlar. Retry backoff bunun yaygın kullanım alanıdır. Delay dolduğunda job hemen çalışmak zorunda değildir. Queue backlog varsa sırada bekleyebilir. Kullanıcıya gösterilen zaman tahmini bu farkı dikkate almalıdır.

Recurring Jobs

Recurring job belirli periyotta tekrar eden görevdir. Günlük rapor veya saatlik sync buna örnektir. Bir önceki çalışma bitmeden yenisinin başlaması istenmeyebilir. Overlap policy açıkça tanımlanmalıdır. Missed run davranışı da belirlenmelidir.

Distributed Scheduler

Birden fazla worker veya app replica bulunduğunda recurring job tek kez oluşturulmalıdır. Distributed scheduler bu koordinasyonu sağlar. Leader election, database lock veya queue özelliği kullanılabilir. Duplicate schedule oluşsa bile idempotent job ek koruma sunar. Scheduler health ayrı izlenmelidir.

Time Zone

Kullanıcı "her gün 09:00" dediğinde hangi timezone'un kastedildiği saklanmalıdır. Tüm tarihleri yalnızca server local time üzerinden yönetmek dağıtık sistemlerde sorun yaratır. UTC event timestamp için iyi varsayılandır. Kullanıcı zamanlaması için IANA timezone bilgisi ayrıca tutulabilir. Display sırasında doğru dönüşüm yapılmalıdır.

DST Problemleri

Daylight Saving Time uygulanan bölgelerde bazı yerel saatler yılda bir kez hiç oluşmayabilir veya iki kez oluşabilir. Recurring job davranışı bu durum için tanımlanmalıdır. Kullanıcı saatine göre schedule yapan sistemler DST kütüphanelerinden yararlanmalıdır. UTC sabit interval ile yerel saat schedule aynı şey değildir. Test senaryolarına DST geçiş günleri eklenmelidir.

Asenkron İşlerin Sonucu Kullanıcıya Nasıl Bildirilir?

Background job kullanıldığında kullanıcı sonucu aynı HTTP response içinde alamayabilir. Bunun yerine API job ID döndürebilir ve kullanıcı durum endpoint'ini sorgulayabilir. Daha gerçek zamanlı deneyim için webhook, Server-Sent Events, WebSocket veya notification mekanizmaları kullanılabilir. Seçim kullanıcı deneyimi ve istemci türüne bağlıdır. Sonucun ready, failed ve expired durumları açık biçimde modellenmelidir.

202 Accepted

202 Accepted isteğin kabul edildiğini fakat işlemin henüz tamamlanmadığını ifade etmek için kullanılabilir. Response içinde job ID ve status endpoint bilgisi sunulabilir. Bu durum başarıyla tamamlandı anlamına gelmez. API dokümantasyonu bunu açıkça belirtmelidir. Kullanıcı daha sonra sonucu takip edebilmelidir.

Job ID

Job ID uzun süren işlemin takip anahtarıdır. Tahmin edilebilir ID kullanılıyorsa authorization kontrolü daha da önem kazanır. Status endpoint yalnızca ilgili kullanıcının job'ını göstermelidir. Job ID trace ve log korelasyonunda kullanılabilir. Business operation ID ile queue internal ID ayrı tutulabilir.

Polling

Polling istemcinin belirli aralıklarla status endpoint'i çağırmasıdır. Uygulaması basit ve güvenilirdir. Çok sık polling gereksiz trafik oluşturabilir. Exponential polling interval veya server tarafından önerilen retry süresi kullanılabilir. Job tamamlandığında polling durdurulmalıdır.

Webhook

Webhook server-to-server sonuç bildirimi için uygundur. Job tamamlandığında kayıtlı endpoint'e event gönderilebilir. Delivery retry ve signature doğrulaması gerekir. Aynı webhook duplicate gelebileceği için alıcı idempotent olmalıdır. Başarısız webhook'lar ayrı queue üzerinden tekrar denenebilir.

Server-Sent Events

SSE server'dan tarayıcıya tek yönlü sürekli event akışı sağlar. Progress update veya job state değişiklikleri için kullanılabilir. WebSocket'e göre daha basit ihtiyaçlarda yeterli olabilir. Bağlantı kopması ve reconnect davranışı tasarlanmalıdır. Çok uzun job'larda polling hâlâ daha basit seçenek olabilir.

WebSocket

WebSocket çift yönlü gerçek zamanlı iletişim sağlar. Job progress gibi güncellemeler anlık gönderilebilir. Connection state ve horizontal scaling ek altyapı gerektirebilir. Kullanıcı bağlantısı kapalıysa sonucun kalıcı notification kanalında saklanması gerekir. Yalnızca tek yönlü bildirim için her zaman şart değildir.

Notification

İş tamamlandığında e-posta, push veya uygulama içi notification gönderilebilir. Kullanıcı uzun işi aktif olarak takip etmek zorunda kalmaz. Notification kendi background job sistemi üzerinden gönderilebilir. Duplicate completion event iki bildirim oluşturmamalıdır. Kullanıcı tercihleri ve izinleri kontrol edilmelidir.

Uzun Süreli API İşlemleri İçin 202 Accepted Pattern

202 Accepted pattern uzun süreli işlerin HTTP request lifecycle'dan ayrılması için temiz bir API tasarımı sunar. API isteği doğrular, job oluşturur ve kullanıcıya takip kimliği döndürür. Worker işi arka planda yürütür. Status ve result endpoint'leri işin durumunu gösterir. Sonuçların ne kadar süre tutulacağı expiration politikasıyla yönetilir.

Request Al

API önce kullanıcının request'ini authentication, authorization ve schema açısından doğrular. Geçersiz isteği queue'ya atmak yerine burada reddetmek daha verimlidir. Büyük dosya varsa önce güvenli storage upload tamamlanabilir. Idempotency key duplicate request'i engelleyebilir. Request accepted olmadan önce gerekli business ön koşulları kontrol edilmelidir.

Job Oluştur

Doğrulanmış işlem için durable queue'ya job eklenir. Job payload minimal olmalıdır. Database değişikliği ile queue publish arasında tutarlılık gerekiyorsa outbox kullanılabilir. Job ID kullanıcıya döndürülecek takip anahtarıyla ilişkilendirilir. Enqueue başarısızsa 202 dönülmemelidir.

202 Dön

Job güvenli biçimde kabul edildiğinde API 202 Accepted dönebilir. Response içinde job ID ve mevcut state bulunabilir. İşin bitmiş olduğu izlenimi verilmemelidir. Retry veya tahmini bekleme bilgisi kullanıcı deneyimini iyileştirebilir. Aynı request idempotency key ile tekrar gelirse mevcut job döndürülebilir.

Status Endpoint

Status endpoint queued, running, completed veya failed gibi durumları sunabilir. Progress yüzdesi yalnızca gerçekten ölçülebiliyorsa gösterilmelidir. Authorization her sorguda uygulanmalıdır. Internal stack trace kullanıcıya döndürülmemelidir. Failure reason güvenli ve anlaşılır biçimde sunulabilir.

Result Endpoint

Completed job için result endpoint final çıktıyı veya download referansını döndürebilir. Büyük dosya için signed object storage URL daha uygun olabilir. Sonuç henüz hazır değilse açık state döndürülmelidir. Download yetkisi job sahibine göre kontrol edilmelidir. Sonuç idempotent olarak tekrar alınabilir olmalıdır.

Expiration

Job sonuçlarını sonsuza kadar saklamak storage maliyeti oluşturur. Expiration süresi ürün ihtiyacına göre belirlenmelidir. Süre dolduğunda result dosyası ve ilişkili geçici metadata temizlenebilir. Status endpoint expired durumunu açıkça gösterebilir. Regulatory retention gereksinimleri ayrıca dikkate alınmalıdır.

Graceful Shutdown Asenkron Sistemlerde Nasıl Yapılır?

Graceful shutdown process kapanmadan önce yeni iş almayı durdurup devam eden görevleri güvenli biçimde tamamlamayı amaçlar. Container ve orchestrator ortamlarında SIGTERM yaygın kapanış sinyalidir. Worker yeni job tüketimini durdurmalı ve aktif job'lara belirli grace süresi tanımalıdır. Süre aşılırsa job'ın yeniden teslim edilebilir olması gerekir. Connection ve thread kaynakları en son kontrollü biçimde kapatılmalıdır.

SIGTERM

SIGTERM uygulamaya kapanması gerektiğini bildiren işletim sistemi sinyalidir. Node.js process bu sinyali yakalayıp shutdown akışını başlatabilir. Handler içinde yalnızca process.exit çağırmak aktif işleri kesebilir. Önce readiness kapatılmalı ve yeni trafik durdurulmalıdır. Ardından worker ve connection cleanup yapılmalıdır.

Yeni Job Almayı Durdurmak

Shutdown başladığında worker queue'dan yeni görev çekmemelidir. Aksi halde kapanış süresi sürekli uzayabilir. Queue client çoğu zaman pause veya close mekanizması sunar. Aktif işlerin state'i ayrı tutulmalıdır. Yeni job almama davranışı deployment sırasında test edilmelidir.

Aktif Job'ları Tamamlamak

Devam eden görevler mümkünse belirlenen grace period içinde bitirilmelidir. İş uzun sürecekse checkpoint veya retry mekanizması gerekebilir. Job tamamlandıktan sonra acknowledgement gönderilir. İş yarıda kesilirse queue yeniden teslim edebilmelidir. Idempotency tekrar çalışmayı güvenli hale getirir.

Timeout

Graceful shutdown sonsuza kadar beklememelidir. Orchestrator process'i belirli süre sonra zorla kapatabilir. Uygulama kendi shutdown timeout değerini bu limitten biraz daha kısa seçebilir. Süre dolduğunda kalan görevler güvenli şekilde abort edilebilir. Queue lock davranışı yeniden teslimi sağlamalıdır.

Queue Connection'ı Kapatmak

Aktif işler tamamlandıktan sonra queue connection kontrollü biçimde kapatılabilir. Connection erken kapanırsa acknowledgement gönderilemeyebilir. Bu durum duplicate redelivery oluşturabilir. Kapanış sırası bu nedenle önemlidir. Error logları shutdown durumunu normal production hatasından ayırmalıdır.

Worker Termination

Worker Thread pool kullanılıyorsa aktif CPU görevlerinin tamamlanması veya iptal edilmesi gerekir. Yeni task dispatch durdurulmalıdır. Her worker termination sonrası resource serbest bırakır. Zorla terminate edilen iş tekrar yürütülebilir olmalıdır. Parent queue idempotency bu senaryoya dayanmalıdır.

Deployment Sırasında Background Job'lar Ne Olur?

Deployment yeni kodu başlatırken eski worker instance'larını kapatır. Aktif job kapanış sırasında yarıda kalabilir. Queue lock veya visibility timeout sayesinde tamamlanmayan iş başka worker tarafından tekrar alınabilir. Bu nedenle job idempotency deployment güvenilirliğinin parçasıdır. Zero-downtime hedefi yalnızca HTTP trafik değil background worker lifecycle'ını da kapsar.

Pod Termination

Kubernetes benzeri ortamlarda pod termination önce shutdown sinyali gönderir. Uygulama readiness durumunu kapatarak yeni trafik veya iş almayı durdurabilir. Aktif job grace period içinde tamamlanır. Süre sonunda process zorla sonlandırılabilir. Uzun işlerin tek seferde bitmek zorunda olmadığı tasarım daha güvenlidir.

Visibility Timeout / Lock

Queue job worker tarafından alındığında belirli süre için lock veya görünmez duruma gelebilir. Worker crash olursa süre dolduğunda job tekrar erişilebilir olur. İşlem lock süresinden uzun sürüyorsa lock renewal gerekebilir. Yanlış ayar duplicate processing oluşturabilir. Teknolojinin lock semantiği iyi anlaşılmalıdır.

Job'ın Yeniden Kuyruğa Alınması

Worker işi tamamlamadan kapanırsa queue job'ı yeniden teslim edebilir. Yeni worker aynı görevi baştan çalıştırabilir. Business side effect tekrar edilmemelidir. Checkpoint varsa belirli aşamadan devam etmek mümkün olabilir. Basit ve idempotent tekrar başlatma çoğu zaman daha güvenlidir.

Idempotency

Deployment redelivery senaryosu idempotency ihtiyacını çok net gösterir. Worker eski sürümde side effect yapıp ack göndermeden kapanabilir. Yeni sürüm aynı job'ı tekrar alır. Unique business key ikinci etkiyi engelleyebilir. Rolling deployment testlerinde bu durum simüle edilmelidir.

Grace Period

Grace period pod'a aktif işleri tamamlaması için verilen süredir. Süre job'ların normal p99 çalışma süresinden çok kısa olmamalıdır. Çok uzun süre deployment hızını düşürebilir. Uzun job'lar checkpoint veya queue retry üzerinden ele alınabilir. Tek ayarı tüm worker türlerine uygulamak zorunlu değildir.

Zero-Downtime Worker Deployment

Yeni worker'lar hazır hale gelmeden eskiler kapatılmamalıdır. Queue tüketici sayısı deployment boyunca yeterli kalmalıdır. Schema compatibility eski ve yeni worker'ların bir süre birlikte çalışacağını kabul etmelidir. Payload değişiklikleri backward compatible yapılmalıdır. Deployment sonrası queue lag'in normal seviyeye döndüğü izlenmelidir.

Asenkron Processing Observability

Asenkron sistemlerde yalnızca HTTP request latency izlemek gerçek kullanıcı deneyimini göstermez. Job'ın queue'da ne kadar beklediği, ne kadar işlendiği ve ne zaman tamamlandığı ayrı metriklerdir. Throughput, failure rate, retry rate ve DLQ size birlikte izlenmelidir. Correlation ID ve distributed trace request ile background job arasındaki ilişkiyi görünür hale getirir. Ölçemediğimiz queue backlog'u kullanıcı şikayeti gelene kadar fark edilmeyebilir.

Request Latency

API'nin job kabul etme süresi düşük olabilir. Bu durum asıl işlemin hızlı olduğu anlamına gelmez. 202 Accepted endpoint'i 50 milisaniyede dönerken job iki saat queue'da bekleyebilir. Bu nedenle request latency yalnızca ilk aşamadır. Kullanıcı açısından end-to-end completion süresi ayrıca izlenmelidir.

Job Latency

Job latency job oluşturulmasından tamamlanmasına kadar geçen toplam süreyi ifade edebilir. Bu süre queue wait ve processing duration bileşenlerine ayrılmalıdır. Sadece ortalama değer peak sorunları gizler. P95 ve p99 dağılımları daha açıklayıcıdır. Farklı job type'lar ayrı metriklere sahip olmalıdır.

Queue Wait Time

Queue wait time job'ın worker tarafından alınmadan önce ne kadar beklediğini gösterir. Processing kısa olduğu halde wait yükseliyorsa consumer kapasitesi yetersiz olabilir. Priority veya tenant fairness de gecikmeye neden olabilir. Oldest job age önemli alarm metriğidir. Wait süresi kullanıcı SLA'sının parçası olmalıdır.

Processing Duration

Processing duration worker'ın job üzerinde aktif çalıştığı süreyi gösterir. Downstream latency veya CPU maliyeti bu metriği etkileyebilir. Job türüne göre histogram tutulmalıdır. Yeni deployment sonrası p99 artışı regresyon göstergesi olabilir. Batch boyutu değişiklikleri bu metriğe yansır.

Throughput

Throughput zaman biriminde tamamlanan başarılı job sayısını gösterir. Yalnızca yüksek throughput iyi sistem anlamına gelmez. Hata ve latency ile birlikte değerlendirilmelidir. Consumer throughput producer rate'in sürekli altındaysa backlog büyür. Capacity planning bu fark üzerinden yapılabilir.

Failure Rate

Failure rate işlerin ne kadarının başarısız olduğunu gösterir. Geçici ve kalıcı hatalar ayrı etiketlenmelidir. Tek bir downstream servis hataların çoğunu oluşturabilir. Deployment versiyonu ile korelasyon regresyonu bulmayı kolaylaştırır. Failure spike alarm üretmelidir.

Retry Rate

Retry rate sistemin ilk denemede ne kadar güvenilir olduğunu gösteren değerli bir sinyaldir. Final başarı oranı yüksek olsa bile sürekli retry ciddi maliyet oluşturabilir. Downstream servis davranışı veya timeout ayarı yanlış olabilir. Retry sayısını normal kabul edip gizlemek sorunu erteler. Job türüne göre baseline belirlenmelidir.

DLQ Size

DLQ size kalıcı başarısız mesaj sayısını gösterir. Sıfırdan yükselmeye başlaması immediate inceleme gerektirebilir. Yalnızca toplam sayı değil yaş dağılımı da önemlidir. Eski mesajlar unutulmuş operasyon sorununu gösterir. DLQ cleanup manuel inceleme olmadan veri silmemelidir.

Event Loop Utilization Nedir?

Event Loop Utilization, event loop'un belirli zaman aralığında ne kadar meşgul olduğunu anlamaya yardımcı olan metriktir. CPU kullanımına benzese de aynı şeyi ölçmez. Yüksek ELU ana JavaScript thread'inin sürekli çalıştığını ve yeni callback'ler için az boşluk kaldığını gösterebilir. CPU-bound kod veya yoğun callback işleme buna neden olabilir. Worker Threads kararı verirken ELU ve event loop delay birlikte güçlü sinyaller sunar.

ELU

ELU event loop'un aktif ve idle zaman oranı üzerinden yorumlanabilir. Sürekli yüksek değer uygulamanın ana thread'de yoğun olduğunu gösterebilir. Tek başına hata değildir çünkü yüksek throughput altında doğal olarak artabilir. Latency bozuluyorsa daha anlamlı hale gelir. Baseline farklı instance boyutları için ayrı tutulmalıdır.

CPU Kullanımından Farkı

Process CPU kullanımı tüm thread'lerin toplam etkisini yansıtabilir. ELU ise ana event loop'un ne kadar meşgul olduğunu anlamaya odaklanır. Worker Threads CPU tüketirken ana thread ELU daha düşük kalabilir. Tersine tek core üzerinde yoğun JavaScript event loop'u tamamen doldurabilir. İki metriği birlikte okumak daha doğru teşhis sağlar.

Yüksek ELU

Yüksek ELU uzun süre devam ediyorsa ana thread'e yeni iş gelmeye devam ediyor demektir. JSON parse, serialization veya büyük döngüler neden olabilir. Profiling hangi fonksiyonların süre tükettiğini gösterir. İşi optimize etmek veya worker'a taşımak gerekebilir. Yüksek ELU ile düşük latency mümkünse mevcut kapasite yine yeterli olabilir.

Event Loop Saturation

Event loop saturation ana JavaScript thread'inin neredeyse sürekli meşgul olmasıdır. Yeni I/O callback'leri hazır olsa bile çalışma sırası bekleyebilir. P99 latency hızla yükselebilir. CPU kullanımını artırmadan daha fazla network concurrency eklemek çözüm sağlamaz. Horizontal scaling veya CPU işini ayırmak gerekebilir.

Worker Threads Kararına Etkisi

Profiling belirli CPU-heavy fonksiyonların yüksek ELU ve delay yarattığını gösteriyorsa Worker Threads güçlü adaydır. Sadece ELU yüksek diye tüm işleri worker'a taşımak gerekmez. Önce fonksiyon düzeyi maliyet bulunmalıdır. Worker sonrası ELU düşerken toplam CPU kullanımı artabilir. Kullanıcı latency'sindeki değişim asıl başarı ölçüsüdür.

Event Loop Delay Nasıl Ölçülür?

Event loop delay planlanan callback'in gerçekte ne kadar gecikmeyle çalıştığını anlamaya yardımcı olur. Node.js perf_hooks içindeki monitorEventLoopDelay benzeri araçlar bu konuda kullanılabilir. Ortalama yerine p50, p95, p99 ve maksimum değerler izlenmelidir. Ani CPU işi veya garbage collection tail latency'yi yükseltebilir. Queue ve HTTP metrikleriyle korelasyon gerçek kullanıcı etkisini gösterir.

monitorEventLoopDelay()

monitorEventLoopDelay event loop gecikmesini histogram benzeri verilerle ölçmeye yardımcı olur. Ölçüm production overhead açısından uygun resolution ile yapılandırılmalıdır. Sonuçlar periyodik olarak metric sistemine aktarılabilir. Tek process değerleri instance etiketiyle tutulmalıdır. Alarm eşikleri uygulamanın latency hedeflerine göre belirlenmelidir.

P50

P50 event loop delay ölçümlerinin ortanca değerini gösterir. Normal çalışma davranışı için iyi bir baseline sağlar. Ancak kısa ama ciddi spike'ları gizleyebilir. Bu nedenle tek başına yeterli değildir. P95 ve p99 ile birlikte incelenmelidir.

P95

P95 ölçümlerin yüzde 95'inin altında kaldığı gecikme seviyesini gösterir. Tail davranışına ortalamadan daha iyi ışık tutar. Yük testi boyunca concurrency arttıkça nasıl değiştiği izlenebilir. Ani yükseliş saturation noktasına yaklaşıldığını gösterebilir. Endpoint latency ile korelasyon kurulmalıdır.

P99

P99 en kötü yüzde birlik kesime yakın gecikmeleri görünür hale getirir. Kullanıcıların küçük bir bölümü ciddi yavaşlık yaşayabilir. CPU spike veya büyük serialization işlemleri p99 üzerinde belirgin olabilir. Performans optimizasyonu yalnızca ortalama süreye göre yapılmamalıdır. Production SLO'larında p99 önemli olabilir.

Max Delay

Maksimum event loop delay nadir ama büyük bloklamaları ortaya çıkarabilir. Tek bir multi-second pause ciddi incident göstergesi olabilir. Max değer noise içerebileceği için histogram ve zaman çizgisiyle birlikte incelenmelidir. Aynı anda heap GC veya CPU profiling verisi yararlıdır. Alarm politikası tek örnek yerine süreklilik isteyebilir.

Async İşlem Metrikleriyle Korelasyon

Event loop delay artarken queue processing duration da yükseliyorsa worker ana thread üzerinde CPU işi yapıyor olabilir. HTTP latency de aynı anda kötüleşiyorsa kullanıcı etkisi daha açıktır. Downstream latency sabitken event loop delay yükselmesi uygulama içi soruna işaret eder. Trace ve metric aynı zaman ekseninde incelenmelidir. Korelasyon kök nedeni daha hızlı bulmayı sağlar.

Queue Monitoring Dashboard'da Neler Olmalıdır?

Queue monitoring dashboard yalnızca toplam job sayısını değil sistemin akış sağlığını göstermelidir. Waiting, active, completed, failed ve delayed job sayıları temel metriklerdir. Retry rate, oldest job age ve consumer throughput kapasite sorunlarını erken gösterir. Job türü ve tenant bazında kırılım gerektiğinde eklenebilir. Dashboard üzerinden doğrudan production payload göstermek güvenlik açısından sınırlandırılmalıdır.

Waiting Jobs

Waiting jobs henüz worker tarafından alınmamış görevlerin sayısını gösterir. Kısa süreli burst sonrası artış normal olabilir. Sürekli yükselen sayı consumer kapasitesi sorununa işaret eder. Job yaşıyla birlikte yorumlanmalıdır. Priority queue kullanılıyorsa sınıflara göre ayrılabilir.

Active Jobs

Active jobs şu anda worker'lar tarafından işlenen görevlerdir. Bu sayı concurrency kapasitesine yakınsa sistem yoğun çalışıyor olabilir. Sürekli düşük active ve yüksek waiting durumu worker bağlantı problemi gösterebilir. Çok uzun active job stuck işaretidir. Job timeout bu nedenle önemlidir.

Completed Jobs

Completed jobs throughput trendini gösterir. Sadece kümülatif sayı yerine zaman birimindeki rate daha yararlıdır. Deployment sonrası completion rate düşerse regresyon olabilir. Completed state retention sonsuza kadar tutulmak zorunda değildir. Business audit gerekiyorsa ayrı kalıcı kayıt kullanılabilir.

Failed Jobs

Failed jobs kalıcı olarak tamamlanamayan görevleri gösterir. Hata koduna göre breakdown kök nedeni bulmayı kolaylaştırır. Aynı hata birden fazla queue'da görülüyorsa ortak downstream problem olabilir. Alarm hızlı biçimde doğru ekibe gitmelidir. Retry ile geçici hata final failure'dan ayrılmalıdır.

Delayed Jobs

Delayed jobs gelecekte çalışmak üzere bekleyen görevleri gösterir. Sayının artması her zaman sorun değildir. Ancak planlanan zamanı geçmiş delayed job varsa scheduler problemi olabilir. Delay dağılımı izlenebilir. Retry backoff job'ları normal scheduled işlerden ayrıştırılabilir.

Retry Rate

Retry rate ilk denemede başarısız olan iş oranını görünür hale getirir. Yüksek retry final success yüksek olsa bile kapasite israfıdır. Downstream servis veya timeout politikası incelenmelidir. Deployment değişiklikleriyle korelasyon kurulabilir. Retry storm başlangıcı bu metrikten erken görülebilir.

Oldest Job Age

Oldest job age queue'da en uzun süredir bekleyen görevin yaşını gösterir. Queue depth düşük olsa bile tek bir stuck job bu metriği yükseltebilir. Kullanıcı SLA'sı açısından oldukça anlamlıdır. Priority starvation bu metrikle görülebilir. Alarm threshold job tipine göre değişebilir.

Consumer Throughput

Consumer throughput worker'ların belirli sürede tamamladığı iş miktarıdır. Producer rate ile karşılaştırıldığında backlog trendi tahmin edilebilir. Throughput düşüşü CPU, database veya broker sorunundan kaynaklanabilir. İşlem süresi metriğiyle birlikte incelenmelidir. Horizontal scaling kararları bu veriden yararlanabilir.

Distributed Tracing Asenkron Sistemlerde Nasıl Çalışır?

Distributed tracing bir HTTP request ile başlayan işlemin queue producer, broker ve background consumer aşamalarında takip edilmesini sağlar. Producer span mesaj yayınlama süresini kaydedebilir. Trace context message metadata üzerinden consumer'a aktarılabilir. Consumer yeni span oluşturarak job processing süresini aynı trace altında gösterebilir. Böylece kullanıcı isteğinin saatler sonra tamamlanan background adımı bile ilişkilendirilebilir.

HTTP Trace

HTTP request geldiğinde root veya server span oluşturulur. Authentication, database ve external API alt span'ları buna bağlanabilir. Background job oluşturulduğunda trace context mesaj metadata'sına eklenebilir. HTTP response 202 ile erken dönebilir. Trace job lifecycle ile daha sonra devam edebilir.

Queue Producer Span

Producer span enqueue veya publish operasyonunun süresini gösterir. Broker bağlantı gecikmesi burada görünür hale gelir. Mesaj ID ve queue adı düşük cardinality kurallarına uygun attribute olabilir. Hassas payload trace içine yazılmamalıdır. Publish başarısızlığı span status ile işaretlenebilir.

Message Context

Trace context message header veya metadata alanında taşınabilir. Consumer bu bilgiyi okuyarak önceki trace ile ilişki kurar. Standart propagation formatı kullanmak farklı servislerin birlikte çalışmasını kolaylaştırır. Kullanıcı payload'ı context'i taklit edememelidir. Trusted producer metadata'sı ayrı tutulmalıdır.

Consumer Span

Consumer job'ı aldığında processing span oluşturabilir. Queue wait süresi event timestamp üzerinden hesaplanabilir. Database ve API çağrıları child span olarak eklenir. Retry denemeleri ayrı span veya attribute ile gösterilebilir. Final failure trace üzerinden kök nedene bağlanabilir.

Correlation ID

Correlation ID log kayıtlarını aynı iş akışında birleştirmeye yardımcı olur. Trace ID ile aynı olmak zorunda değildir. Business job ID ve request ID ayrı amaçlara hizmet edebilir. Kullanıcıya support için güvenli referans ID verilebilir. Loglar yüksek cardinality maliyetini dikkate almalıdır.

Trace Propagation

Trace propagation context'in process ve servis sınırları boyunca taşınmasıdır. Queue ile zaman gecikmeli işlerde context'in serialization biçimi önemlidir. Yeni consumer span doğru parent veya link semantiğiyle oluşturulabilir. Çok uzun job gecikmelerinde link yaklaşımı daha anlamlı olabilir. Kullanılan observability standardının önerileri takip edilmelidir.

AsyncLocalStorage Ne İşe Yarar?

AsyncLocalStorage aynı asenkron execution zinciri boyunca request veya context bilgisini erişilebilir tutmaya yardımcı olur. Correlation ID, tenant ID ve trace context gibi bilgiler bu yapıyla taşınabilir. Her fonksiyona manuel parametre geçirmek zorunda kalmadan loglama kolaylaşır. Background job yeni execution olduğu için context tekrar kurulmalıdır. Hassas authorization kararlarını yalnızca global benzeri state'e bağlamadan açık güvenlik kontrolleri korunmalıdır.

Request Context

Request geldiğinde AsyncLocalStorage içinde request ID ve benzeri metadata saklanabilir. Alt servis fonksiyonları log oluştururken bu bilgiye erişebilir. Context lifecycle request tamamlandığında sona ermelidir. Bir request'in bilgisi başka request'e sızmamalıdır. Concurrency testleri bu izolasyonu doğrulamalıdır.

Correlation ID

Correlation ID her log satırına manuel parametre vermeden context üzerinden eklenebilir. Bu debugging süresini kısaltır. ID client'tan geliyorsa güvenlik ve format doğrulaması yapılmalıdır. İç sistem gerektiğinde yeni güvenilir ID üretebilir. Background job'a aktarılırken metadata olarak açıkça yazılmalıdır.

Tenant Context

Multi-tenant servislerde tenant bilgisi context içinde taşınabilir. Ancak database authorization yalnızca bu convenience mekanizmasına güvenmemelidir. Tenant ID güvenilir authentication sonucundan kurulmalıdır. Background worker job metadata'sından context'i yeniden oluşturur. Loglama politikası tenant bilgisinin hassasiyetini dikkate almalıdır.

Trace Context

Observability kütüphaneleri async context propagation için benzer mekanizmalardan yararlanabilir. Span bilgisi async/await zinciri boyunca korunabilir. Custom callback veya farklı thread sınırında context kaybolabilir. Worker Threads için context mesajla aktarılmalıdır. Test ve instrumentation kullanılan Node.js sürümüyle uyumlu olmalıdır.

Background Job'da Yeni Context

Queue worker bir HTTP request'in mevcut AsyncLocalStorage context'ini otomatik olarak devralmaz. Job başladığında job ID, tenant ve trace bilgisiyle yeni context oluşturulmalıdır. İş tamamlandığında context temizlenmelidir. Aynı worker concurrent job işliyorsa her execution izole olmalıdır. Bu ayrım yanlış log korelasyonunu önler.

OpenTelemetry ile Async Processing İzleme

OpenTelemetry trace, metric ve log sinyallerini standart biçimde üretmek için kullanılabilen gözlemlenebilirlik ekosistemidir. Async processing sistemlerinde HTTP isteğinden broker ve worker'a kadar uçtan uca bağlantı kurmaya yardımcı olur. Message broker instrumentation producer ve consumer span'larını otomatik veya yarı otomatik oluşturabilir. Custom worker span'ları business job detayını görünür kılar. End-to-end latency yalnızca tek servis süresinden daha anlamlıdır.

Trace

Trace bir iş akışındaki farklı operasyonların zaman çizelgesini gösterir. HTTP, database, queue publish ve worker processing aynı bağlamda görülebilir. Böylece queue wait ile processing süresi ayrılır. Sampling politikası yüksek trafikte maliyet kontrolü sağlar. Hatalı veya yavaş trace'ler daha yüksek oranda tutulabilir.

Metric

Metric sistem davranışını zaman içinde sayısal olarak izlemeyi sağlar. Queue depth, job latency ve event loop delay histogram olarak tutulabilir. Label cardinality kontrollü olmalıdır. Her job ID'yi metric label yapmak doğru değildir. Dashboard ve alert'ler stabil metriklerden oluşturulmalıdır.

Log

Log hata ve business event detaylarını metin veya structured formatta kaydeder. Trace ID ve job ID log korelasyonunu kolaylaştırır. Payload'ın tamamını loglamak güvenlik ve maliyet sorunu yaratabilir. Structured error code analiz için daha değerlidir. Log seviyesi production gürültüsünü kontrol etmelidir.

Message Broker Instrumentation

Broker instrumentation publish ve consume operasyonlarını otomatik span haline getirebilir. Queue adı, operation type ve messaging system gibi standart attribute'lar kullanılabilir. Raw message body trace'e eklenmemelidir. Instrumentation sürümü kullanılan client library ile uyumlu olmalıdır. Auto instrumentation'ın overhead'i load test ile ölçülebilir.

Worker Spans

Worker processing span gerçek business job süresini gösterir. Job type, attempt ve outcome gibi bilgiler düşük cardinality attribute olarak eklenebilir. Her alt API çağrısı child span olur. CPU-heavy Worker Thread görevleri için ayrıca span propagation gerekir. Başarısızlık exception event ile kaydedilebilir.

End-to-End Latency

End-to-end latency request'in işi oluşturmasından final sonucun hazır olmasına kadar geçen süredir. Queue wait, retry ve processing aşamalarının tamamını içerir. Kullanıcı açısından çoğu zaman gerçek performans metriği budur. HTTP endpoint çok hızlı olsa bile bu değer kötü olabilir. SLO tasarımında background süreçler mutlaka hesaba katılmalıdır.

Asenkron Sistemlerde Error Handling

Asenkron sistemlerde hata yönetimi yalnızca try/catch kullanmak değildir. Hatanın local, transient, permanent, timeout veya cancellation kaynaklı olduğunu sınıflandırmak gerekir. Bu sınıflandırma retry, DLQ ve kullanıcı bildirimi davranışını belirler. Aynı error object'i tüm katmanlarda aynı biçimde ele almak doğru değildir. Error code ve context standardı üretim operasyonunu büyük ölçüde kolaylaştırır.

Local Error

Local error uygulama kodundaki beklenmeyen durum veya doğrulama problemi olabilir. Bazıları bug, bazıları beklenen business rejection'dır. Hata türleri bu iki sınıfı ayırmalıdır. Retry genellikle deterministic bug için fayda sağlamaz. Alert seviyesi error class'a göre belirlenebilir.

Transient Error

Transient error geçici network kesintisi veya servis overload gibi kısa süre sonra düzelebilecek hatadır. Kontrollü retry uygulanabilir. Backoff ve jitter downstream sistemi korur. Maksimum attempts yine sınırlandırılmalıdır. Sürekli transient hata sonunda kalıcı operasyon failure'a dönüşür.

Permanent Error

Permanent error aynı input ile tekrar denendiğinde düzelmesi beklenmeyen hatadır. Validation error veya unsupported schema örnek olabilir. Retry gereksiz kapasite tüketir. Job doğrudan failed veya DLQ durumuna taşınabilir. Producer hatası varsa kök neden orada düzeltilmelidir.

Timeout

Timeout operasyonun izin verilen sürede tamamlanmadığını gösterir. Bunun nedeni downstream yavaşlığı veya lokal saturation olabilir. Timeout sonrası side effect'in gerçekleşip gerçekleşmediği bilinmeyebilir. Idempotent retry bu belirsizliği güvenli hale getirir. Timeout değerini rastgele yükseltmek yalnızca sorunu gizleyebilir.

Cancellation

Cancellation kullanıcı veya parent operation tarafından bilinçli biçimde işi durdurma isteğidir. Her cancellation hata olarak alarm üretmemelidir. Metric içinde ayrı outcome olarak tutulabilir. Cancellation downstream kaynağa propagate edilmelidir. Kısmi side effect varsa cleanup veya compensation gerekebilir.

Retry

Retry yalnızca belirlenmiş hata sınıflarında uygulanmalıdır. Hata object'i retryable flag veya error code taşıyabilir. Attempt sayısı log ve trace'e eklenmelidir. Her retry aynı timeout'u sıfırdan uzun süre kullanmamalıdır. Genel operation deadline korunmalıdır.

DLQ

Maksimum retry sonunda job görünür failed kanalına taşınmalıdır. DLQ payload güvenlik açısından kontrol edilmelidir. Hata nedeni ve son stack debugging için saklanabilir. Alarm ilgili ekibe yönlendirilmelidir. Replay işlemi audit edilebilir olmalıdır.

Unhandled Promise Rejection Nasıl Önlenir?

Unhandled Promise rejection bir Promise başarısız olduğunda sonucu hiçbir kodun ele almaması durumudur. Bu hata background işlerde kolayca gözden kaçabilir. Her Promise ya await edilmeli, ya return edilmeli ya da açık catch davranışına sahip olmalıdır. Process-level handler yalnızca son savunma hattı olarak düşünülmelidir. Fire-and-forget işlemler için durable queue daha güvenli bir modeldir.

Await Etmeyi Unutmak

Async fonksiyon çağrılıp await veya return edilmezse rejection üst kontrol akışından kopabilir. TypeScript ve lint kuralları floating Promise'leri tespit etmeye yardımcı olabilir. Kod review sırasında özellikle event handler'lar incelenmelidir. Eğer bilinçli fire-and-forget gerekiyorsa açık catch eklenmelidir. Kritik business işlerinde bunun yerine queue kullanılmalıdır.

Fire-and-Forget Promise

Fire-and-forget Promise çağıran kodun sonucu beklemediği işlemdir. Process restart olursa görev kaybolabilir. Hata kullanıcı veya job state'e yansımayabilir. Telemetri gibi kaybı kabul edilebilir bazı işlerde bilinçli kullanılabilir. Kritik side effect için durable mekanizma tercih edilmelidir.

.catch()

Await edilmeyen bilinçli Promise üzerinde catch kullanmak rejection'ın kaybolmasını önler. Catch yalnızca log atıp hatayı unutmak için kullanılmamalıdır. İş kritikse retry veya failure state gerekebilir. Log correlation ID içermelidir. Hata handler'ın kendisi yeni unhandled rejection üretmemelidir.

Background Promise Registry

Process içinde kısa ömürlü background Promise'ler varsa registry ile takip edilebilir. Graceful shutdown bu Promise'lerin tamamlanmasını bekleyebilir. Hata sonuçları merkezi olarak gözlemlenebilir. Bu yapı durable queue yerine geçmez. Process crash durumunda registry state kaybolur.

Process-Level Handler'a Güvenmemek

Global unhandledRejection handler hatayı gözlemlemek için yararlı olabilir. Ancak business control flow mekanizması olarak kullanılmamalıdır. Hangi Promise'in neden sahipsiz kaldığını bulmak asıl hedeftir. Global handler log ve alarm üretebilir. Uygulamanın devam edip etmeme politikası hata türüne göre dikkatle belirlenmelidir.

Fire-and-Forget Neden Tehlikelidir?

Fire-and-forget yaklaşımı hızlı görünür çünkü request sonucu beklemeden döner. Ancak process restart, hata görünürlüğü ve retry yokluğu nedeniyle kritik işlemlerde veri kaybına yol açabilir. Kullanıcıya başarı dönülürken arka plandaki e-posta veya ödeme yan işlemi başarısız olabilir. Durable queue bu görevi process yaşam süresinden ayırır. Asenkron olmak ile dayanıklı olmak aynı şey değildir.

Process Restart

Memory içindeki Promise process kapanınca kaybolur. Deployment, crash veya autoscaling bunu herhangi bir anda oluşturabilir. Kullanıcı isteği başarıyla tamamlandı sanabilir. Durable queue job'ı başka worker'a taşıyabilir. Kritik iş bu nedenle memory state'e bağlı bırakılmamalıdır.

Hata Görünürlüğü

Await edilmeyen Promise'in hatası kolayca gözden kaçabilir. Log olsa bile business state failed olarak işaretlenmeyebilir. Dashboard üzerinden job durumunu izlemek daha güvenlidir. Error metric ve alert eklenmelidir. Görünmeyen hata kullanıcı güvenini doğrudan etkiler.

Retry Yokluğu

Fire-and-forget Promise başarısız olduğunda otomatik retry mekanizması bulunmayabilir. Geliştirici manuel loop yazarsa process restart sorununu yine çözmez. Durable queue attempts ve backoff state'ini kalıcı tutar. Hata türüne göre retry sınıflandırılabilir. Uzun ömürlü işler için bu fark önemlidir.

Kullanıcıya Yanlış Başarı Dönmek

API arka plandaki kritik işi garanti altına almadan 200 dönerse kullanıcı tamamlanmış işlem bekleyebilir. İş daha sonra başarısız olduğunda state ile kullanıcı beklentisi çelişir. 202 Accepted bu semantiği daha doğru ifade eder. İşin accepted ve completed durumları ayrılmalıdır. API sözleşmesi bunu açıkça göstermelidir.

Durable Queue Kullanmak

Durable queue job'ı worker process'inden bağımsız saklar. Worker crash olduğunda görev yeniden teslim edilebilir. Retry, DLQ ve monitoring merkezi hale gelir. Bunun bedeli ekstra altyapıdır. İş kaybı kabul edilemiyorsa bu maliyet genellikle anlamlıdır.

Async Processing Güvenliği

Queue veya event sistemi iç ağda çalışıyor diye payload güvenilir kabul edilmemelidir. Worker input validation, authorization ve tenant izolasyonu uygulamalıdır. Secret veya gereksiz kişisel veriler job payload içinde tutulmamalıdır. Replay aynı authorization bağlamını güvenli biçimde yeniden kurmalıdır. Asenkron sistemler de HTTP endpoint'leri kadar güçlü güvenlik kontrollerine ihtiyaç duyar.

Queue Payload Validation

Worker aldığı payload'ı schema üzerinden doğrulamalıdır. Producer bug'ı veya eski sürüm hatalı veri gönderebilir. Validation başarısızsa job sonsuz retry edilmemelidir. Schema version hata mesajında görünür olabilir. Doğrulama business authorization'ın yerine geçmez.

Untrusted Message

Broker'a erişen her mesajı güvenilir saymak risklidir. Mesaj header ve payload alanları yetki yükseltme amacıyla manipüle edilebilir. Producer identity veya broker access control uygulanmalıdır. Worker kritik kimlik bilgilerini authoritative kaynaktan doğrulayabilir. Serialized kod veya güvenilmeyen komut çalıştırılmamalıdır.

Authorization

Job oluşturulduğu anda kullanıcının yetkisi kontrol edilir. Uzun süre sonra worker çalıştığında bazı işlemlerde yetkinin hâlâ geçerli olup olmadığı yeniden değerlendirilebilir. Service-to-service authorization ayrı politikaya sahip olabilir. Tenant ve resource ownership korunmalıdır. Authorization bilgisi yalnızca istemci tarafından gelen role alanına dayanmamalıdır.

Tenant Context

Worker job'ın tenant bilgisini trusted metadata üzerinden almalıdır. Tüm database sorguları tenant scope ile çalışmalıdır. Shared cache key'leri de tenant ayrımını korumalıdır. Cross-tenant veri sızıntısı ciddi güvenlik problemidir. Integration test farklı tenant job'larını concurrent çalıştırmalıdır.

Secret İçeren Payload

API key veya password gibi secret değerleri queue payload içinde saklamak risklidir. Queue dashboard, backup veya log yoluyla geniş erişim alanı oluşabilir. Worker secret'ı güvenli secret manager üzerinden runtime'da almalıdır. Zorunlu hassas veri şifreli saklanmalıdır. Retention süresi minimum tutulmalıdır.

Job Replay Güvenliği

Eski job replay edildiğinde o zamanki yetki ve veri bağlamı değişmiş olabilir. Worker replay'i kör biçimde çalıştırmamalıdır. Resource hâlâ mevcut mu ve işlem hâlâ geçerli mi kontrol edilmelidir. Finansal veya hassas işler manuel onay gerektirebilir. Replay audit log'a kaydedilmelidir.

Büyük Payload'ları Queue'ya Koymalı mıyız?

Genellikle büyük binary veya dev JSON payload'ları doğrudan queue backend içine koymak iyi fikir değildir. Redis memory, broker disk ve network maliyeti hızla büyüyebilir. Bunun yerine büyük veri object storage veya database içinde tutulup job'a referans gönderilebilir. Worker gerektiğinde veriyi stream ederek alabilir. Payload küçük, versionlanmış ve bağımsız doğrulanabilir olmalıdır.

Message Size

Broker ve client library mesaj boyutu için limit veya performans sınırına sahip olabilir. Büyük message replication ve transfer maliyetini yükseltir. Retry durumunda aynı büyük veri tekrar taşınır. Queue throughput düşebilir. Maksimum payload boyutu sistem standardı olarak belirlenmelidir.

Redis Memory

Redis tabanlı queue'da her büyük job doğrudan memory tüketebilir. Binlerce büyük payload kısa sürede node memory'sini aşabilir. Persistence dosyalarının boyutu da artar. Büyük veriyi external storage'a taşımak daha verimli olur. Queue yalnızca referans ve metadata tutar.

Broker Storage

Disk tabanlı broker'larda büyük message storage ve replication maliyetini artırır. Network üzerinden consumer'a taşınma süresi de uzar. Backlog oluştuğunda toplam storage ihtiyacı hızla büyür. Retention ve DLQ kopyaları ek alan tüketebilir. Capacity planning message size dağılımını dikkate almalıdır.

Object Storage Reference

Büyük dosya object storage'a yazılıp job içine key veya güvenli referans konabilir. Worker dosyayı stream ile indirir. URL yerine internal object key kullanmak expiration sorunlarını azaltabilir. Worker authorization ile storage erişimini kendi identity'si üzerinden yapar. İş tamamlandığında geçici dosya lifecycle politikası uygulanır.

Database ID Göndermek

Job payload içinde tüm entity yerine database ID göndermek payload boyutunu azaltır. Worker güncel veriyi gerektiğinde database'den okur. Bunun sonucu olarak job oluşturma anındaki snapshot ile çalışma anındaki state farklı olabilir. İş bu farkı kabul etmiyorsa version veya snapshot referansı kullanılmalıdır. Veri semantiği açık olmalıdır.

Payload Versioning

Queue'da bekleyen job eski producer sürümünden gelebilir. Yeni worker eski payload formatını anlamalı veya migration uygulamalıdır. Schema version alanı bu süreci kolaylaştırır. Breaking değişiklikler rolling deployment ile uyumlu planlanmalıdır. Version bilinmiyorsa job DLQ'ya yönlendirilebilir.

Job Payload Versioning

Job payload versioning producer ve worker sürümlerinin aynı anda farklı versiyonlarda çalışabileceği gerçeğini kabul eder. Rolling deployment sırasında eski job'lar yeni worker'a ulaşabilir. Schema version alanı doğru parser veya migration yolunu seçmeye yardımcı olur. Backward compatibility mümkün olduğunca korunmalıdır. Uzun süre queue'da kalabilen işler daha dikkatli version politikası gerektirir.

Schema Version

Payload içine açık schema version eklemek format değişikliklerini yönetmeyi kolaylaştırır. Worker hangi alanların mevcut olduğunu bu bilgiyle yorumlayabilir. Version yalnızca major breaking değişikliklerde artırılabilir. Küçük opsiyonel alan eklemeleri backward compatible olabilir. Schema dokümantasyonu repository içinde tutulmalıdır.

Eski Worker / Yeni Producer

Deployment sırasında yeni producer yeni payload üretirken eski worker hâlâ çalışıyor olabilir. Yeni alanlar opsiyonel ise sorun çıkmayabilir. Breaking format eski worker'ı poison message üretir hale getirebilir. Deployment sırası veya feature flag bu riski azaltır. Producer değişikliği tüm worker'lar uyumlu olduktan sonra aktive edilebilir.

Backward Compatibility

Yeni worker önceki payload sürümlerini belirli süre desteklemelidir. Eski alanı hemen silmek queue backlog varsa job kaybına neden olabilir. Deprecation süresi queue maksimum yaşından uzun tutulabilir. Contract test uyumluluğu doğrulayabilir. Destek penceresi açık biçimde belgelenmelidir.

Migration

Bazı breaking değişikliklerde eski payload yeni forma migrate edilebilir. Migration worker başlangıcında veya ayrı replay sürecinde yapılabilir. Eski verinin anlamı kaybolmamalıdır. Migration idempotent olmalıdır. Büyük backlog için performans etkisi ölçülmelidir.

Dead Letter

Worker desteklemediği schema version ile karşılaşırsa sonsuz retry yapmamalıdır. Job DLQ'ya reason code ile taşınabilir. Operasyon ekibi gerekli migration veya eski worker sürümünü devreye alabilir. Alert payload version bilgisini göstermelidir. Güvenlik nedeniyle ham payload her ekrana açılmamalıdır.

Asenkron Veri İşlemede Memory Yönetimi

Asenkron mimari yüksek concurrency sağladığı için memory kullanımını kolayca görünmez biçimde büyütebilir. Sınırsız Promise, büyük queue, stream buffer, yüksek batch size ve Worker Thread sayısı aynı anda memory tüketir. Sorun yalnızca heap out-of-memory değildir, garbage collection latency'yi de artırabilir. Bounded processing aktif veri miktarını kontrol altında tutar. Heap, RSS ve external memory metrikleri birlikte izlenmelidir.

Sınırsız Promise

Binlerce Promise aynı anda oluşturulduğunda her görevin state'i memory içinde tutulur. Request ve response nesneleri de bu state'e eklenebilir. Bounded concurrency aktif Promise sayısını azaltır. Görev listesi çok büyükse lazy iterator kullanılabilir. Böylece henüz başlamamış işler için Promise bile oluşturulmaz.

Büyük Queue

Durable queue backlog kabul eder fakat sonsuz büyümesi normal değildir. Redis tabanlı sistemde büyük queue doğrudan memory maliyetine dönüşebilir. Disk tabanlı broker'da storage ve replay süresi büyür. Producer rate kontrol edilmelidir. Oldest job age kapasite sorununun kullanıcı etkisini gösterir.

Stream Buffer

Her stream internal buffer kullanabilir. Çok sayıda concurrent pipeline bu buffer'ların toplamını yükseltir. highWaterMark tek stream için küçük görünse bile bin stream'de büyük memory oluşturabilir. Concurrency stream sayısında da sınırlandırılmalıdır. Object mode nesne boyutları özellikle önemlidir.

Büyük Batch

Batch büyüdükçe aynı anda bellekte tutulan kayıt sayısı artar. Her kayıt büyük JSON içeriyorsa birkaç bin öğe önemli heap tüketebilir. Batch optimize edilirken throughput ile memory birlikte ölçülmelidir. Stream'den gelen kayıtlar kontrollü batch'e aktarılabilir. Batch tamamlanınca referanslar serbest bırakılmalıdır.

Worker Thread Memory

Her Worker Thread kendi V8 ortamı nedeniyle ek memory kullanır. Worker sayısını CPU kadar memory de sınırlar. Aynı büyük dataset her worker'a kopyalanıyorsa tüketim daha da artar. Transferable veya shared immutable data özel durumlarda yardımcı olabilir. RSS metriği worker pool tuning sırasında izlenmelidir.

Bounded Processing

Bounded processing tüm katmanlarda aktif iş miktarını sınırlama ilkesidir. Promise concurrency, stream pipeline sayısı, worker pool ve queue consumer sayısı buna dahildir. Böylece sistem peak yükte daha öngörülebilir davranır. Queue backlog kontrollü yerde tutulur, memory'de değil. Bu yaklaşım performans kadar dayanıklılık sağlar.

Asenkron İşleme Performansı Nasıl Optimize Edilir?

Asenkron processing optimizasyonu tek bir Node.js ayarını değiştirmekten ibaret değildir. Batching, bounded concurrency, stream, doğru connection pool ve worker pool birlikte değerlendirilmelidir. Serialization ve gereksiz veri transferi de yüksek hacimde önemli maliyet oluşturur. Rate limit'ler sistemin gerçek kapasitesiyle uyumlu olmalıdır. Kurumsal Node.js asenkron veri işleme ve performans optimizasyon hizmeti verirken ilk yaptığım şey kodu değiştirmekten önce ölçüm zincirini tamamlamak olur.

Batching

Benzer küçük operasyonları batch halinde göndermek round-trip maliyetini azaltabilir. Database bulk insert veya toplu API endpoint'i buna örnektir. Batch çok büyütülürse latency ve memory artar. Hata durumunda tekrar işlenecek veri miktarı da genişler. Optimum boyut yük testiyle bulunmalıdır.

Bounded Concurrency

Bounded concurrency sistemin aktif görev sayısını kontrollü tutar. Bu yöntem connection pool ve external API kapasitesini korur. Tek bir global limit yerine iş türüne göre ayrı limitler gerekebilir. Limit performans metriğine göre dinamik hale getirilebilir. Sınırsız Promise.all kullanımına göre daha öngörülebilir davranır.

Streams

Streams büyük veriyi kademeli işleyerek memory kullanımını azaltır. Pipeline backpressure sağladığında producer tüketici kapasitesine uyum sağlar. Compression, parsing ve database batching aynı akışa bağlanabilir. Transform aşamasında büyük state biriktirilmemelidir. Multi-GB veri işlemlerinde stream çoğu zaman temel optimizasyon aracıdır.

Connection Pool

Database connection pool concurrency'nin fiziksel sınırlarından biridir. Çok küçük pool throughput'u kısıtlayabilir. Çok büyük pool database'i saturation'a taşıyabilir. Uygulama replica sayısı arttıkça toplam connection sayısı hesaplanmalıdır. Pool wait time ayrı metrik olarak izlenebilir.

Worker Pool

CPU-heavy görevler sabit Worker Thread pool üzerinde yürütülebilir. Pool boyutu CPU ve memory kapasitesine göre ayarlanır. Task queue sınırsız olmamalıdır. HTTP latency ile worker throughput birlikte izlenmelidir. CPU işi yoğunlaştığında ayrı worker deployment daha iyi izolasyon sağlayabilir.

Rate Limits

Rate limit downstream servisleri ve tenant kotasını korur. Concurrency limitiyle aynı şey değildir. Çok kısa işlerde concurrency düşük olsa bile saniyelik rate yüksek olabilir. Token bucket gibi algoritmalar burst kontrolü sunar. Retry trafiği de rate limit hesabına dahil edilmelidir.

Serialization Maliyeti

JSON parse, stringify ve worker message clone işlemleri yüksek hacimde CPU tüketebilir. Gereksiz büyük payload üretmemek en iyi optimizasyondur. Binary veya stream formatları bazı kullanım durumlarında daha verimli olabilir. Serialization süresi profiler ile ölçülebilir. API response şemasını küçültmek hem network hem CPU kazancı sağlar.

Throughput ve Latency Arasındaki Denge

Bir sistemi yalnızca saniyedeki işlem sayısına göre optimize etmek kullanıcı deneyimini bozabilir. Batch ve concurrency yükseldikçe throughput artarken queue wait veya p99 latency kötüleşebilir. İyi kapasite noktası toplam işlem miktarıyla kabul edilebilir gecikmeyi dengeler. Peak yük altında sistemin recovery süresi de önemlidir. Performans hedefleri p50 kadar p99 değerlerini de içermelidir.

Batch Size

Daha büyük batch database veya network round-trip sayısını azaltabilir. Ancak ilk kaydın batch dolmasını beklemesi latency oluşturur. Büyük batch failure durumunda daha fazla işi tekrar ettirebilir. Time-based flush bu beklemeyi sınırlandırabilir. Batch size gerçek trafik deseniyle test edilmelidir.

Concurrency

Concurrency artırıldığında aynı anda daha fazla I/O beklemesi örtüşebilir. Bir noktadan sonra downstream saturation oluşur ve latency hızla yükselir. Bu kırılma noktası benchmark ile görülür. En yüksek concurrency değeri genellikle en sağlıklı değer değildir. Güvenli headroom bırakılmalıdır.

Queue Wait Time

Queue wait consumer kapasitesinin kullanıcı işini ne kadar geciktirdiğini gösterir. Processing çok hızlı olsa bile yüksek wait toplam deneyimi kötüleştirir. Autoscaling backlog'a göre tetiklenebilir. Ancak downstream kapasitesi yoksa yalnızca worker artırmak çözüm değildir. Producer admission control gerekebilir.

Processing Time

Processing time worker'ın gerçek job üzerinde harcadığı süreyi ölçer. Batch, API latency veya CPU maliyeti bu değeri etkiler. Aynı job type içinde dağılım genişse veri boyutu etiketi değerlendirilebilir. Çok yüksek cardinality metric label kullanılmamalıdır. P99 outlier'lar ayrıca profiling ile incelenebilir.

End-to-End Latency

End-to-end latency enqueue öncesinden final sonucun hazır olmasına kadar bütün süreci içerir. Kullanıcı açısından en anlamlı göstergelerden biridir. Queue wait, processing ve retry gecikmeleri bu değere eklenir. Sadece worker süresini optimize etmek yeterli olmayabilir. SLO uçtan uca tanımlanmalıdır.

P99

P99 en yavaş yüzde birlik iş grubunun davranışını gösterir. Büyük tenant veya özel payload'lar burada görünür hale gelebilir. Ortalama latency iyi olsa bile p99 kötü kullanıcı deneyimi oluşturabilir. Concurrency tuning sırasında p99 dikkatle izlenmelidir. Performans iyileştirmesi tail latency'yi de hedeflemelidir.

Async Processing Benchmark Nasıl Yapılır?

Benchmark gerçek production davranışına yakın veri ve trafik modeliyle yapılmalıdır. Küçük sentetik payload üzerinden elde edilen yüksek throughput büyük dataset'te geçerli olmayabilir. Request rate, concurrency, CPU, memory, event loop delay ve error rate birlikte ölçülmelidir. Queue tabanlı sistemlerde backlog ve recovery süresi de teste dahil edilmelidir. Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri ancak ölçümle gerçek sisteme uygun hale gelir.

Gerçekçi Dataset

Test verisi production kayıt büyüklüğü ve dağılımını temsil etmelidir. Sadece küçük kayıtlarla test yapmak serialization ve memory maliyetini gizler. Büyük ve problemli edge case'ler de eklenmelidir. Hassas production verisi doğrudan kullanılmamalıdır. Anonim veya sentetik ama benzer dağılımlı dataset tercih edilmelidir.

Request Rate

Request rate saniyede sisteme gelen yeni iş miktarını belirler. Normal, peak ve burst seviyeleri ayrı test edilmelidir. Sabit rate ile concurrency-driven load farklı sonuç verebilir. Production trafik şekli mümkün olduğunca modellenmelidir. Autoscaling davranışı uzun testlerde gözlenmelidir.

Concurrency

Benchmark farklı concurrency seviyelerini kademeli test etmelidir. Throughput artarken latency'nin hangi noktada hızla yükseldiği bulunur. Connection pool ve worker pool limitleri kaydedilmelidir. Çok kısa testler queue birikimini göstermeyebilir. Steady-state süre yeterince uzun tutulmalıdır.

CPU

CPU kullanımı process ve container seviyesinde izlenmelidir. Worker Threads varsa thread dağılımı ayrıca incelenebilir. CPU yüzde 100'e yaklaşırken throughput artmıyorsa saturation oluşmuştur. Steal veya throttling container ortamında önemli olabilir. CPU limiti test ve production arasında uyumlu olmalıdır.

Memory

Heap, RSS ve external memory farklı anlamlara sahiptir. Stream buffer ve Worker Thread kullanımı RSS tarafında görülebilir. Uzun test memory leak davranışını yakalar. Sadece kısa peak değil zaman içindeki trend önemlidir. OOM limitinin altında güvenli headroom bırakılmalıdır.

Event Loop Delay

Event loop delay ana JavaScript thread bloklanmasını gösterir. Concurrency arttıkça p95 ve p99 değerleri izlenmelidir. Ani artış serialization veya CPU-heavy kodu işaret edebilir. CPU genel kullanımı orta seviyedeyken bile tek event loop doygun olabilir. Bu metrik Node.js benchmark'ında kritik yer tutar.

Throughput

Throughput tamamlanan başarılı iş sayısı olarak ölçülmelidir. Retry edilen denemeleri başarı gibi saymak yanlış sonuç verir. Job type veya endpoint bazında ayrı değerler tutulabilir. En yüksek throughput noktası latency SLA'sını ihlal ediyorsa tercih edilmemelidir. Sürdürülebilir throughput hedeflenmelidir.

Error Rate

Concurrency arttıkça timeout, 429 veya database error oranı yükseliyorsa sistem kapasite sınırına yaklaşmıştır. Benchmark yalnızca başarılı request'leri saymamalıdır. Error reason breakdown kök nedeni gösterir. Retry sonrası final success ayrı metrik olmalıdır. Hata oranı kabul edilebilir sınırın üstüne çıktığında test sonucu başarısız sayılmalıdır.

Load Test ile Asenkron Mimariyi Doğrulamak

Load test yalnızca normal trafik altında throughput ölçmek için yapılmamalıdır. Burst load, slow consumer, downstream failure ve queue backlog gibi stres senaryoları da test edilmelidir. Asıl önemli soru sistemin yük altında nasıl bozulduğu ve yük azaldığında ne kadar sürede toparlandığıdır. Queue sınırsız büyümemeli ve retry storm oluşmamalıdır. Recovery time dayanıklılık değerlendirmesinin temel parçasıdır.

Normal Load

Normal load günlük tipik trafik seviyesini temsil eder. Sistem bu koşulda düşük hata oranı ve stabil latency göstermelidir. Resource kullanımında güvenli headroom bulunmalıdır. Queue backlog sürekli sıfıra yakın veya kontrollü olmalıdır. Normal test baseline sağlar.

Burst Load

Burst load kısa sürede normalin birkaç katı trafik gönderir. Queue bu geçici artışı absorbe edebilir. Memory ve connection sayısının kontrolsüz büyümemesi gerekir. Burst sonrası backlog makul sürede temizlenmelidir. Autoscaling varsa tepki süresi ölçülür.

Slow Consumer

Consumer yapay olarak yavaşlatılarak producer-consumer dengesizliği test edilebilir. Queue depth'in nasıl büyüdüğü ve limiter davranışı gözlenir. Stream sisteminde slow destination backpressure test edilir. Memory sabit kalmalıdır. Alarm mekanizmaları beklenen zamanda tetiklenmelidir.

Downstream Failure

Database veya harici API geçici olarak hata verecek şekilde simüle edilebilir. Retry policy'nin exponential backoff ve jitter kullandığı doğrulanır. Circuit breaker varsa state geçişleri test edilir. Queue backlog artarken sistem kabul edilebilir biçimde davranmalıdır. Downstream geri geldiğinde kontrollü recovery gözlenmelidir.

Queue Backlog

Bilerek büyük backlog oluşturmak oldest job age ve worker scaling davranışını test eder. Sistemin memory kullanımı backlog büyüklüğüne bağlı olarak kontrolden çıkmamalıdır. Priority starvation gözlenebilir. Kullanıcı status endpoint'i doğru durumu göstermelidir. Backlog temizleme süresi kapasite planına veri sağlar.

Recovery Time

Arıza veya burst bittikten sonra sistemin normal latency ve queue seviyesine dönme süresi recovery time'dır. Sadece incident sırasındaki davranış değil toparlanma da önemlidir. Çok agresif retry recovery'yi uzatabilir. Worker autoscaling scale-down politikası da test edilmelidir. SLO'ya recovery hedefi eklenebilir.

Chaos Test Senaryoları

Chaos test kontrollü hata koşulları oluşturarak asenkron mimarinin gerçek failure davranışını doğrular. Broker kesintisi, worker crash, database timeout ve duplicate message gibi durumlar üretimde er ya da geç karşılaşılabilecek olaylardır. Amaç sistemi bozmak değil, varsayımları ölçülebilir biçimde test etmektir. Deneyler güvenli staging ortamında veya kontrollü production scope içinde yürütülmelidir. Her senaryo için beklenen recovery davranışı önceden yazılmalıdır.

Redis Kesintisi

Redis tabanlı queue kullanılıyorsa kısa bağlantı kesintisi simüle edilmelidir. Producer enqueue başarısız olduğunda yanlış başarı dönmemelidir. Worker connection yeniden kurulurken duplicate işlem oluşmamalıdır. Persistence ve failover davranışı gözlenmelidir. Alert doğru zamanda tetiklenmelidir.

RabbitMQ Kesintisi

RabbitMQ bağlantısı kesildiğinde publisher ve consumer davranışı test edilmelidir. Publisher confirm bekleyen mesajların durumu anlaşılmalıdır. Reconnect sırasında duplicate publish oluşabilir. Consumer ack verilmemiş mesajları tekrar alabilir. Idempotency bu senaryoda doğrulanmalıdır.

Worker Crash

Job ortasında worker process zorla kapatılabilir. Queue lock süresi dolduktan sonra job'ın yeniden teslim edildiği doğrulanmalıdır. Side effect iki kez oluşmamalıdır. Graceful olmayan crash observability sisteminde görünmelidir. Worker replacement süresi ölçülmelidir.

Database Timeout

Database query'lerine yapay latency veya timeout eklenebilir. Worker retry politikası gözlenir. Connection pool'un tamamı bekleyen query'lerle dolmamalıdır. Timeout sonrası transaction state temizlenmelidir. Recovery sırasında retry storm oluşmamalıdır.

Third-Party API 500

Dış API sürekli 500 döndürdüğünde job'lar belirlenen attempts sınırını aşmamalıdır. Exponential backoff ve circuit breaker devreye girmelidir. Queue depth artışı alert üretmelidir. Servis düzeldiğinde recovery kontrollü gerçekleşmelidir. Kullanıcıya gerektiğinde gecikme bilgisi sunulmalıdır.

Duplicate Message

Aynı event iki veya daha fazla kez kasıtlı olarak gönderilmelidir. Consumer'ın yalnızca tek business side effect ürettiği doğrulanmalıdır. Processed event table veya unique constraint test edilir. Duplicate log noise yaratmamalıdır. Metric duplicate oranını takip edebilir.

Poison Message

Her denemede validation hatası veren payload queue'ya gönderilebilir. Job maksimum attempts sonrası DLQ'ya gitmelidir. Diğer sağlıklı job'lar normal hızda ilerlemelidir. Alert payload reason code'u göstermelidir. Replay aynı hata düzeltilmeden yapılmamalıdır.

Asenkron Veri İşleme İçin Mimari Karar Matrisi

Asenkron işleme aracını seçerken iş yükü türü, veri büyüklüğü, süre ve dayanıklılık ihtiyacı birlikte değerlendirilmelidir. Küçük dosya ile multi-GB dosya aynı yöntemle işlenmemelidir. CPU-heavy task ile HTTP fan-out farklı concurrency modellerine sahiptir. Event stream replay ihtiyacı klasik e-posta job'ından tamamen farklıdır. Aşağıdaki başlıklar hızlı karar çerçevesi sunar.

Küçük Dosya

Küçük dosya belleğe alınabilir ve basit async fs API'siyle işlenebilir. Dosya boyutu için açık limit bulunmalıdır. Beklenmeyen büyük input geldiğinde stream'e geçiş veya hata davranışı tanımlanmalıdır. CPU-heavy parsing varsa event loop etkisi ölçülmelidir. Gereksiz stream katmanı küçük işlerde şart değildir.

Büyük Dosya

Büyük dosya varsayılan olarak stream üzerinden işlenmelidir. Parsing ve transform aşamaları pipeline içinde kademeli ilerler. Backpressure destination hızına göre akışı düzenler. HTTP request süresi uzun olacaksa background job kullanılabilir. Dosya object storage'da tutulabilir.

API Fan-Out

Bir request birden fazla bağımsız API çağrısı yapacaksa concurrent I/O değerlidir. Küçük sayıda çağrı Promise.all ile yapılabilir. Büyük listede bounded concurrency gerekir. Rate limit ve timeout uygulanmalıdır. Partial success gerekiyorsa allSettled değerlendirilebilir.

CPU-Heavy Task

Uzun CPU hesabı Worker Thread pool'a taşınmalıdır. İş çok uzunsa queue worker'ı içinde worker pool kullanılabilir. Ana HTTP event loop korunmalıdır. Veri aktarım maliyeti ölçülmelidir. Worker sayısı CPU benchmark'ıyla belirlenmelidir.

E-posta

E-posta genellikle background job olarak uygundur. Provider geçici hataları retry edilebilir. Duplicate gönderimi önlemek için idempotency key kullanılabilir. Kritik transaction e-postanın tamamlanmasını beklemek zorunda değildir. Delivery sonucu observability içinde izlenmelidir.

Rapor Üretimi

Uzun rapor üretimi 202 Accepted ve queue modeliyle yönetilebilir. Database sorgusu stream veya pagination kullanabilir. PDF üretimi CPU-heavy ise Worker Thread veya ayrı process değerlendirilebilir. Sonuç object storage'a yazılabilir. Kullanıcı tamamlanma notification'ı alabilir.

Event Stream

Yüksek hacimli, replay edilebilir event akışında Kafka benzeri log tabanlı broker değerlendirilebilir. Partition key ordering ihtiyacına göre seçilir. Consumer group yatay ölçek sağlar. Event schema backward compatible tutulmalıdır. Idempotent consumer duplicate riskini yönetir.

ETL

ETL işlemleri stream, async iterator, batch database write ve queue kombinasyonundan yararlanabilir. Büyük dataset RAM'e alınmamalıdır. Her aşama observability metriği üretmelidir. Retry sınırı kaydın veya batch'in semantiğine göre seçilir. Workflow çok aşamalıysa dependency graph gerekebilir.

Scheduled Job

Düşük riskli tek instance görevlerde cron yeterli olabilir. Kritik ve multi-instance sistemlerde distributed scheduler daha güvenlidir. Scheduler job'ı durable queue'ya ekleyebilir. Missed run ve duplicate davranışı açık olmalıdır. Timezone ile DST testleri unutulmamalıdır.

Uçtan Uca Örnek: Büyük CSV Dosyasını Asenkron İşlemek

Büyük CSV import sistemi Node.js'in stream, queue, backpressure ve batch yeteneklerini birlikte göstermek için iyi bir örnektir. Kullanıcı dosyayı yükler ve API doğrudan işlemek yerine object storage'a kaydeder. Queue job dosya referansını worker'a taşır. Worker dosyayı stream ederek parse eder, doğrular ve database'e batch halinde yazar. Progress ve tamamlanma bilgisi kullanıcıya ayrı kanaldan sunulur.

Upload

Kullanıcı dosyayı doğrudan API'ye veya presigned storage upload akışıyla yükleyebilir. Maksimum dosya boyutu baştan sınırlandırılmalıdır. MIME type tek güvenlik kontrolü olarak kullanılmamalıdır. Upload tamamlanmadan import job oluşturulmamalıdır. Dosya checksum gerektiğinde bütünlük doğrulaması sağlayabilir.

Object Storage

Büyük CSV queue payload içine konmak yerine object storage'da tutulur. Job yalnızca object key taşır. Worker kendi servis kimliğiyle dosyayı okur. Geçici import dosyalarının retention politikası bulunmalıdır. Kullanıcı tarafından değiştirilemeyen immutable key tercih edilebilir.

Queue Job

Import job dosya ID, tenant ID ve schema version gibi metadata içerir. Job deterministik ID ile duplicate import riskini azaltabilir. Worker concurrency database kapasitesine göre sınırlanır. Uzun iş için timeout yeterince gerçekçi olmalıdır. Crash sonrası job yeniden çalışabileceği için yazım idempotent olmalıdır.

Read Stream

Worker dosyayı read stream olarak açar. Tüm CSV belleğe alınmaz. Storage SDK gerçek streaming desteklemelidir. Download ve parser arasındaki backpressure korunmalıdır. AbortSignal job cancellation ile ilişkilendirilebilir.

CSV Parser

Parser chunk'ları satırlara dönüştürür ve object mode kayıtlar üretebilir. Hatalı CSV satırı için line number saklanmalıdır. Parser'ın tüm dosyayı içeride buffer etmediği doğrulanmalıdır. Encoding hataları kontrollü biçimde ele alınmalıdır. Header schema import türüne göre doğrulanmalıdır.

Transform

Her satır normalize edilir ve business modeline dönüştürülür. Tarih, sayı ve kod alanları standart formata çevrilebilir. Validation hataları ayrı reject akışına yazılabilir. CPU-heavy dönüşüm varsa Worker Thread değerlendirilebilir. Basit transform ana stream içinde kalabilir.

Batch Database Write

Doğrulanmış kayıtlar örneğin 100 veya 500 öğelik batch'lerde database'e yazılır. Batch size benchmark ile seçilir. Unique constraint duplicate kayıtları önleyebilir. Transaction bir batch ile sınırlı tutulabilir. Başarısız batch hata türüne göre retry veya split edilebilir.

Backpressure

Database yazımı yavaşladığında parser ve read stream üretimi de yavaşlamalıdır. pipeline veya async iterator bu akış kontrolünü destekleyebilir. Veriyi okuyup milyonlarca kaydı memory queue'ya koymak yanlış olur. Memory sabit seviyeye yakın tutulmalıdır. Downstream database sistemin gerçek throughput sınırını belirleyebilir.

Progress

Worker işlenen satır sayısını periyodik olarak database veya cache'e yazabilir. Her satırda progress update yapmak gereksiz I/O oluşturur. Belirli sayı veya zaman aralığında checkpoint yeterlidir. Kullanıcı status endpoint üzerinden ilerlemeyi görebilir. Yüzde yalnızca toplam satır sayısı güvenilir şekilde biliniyorsa gösterilmelidir.

Completion Notification

Import tamamlandığında job state completed olarak güncellenir. Başarılı, başarısız ve atlanan satır sayıları sonuçta gösterilebilir. Kullanıcıya e-posta veya uygulama içi bildirim gönderilebilir. Reject dosyası varsa güvenli download link sunulabilir. Notification duplicate completion nedeniyle iki kez gitmemelidir.

Uçtan Uca Örnek: CPU-Heavy Veri Dönüşümü

CPU-heavy veri dönüşümünde ana hedef HTTP event loop'u uzun hesaplamadan korumaktır. Request önce input validation yapar ve görev yeterince kısaysa Worker Thread pool'a gönderilir. Pool sabit worker sayısıyla CPU çekirdeklerini kontrollü kullanır. Timeout veya worker failure durumunda görev açık hata sonucu üretir. Çok uzun görevlerde bu desen queue ile birleştirilerek request lifecycle'dan tamamen ayrılabilir.

HTTP Request

Request kullanıcı girdisini ve yetkisini doğrulamak için ana thread üzerinde kısa işlemler yapar. Büyük payload boyutu sınırlandırılır. İşin birkaç saniyeden uzun süreceği biliniyorsa 202 Accepted modeli tercih edilebilir. Kısa CPU görevinde request sonucu worker'dan bekleyebilir. Deadline worker task'a aktarılmalıdır.

Input Validation

Worker'a geçersiz veri göndermek gereksiz CPU tüketir. Schema ve temel business validation main thread üzerinde hızlı biçimde yapılabilir. Çok pahalı validation yine worker içinde olabilir. Payload içindeki güvenilmeyen dosya veya kod güvenlik kontrolünden geçmelidir. Validation error retry edilmemelidir.

Worker Pool

Önceden oluşturulmuş Worker Thread pool task'ları alır. Havuz boyutu CPU core ve memory limitine göre belirlenir. Yeni request geldiğinde boş worker yoksa task queue'da bekler. Queue için maksimum uzunluk belirlenmelidir. Saturation durumunda kontrollü hata veya background job seçeneği kullanılabilir.

Task Dispatch

Dispatcher task ID ve gerekli input'u worker'a gönderir. Büyük ArrayBuffer varsa transfer listesi kullanılabilir. Promise task ID ile sonucu bekler. Timeout süresi dolarsa task iptal veya ignore edilmemelidir. Worker'ın gerçekten durdurulup durdurulamayacağı tasarıma göre belirlenir.

CPU Processing

Worker yoğun dönüşümü kendi JavaScript thread'inde yürütür. Ana event loop bu sırada başka HTTP isteklerine yanıt verebilir. CPU yüzdesi yükselse de event loop delay daha kontrollü kalmalıdır. Task algoritması profiling ile optimize edilebilir. Paylaşılan state minimum tutulmalıdır.

Result

Worker sonucu postMessage ile geri gönderir. Büyük result kopyalama maliyeti yaratabilir. Gerekirse result object storage veya shared transfer yöntemiyle taşınabilir. Main thread response'u kullanıcıya iletir. Sonuç schema'sı worker ile main thread arasında versionlanabilir.

Timeout

CPU task belirli deadline'ı aşarsa kullanıcı sonsuza kadar beklememelidir. Timeout task state'ini failed olarak işaretleyebilir. Worker gerçekten iptal edilemiyorsa gereksiz CPU çalışması devam edebilir. Cooperative cancellation için worker belirli checkpoint'lerde signal kontrol edebilir. Uzun işler background queue'ya taşınmalıdır.

Worker Failure

Worker exception veya beklenmeyen exit ile kapanabilir. Pool ilgili task'ı reject etmeli ve yeni worker oluşturmalıdır. Görev idempotent ise tekrar denenebilir. Sürekli aynı input worker'ı çökertiyorsa poison task olarak ayrılmalıdır. Worker crash rate metric ve alert ile izlenmelidir.

Uçtan Uca Örnek: Güvenilir Background Job

Güvenilir background job tasarımı yalnızca queue'ya veri göndermekten daha fazlasıdır. API business transaction yapar, transactional outbox event kaydını aynı commit içinde oluşturur ve publisher queue'ya güvenli biçimde gönderir. Worker job'ı idempotent olarak işler. Geçici hata retry edilir ve kalıcı hata DLQ'ya taşınır. Observability tüm akışı request'ten final sonuca kadar bağlar.

API Request

Kullanıcı isteği authentication ve validation aşamasından geçer. Business operation için idempotency key kullanılabilir. API uzun işi doğrudan yürütmez. Kabul edilen işlem için database transaction başlatır. Response yalnızca güvenilir state oluşturulduktan sonra döner.

Database Transaction

Business değişikliği transaction içinde yapılır. Aynı transaction outbox event kaydını da oluşturur. Her iki kayıt birlikte commit olur veya rollback olur. Böylece event niyeti business state'ten kopmaz. Transaction mümkün olduğunca kısa tutulmalıdır.

Transactional Outbox

Outbox event'i publisher tarafından daha sonra okunur. Event ID sabit kalır. Publisher crash durumunda aynı event tekrar gönderilebilir. Consumer duplicate'i güvenli işlemelidir. Outbox cleanup gönderilmiş eski kayıtları kontrollü siler.

Queue Publish

Publisher event'i seçilen queue veya broker'a gönderir. Publish acknowledgement alınmadan kayıt sent olarak işaretlenmemelidir. Ack sonrası database update öncesi crash duplicate oluşturabilir. Bu durum tasarım tarafından kabul edilmelidir. Event ID duplicate kontrolünü sağlar.

Worker

Worker mesajı alır ve payload schema'sını doğrular. Tenant ve authorization context güvenli kaynaktan kurulur. Downstream çağrılar timeout ve rate limit ile korunur. İş başarılı olduğunda acknowledgement verilir. Shutdown sırasında aktif job kontrollü tamamlanır.

Idempotency

Worker aynı event'i iki kez aldığında business etkisi tekrarlanmamalıdır. Database unique constraint veya processed event table kullanılabilir. Harici API destekliyorsa event ID idempotency key olarak gönderilebilir. Kontrol ve side effect mümkün olduğunca aynı transaction içinde tutulmalıdır. Duplicate teslim chaos test ile doğrulanmalıdır.

Retry

Geçici hata exponential backoff ve jitter ile tekrar denenir. Validation hatası retry edilmez. Maksimum attempts sınırı vardır. Her deneme trace içinde attempt numarasıyla görünür. Deadline aşılırsa yeni retry başlatılmaz.

DLQ

Maksimum retry sonrası job DLQ'ya taşınır. Payload güvenli biçimde saklanır ve error reason görünür olur. Alert operasyon ekibine gider. Sorun düzeltildikten sonra replay kontrollü hızda yapılır. Replay idempotency garantisini kullanır.

Observability

Request ID, event ID ve job ID birbirine bağlanabilir. Queue wait, processing duration ve final status metrik olarak tutulur. Trace producer ve consumer adımlarını gösterir. Failed job error code ile aranabilir. Kullanıcı destek ekibi tek referans üzerinden işlem geçmişini bulabilir.

Node.js Asenkron Veri İşlemede Sık Yapılan Hatalar

Node.js performans sorunlarının önemli bölümü API seçiminden çok yanlış varsayımlardan kaynaklanır. async keyword kullanmak bloklayan CPU işini asenkron hale getirmez. Promise.all sınırsız concurrency sağladığında downstream servisleri zorlayabilir. Stream backpressure'ını veya queue idempotency'sini görmezden gelmek yüksek hacimde ciddi sonuçlar üretir. Üretim öncesi tasarım review bu hataların büyük bölümünü erkenden yakalayabilir.

Her Şeye async Yazınca Non-Blocking Olduğunu Sanmak

async keyword Promise tabanlı API sunar fakat JavaScript hesaplamasını başka thread'e taşımaz. Büyük döngü async fonksiyon içinde de ana event loop'u bloke eder. JSON.parse davranışı da değişmez. Profiling gerçek CPU maliyetini gösterir. Gerektiğinde Worker Thread kullanılmalıdır.

Büyük Dizilerde Sınırsız Promise.all() Kullanmak

Binlerce öğeyi doğrudan Promise.all ile başlatmak connection ve memory sınırlarını zorlar. Downstream API veya database ilk doygunlaşan bileşen olabilir. Promise pool ile concurrency sınırı uygulanmalıdır. Lazy iterator tüm Promise'leri baştan oluşturmamayı sağlar. Throughput ve error rate birlikte test edilmelidir.

Backpressure'ı Yok Saymak

Producer consumer'dan hızlıysa veri bir yerde birikir. Stream write false sonucunu yok saymak memory'yi büyütür. Queue backlog'unu izlememek aynı sorunun dağıtık versiyonudur. Akış kontrolü sistemin doğal parçası olmalıdır. Sınırsız buffer sürdürülebilir değildir.

CPU İşini Main Thread'de Çalıştırmak

Uzun CPU görevi tüm I/O callback'lerini geciktirebilir. Sistem tek endpoint nedeniyle genel latency problemi yaşayabilir. Worker Thread pool ana event loop'u korur. İş algoritmik olarak optimize edilebiliyorsa önce bu yapılmalıdır. Worker yalnızca yükü başka yere taşımak için kullanılmamalıdır.

Her Task İçin Yeni Worker Oluşturmak

Yeni worker startup ve memory maliyeti taşır. Yüksek trafikte her task için worker açmak thread explosion oluşturur. Sabit pool worker reuse sağlar. Queue pool saturation'ı yönetir. Worker sayısı benchmark ile seçilmelidir.

HTTP Request İçinde Uzun İş Çalıştırmak

Dakikalar süren iş proxy timeout ve deployment riskine açıktır. Kullanıcı uzun süre bağlantı bekler. 202 Accepted ve background queue daha dayanıklıdır. Status endpoint kullanıcıya ilerleme sunar. İş process restart'tan bağımsız hale gelir.

Fire-and-Forget Kullanmak

Kritik job'ı await etmeden Promise olarak başlatmak veri kaybı riskidir. Process kapanırsa iş kaybolur. Hata görünürlüğü düşer. Durable queue retry ve monitoring sağlar. Fire-and-forget yalnızca kaybın gerçekten kabul edildiği işler için kullanılmalıdır.

Sınırsız Retry

Kalıcı hatayı sonsuz retry etmek queue kapasitesini tüketir. Downstream outage sırasında retry storm oluşabilir. Maksimum attempts, exponential backoff ve jitter gereklidir. Poison message DLQ'ya taşınmalıdır. Retry bir hata çözümü değil recovery mekanizmasıdır.

Idempotency Kullanmamak

At-least-once sistem duplicate mesaj üretebilir. Idempotency yoksa ödeme, e-posta veya kayıt iki kez oluşabilir. Unique constraint güçlü koruma sağlar. Event ID consumer state ile ilişkilendirilebilir. Duplicate delivery normal test senaryosu olmalıdır.

Queue Depth'i İzlememek

Worker'lar çalışıyor görünse bile queue sürekli büyüyor olabilir. Kullanıcı job'ları saatlerce beklemeye başlayabilir. Queue depth ve oldest job age alarm üretmelidir. Consumer throughput producer rate ile karşılaştırılmalıdır. Observability asenkron mimarinin zorunlu parçasıdır.

Production Öncesi Async Processing Kontrol Listesi

Production öncesinde iş yükünün I/O veya CPU yapısı açıkça sınıflandırılmalıdır. Concurrency, backpressure, timeout, cancellation ve retry davranışı yalnızca kod review ile değil load test ile doğrulanmalıdır. Background job'ların durable ve idempotent olduğundan emin olunmalıdır. Queue lag, event loop delay ve DLQ gibi metrikler dashboard üzerinde hazır olmalıdır. Aşağıdaki sorulardan birine net cevap verilemiyorsa production öncesi ek inceleme yapmak faydalıdır.

İş Yükü I/O mu CPU mu?

Her ana job türü için zamanın nerede harcandığı bilinmelidir. Network veya database beklemesi I/O-bound davranıştır. Yoğun hesaplama CPU-bound'dur. İki tür aynı worker'da bulunabilir. Profiling doğru stratejiyi seçmeye yardımcı olur.

Concurrency Limiti Var mı?

Binlerce iş geldiğinde aynı anda kaçının aktif olacağı belirlenmelidir. Limit database pool ve dış servis kapasitesiyle uyumlu olmalıdır. Sınırsız Promise kabul edilmemelidir. Queue worker concurrency de açıkça konfigüre edilmelidir. Limit load test ile doğrulanmalıdır.

Büyük Veri Stream Ediliyor mu?

Büyük dosya veya response tek seferde RAM'e alınmamalıdır. Stream pipeline kullanımı kontrol edilmelidir. Parser ve transform katmanlarının gerçekten incremental çalıştığı doğrulanmalıdır. Object mode nesne boyutu ölçülmelidir. Memory peak üretim limitinin altında kalmalıdır.

Backpressure Yönetiliyor mu?

Producer consumer'dan hızlı olduğunda sistemin nasıl davranacağı bilinmelidir. Stream write false sinyali uygulanmalıdır. Queue backlog için sınır ve alert bulunmalıdır. Rate limit veya load shedding gerekebilir. Memory sınırsız tampon olarak kullanılmamalıdır.

Timeout Var mı?

Her network ve uzun işlem sonsuza kadar bekleyemez. Timeout iş türüne göre tanımlanmalıdır. Üst request deadline ile uyumlu olmalıdır. Timeout sonrası gerçek operasyon mümkünse iptal edilmelidir. Timeout hatası retry sınıflandırmasına dahil edilmelidir.

Cancellation Var mı?

Kullanıcı veya parent request işi iptal ettiğinde alt kaynaklar durabilmelidir. AbortSignal desteklenen API'lere aktarılmalıdır. Stream ve worker cleanup yapılmalıdır. Cancellation normal hata metriğinden ayrılabilir. Kısmi side effect davranışı açıklanmalıdır.

Background İşler Durable Queue'da mı?

Kritik ve uzun işler yalnızca memory içi Promise'e bırakılmamalıdır. Process restart sonrası job korunmalıdır. Queue persistence gereksinime uygun olmalıdır. Enqueue başarısızsa kullanıcıya yanlış başarı dönülmemelidir. Outbox ihtiyacı değerlendirilmelidir.

Retry Sınırlı mı?

Her retryable job maksimum deneme sayısına sahip olmalıdır. Kalıcı hatalar tekrar denenmemelidir. Attempt sayısı metric ve trace içinde görünmelidir. Limit aşımında DLQ kullanılmalıdır. Sonsuz loop production kapasitesini tüketir.

Backoff ve Jitter Var mı?

Retry'lar anında tekrarlanmamalıdır. Exponential backoff downstream sisteme toparlanma fırsatı verir. Jitter worker'ların aynı anda retry yapmasını önler. Maksimum backoff ve deadline birlikte tasarlanmalıdır. Load test recovery davranışını doğrulamalıdır.

Job'lar Idempotent mı?

Aynı job iki kez çalıştırıldığında business sonucu değişmemelidir. Unique constraint veya idempotency key kullanılabilir. External API side effect'leri de korunmalıdır. Duplicate chaos testi yapılmalıdır. Idempotency sadece queue ayarına bırakılmamalıdır.

DLQ Var mı?

Retry limitini aşan job görünür bir alana gitmelidir. DLQ alarm üretmelidir. Payload güvenli biçimde saklanmalıdır. Replay süreci belgelenmelidir. Eski DLQ mesajları düzenli incelenmelidir.

Graceful Shutdown Var mı?

SIGTERM geldiğinde worker yeni job almayı durdurmalıdır. Aktif işler grace period içinde tamamlanmalıdır. Queue acknowledgement doğru sırada yapılmalıdır. Connection ve Worker Threads kapatılmalıdır. Zorla termination senaryosu test edilmelidir.

Event Loop Delay İzleniyor mu?

Node.js backend için yalnızca CPU metriği yeterli değildir. Event loop delay p95 ve p99 değerleri izlenmelidir. CPU-heavy kod deployment sonrası bu metriği bozabilir. Alarm threshold baseline üzerinden seçilmelidir. Endpoint latency ile korelasyon kurulmalıdır.

Queue Lag İzleniyor mu?

Queue depth ile birlikte oldest job age veya consumer lag takip edilmelidir. Worker sağlıklı görünürken backlog büyüyebilir. Job type bazlı SLA uygulanabilir. Autoscaling bu metriklerden yararlanabilir. Recovery time ayrıca ölçülmelidir.

Load Test Yapıldı mı?

Production öncesi yalnızca unit test yeterli değildir. Normal, burst, failure ve recovery workload'ları çalıştırılmalıdır. CPU, memory, event loop ve queue metrikleri kaydedilmelidir. Test gerçekçi veri boyutlarını içermelidir. Sonuçlar kapasite kararına dönüştürülmelidir.

Node.js İçin En İyi Programlama Dili Hangisidir?

Node.js uygulamaları temel olarak JavaScript çalıştırır ve TypeScript geliştirme aşamasında güçlü tip güvenliği ekler. Kurumsal projelerde TypeScript büyük ekipler ve uzun ömürlü codebase için önemli avantaj sağlar. Go, Java veya C# gibi diller de backend için güçlü seçeneklerdir fakat teknoloji seçimi yalnızca dil performansına indirgenmemelidir. Asenkron I/O, queue dayanıklılığı ve veri akışı tasarımı seçilen dil ne olursa olsun önemini korur. Ekibin deneyimi ve sistem gereksinimi teknik kararın merkezinde olmalıdır.

JavaScript

JavaScript Node.js'in doğal runtime dilidir. Async/await, Promise ve stream API'leri doğrudan kullanılabilir. Dinamik tip sistemi küçük projelerde hızlı geliştirme sağlayabilir. Büyük codebase'de contract hataları daha geç ortaya çıkabilir. Güçlü test ve lint standartları önemlidir.

TypeScript

TypeScript JavaScript üzerine statik tip sistemi ekler. Job payload, API response ve worker message sözleşmelerini daha açık hale getirir. Refactoring sırasında kırılmaları derleme aşamasında yakalamaya yardımcı olur. Runtime validation yine gereklidir çünkü dış veri type system'e güvenilir olarak girmez. Büyük Node.js projelerinde güçlü bir varsayılan seçimdir.

Kurumsal Node.js Sistemlerinde TypeScript

Kurumsal sistemlerde çok sayıda ekip aynı API ve event sözleşmelerini kullanabilir. TypeScript shared type ve contract yönetimini kolaylaştırır. Discriminated union ile job türleri güvenli modellenebilir. Generated OpenAPI veya schema tipleri tekrar eden kodu azaltır. Buna rağmen runtime compatibility ve schema versioning ayrı çözülmelidir.

Node.js vs Go

Node.js I/O ağırlıklı uygulamalarda event-driven modeliyle güçlü bir geliştirici deneyimi sunar. Go goroutine ve hafif concurrency modeliyle farklı yaklaşım getirir. CPU ve memory profili workload'a göre değişebilir. Ekip yetkinliği ve kütüphane ekosistemi önemlidir. Tek bir benchmark tüm backend sistemleri için doğru dil seçimini belirleyemez.

Node.js vs Java

Java uzun yıllardır büyük backend sistemlerinde güçlü concurrency ve JVM ekosistemi sunar. Node.js JavaScript ve TypeScript ekipleri için hızlı full-stack paylaşım avantajına sahip olabilir. Runtime davranışı ve thread modeli farklıdır. Seçim deployment, ekip ve workload gereksinimlerine göre yapılmalıdır. İyi mimari kötü concurrency kararlarını hiçbir dilde tamamen telafi etmez.

Node.js vs C#

C# ve .NET güçlü async/await modeli ve geniş backend araçları sunar. Node.js de benzer okunabilir async syntax ile event-driven I/O yaklaşımına sahiptir. İki platformun runtime ve ekosistem karakteri farklıdır. Kurumsal standartlar, deployment hedefi ve ekip becerisi belirleyicidir. Yalnızca sentetik performans tablosuyla karar vermek sağlıklı değildir.

Asenkron I/O İçin Dil Seçiminden Daha Önemli Olan Mimari Kararlar

Concurrency limiti, timeout, retry, idempotency ve backpressure yanlışsa iyi dil seçimi sistemi kurtarmaz. Büyük veriyi RAM'e almak her runtime'da risklidir. Downstream sistemi sınırsız istekle doldurmak her teknolojide sorun yaratır. Dil performansın yalnızca bir bileşenidir. Uçtan uca sistem davranışı daha büyük etkiye sahiptir.

Yazılımcı Olmak İçin Ne Yapmalı? Asenkron Backend Yol Haritası

Asenkron backend geliştirmeyi öğrenmek yalnızca async/await sözdizimini ezberlemekten çok daha geniş bir yolculuktur. JavaScript ve TypeScript temeli üzerine event loop, stream, database ve queue bilgisi eklenmelidir. Worker Threads ile CPU işlerinin, broker sistemleriyle dağıtık işlerin nasıl ayrıldığı anlaşılmalıdır. Observability ve distributed systems bilgisi production sorunlarını çözme becerisini geliştirir. En hızlı öğrenme yolu küçük çalışan projeler kurup gerçek yük testleri yapmaktır.

JavaScript

Scope, closure, object, function ve event-driven programlama temelleri iyi anlaşılmalıdır. Promise öğrenmeden önce fonksiyon ve hata davranışı sağlam olmalıdır. Array metotlarının CPU maliyeti de fark edilmelidir. Event emitter ve module sistemi pratik edilmelidir. Küçük API projeleri temel için uygundur.

TypeScript

Interface, type alias, union ve generic kavramları öğrenilmelidir. API request ve response tipleri modellenebilir. Worker message ve job payload sözleşmeleri güzel pratik alanlarıdır. Runtime validation ile compile-time type farkı anlaşılmalıdır. Zod benzeri bir doğrulama yaklaşımıyla entegrasyon öğrenilebilir.

Promise ve async/await

Promise lifecycle, resolve ve reject davranışı anlaşılmalıdır. async/await ile sequential ve concurrent akış farkı pratik edilmelidir. Promise.all, allSettled, race ve any küçük örneklerle test edilebilir. Cancellation ayrı konu olarak öğrenilmelidir. Sınırsız concurrency'nin riskleri erkenden görülmelidir.

Event Loop

Call stack, microtask ve event loop fazları temel seviyede anlaşılmalıdır. process.nextTick ve setImmediate davranışı küçük deneylerle görülebilir. Uzun senkron döngünün timer'ı nasıl geciktirdiği ölçülebilir. monitorEventLoopDelay kullanmak öğretici olur. Teori profiling ile birleştiğinde anlam kazanır.

Streams

Readable, Writable ve Transform stream ile dosya pipeline'ı kurulabilir. Büyük dosya ile readFile ve stream memory farkı ölçülebilir. pipeline API ve error handling öğrenilmelidir. Object mode üzerinden CSV işlemek iyi egzersizdir. Backpressure testleri özellikle yapılmalıdır.

Backpressure

Hızlı producer ve yavaş consumer senaryosu bilinçli olarak oluşturulabilir. write false ve drain davranışı gözlenebilir. highWaterMark değişikliklerinin memory üzerindeki etkisi ölçülebilir. Queue depth benzer kavramla ilişkilendirilebilir. Bu bilgi yüksek hacimli sistemlerde çok değerlidir.

Database

Connection pool, transaction ve query plan temelleri öğrenilmelidir. Büyük result set cursor ile işlenebilir. Batch insert performansı tek tek insert ile karşılaştırılabilir. Index ve pagination davranışı test edilebilir. Async uygulamanın database sınırlarına saygı duyması gerektiği görülür.

Worker Threads

Basit CPU-heavy fonksiyon main thread ve worker üzerinde karşılaştırılabilir. Worker startup maliyeti ölçülmelidir. Sonra küçük worker pool geliştirilebilir. ArrayBuffer transferi deneyilebilir. Event loop delay farkı benchmark edilmelidir.

Redis ve Queue

Basit background job sistemi kurarak producer ve worker ayrımı öğrenilebilir. Retry, backoff ve delayed job eklenebilir. Duplicate job ile idempotency test edilir. Graceful shutdown uygulanabilir. Queue dashboard metrikleri gözlemlenebilir.

RabbitMQ/Kafka

RabbitMQ ile exchange, queue, ack ve prefetch öğrenilebilir. Kafka ile topic, partition, consumer group ve offset pratik edilir. Aynı senaryoyu iki araçla çözmek farkları görmeyi kolaylaştırır. Replay ve work queue semantiği karşılaştırılabilir. Araçları isimlerinden değil davranışlarından öğrenmek önemlidir.

Observability

Log, metric ve trace üçlüsü küçük projeye erkenden eklenmelidir. Correlation ID request'ten worker'a taşınabilir. Event loop delay ve queue wait metric olarak tutulabilir. Basit dashboard oluşturmak production düşüncesini geliştirir. Hata senaryosunu yalnızca console.log ile çözmemek gerekir.

Distributed Systems

Retry, duplicate delivery, partial failure ve eventual consistency kavramları öğrenilmelidir. Transactional Outbox küçük projeyle uygulanabilir. Network'ün her an hata verebileceği varsayımı geliştirilmelidir. Idempotency distributed systems için temel beceridir. Chaos test pratikleri teoriyi somutlaştırır.

Open Source ve İşbirliği ile Async Node.js Yetkinliği

Asenkron Node.js bilgisini geliştirmek için yalnızca eğitim içeriği tüketmek yeterli değildir. Node.js Core, libuv ve kullanılan queue kütüphanelerinin issue ve pull request'lerini incelemek gerçek production problemlerini görmeyi sağlar. Küçük benchmark paylaşmak bile topluluk içinde teknik geri bildirim almanın iyi yoludur. Açık kaynak katkısı kod review kültürünü güçlendirir. En değerli öğrenme çoğu zaman gerçek bug tartışmalarında ortaya çıkar.

Node.js Core

Node.js repository'sindeki issue ve release notları runtime davranışını anlamak için güçlü kaynaktır. Event loop, stream ve worker değişikliklerinin nasıl tartışıldığı görülebilir. Küçük documentation katkıları başlangıç için uygundur. Core kodunun tamamını anlamak gerekmez. İlgilenilen modülden başlamak daha verimlidir.

libuv

libuv kaynak kodu event loop ve thread pool altyapısına daha yakından bakmayı sağlar. Platformlar arası I/O davranışının nasıl soyutlandığı görülebilir. Her backend geliştiricisinin libuv geliştiricisi olması gerekmez. Ancak temel issue tartışmalarını okumak yanlış varsayımları azaltır. Özellikle thread pool performans problemlerinde faydalıdır.

BullMQ

BullMQ issue sayfaları retry, stalled job ve Redis davranışıyla ilgili gerçek kullanım örnekleri sunar. Documentation örnekleriyle küçük queue projesi kurulabilir. Edge case'leri anlamak production hazırlığını artırır. Katkı için test veya dokümantasyon alanları değerlendirilebilir. Kütüphane sürüm notları upgrade öncesi okunmalıdır.

pg-boss

pg-boss PostgreSQL tabanlı job processing modelini anlamak için iyi çalışma alanıdır. Transactional enqueue davranışı küçük demo ile incelenebilir. Database query ve locking etkisi gözlemlenebilir. Issue tartışmaları gerçek operasyon gereksinimlerini gösterir. Redis tabanlı queue ile karşılaştırmalı benchmark öğretici olabilir.

RabbitMQ

RabbitMQ documentation ve örnekleri ack, prefetch ve routing davranışını anlamaya yardımcı olur. Node.js client ile producer-consumer laboratuvarı kurulabilir. Worker crash sonrası redelivery test edilebilir. Prefetch değerleri değiştirilerek throughput ölçülebilir. Bu deneyler teoriye göre daha kalıcı öğrenme sağlar.

KafkaJS

KafkaJS üzerinden Node.js producer ve consumer group davranışı pratik edilebilir. Partition key, offset commit ve rebalance gibi konular gerçek kodla daha anlaşılır hale gelir. Duplicate event senaryosu test edilebilir. Replay için offset reset deneyleri yapılabilir. Kütüphane sürüm notları client davranışını takip etmek için önemlidir.

OpenTelemetry

OpenTelemetry ile HTTP, queue ve worker trace'leri bağlamak production observability becerisini geliştirir. Auto instrumentation ile manuel span farkı öğrenilebilir. Metric cardinality sorunları küçük projede görülebilir. Trace context queue metadata üzerinden taşınabilir. Performance overhead load test ile ölçülebilir.

GitHub Issue İncelemek

Issue incelemek yalnızca bug okumak değildir. Kullanıcıların hangi edge case'lerde zorlandığı görülür. Maintainer'ın neden belirli tasarım kararını tercih ettiği anlaşılabilir. Minimal reproduction hazırlama becerisi gelişir. Bu çalışma problem çözme yeteneğini güçlendirir.

Pull Request

Pull request göndermek kodun başka geliştiriciler tarafından incelenmesini sağlar. Küçük test düzeltmesi veya documentation katkısı iyi başlangıçtır. Review yorumları teknik iletişim becerisini geliştirir. Değişikliğin backward compatibility etkisi düşünülür. Açık kaynak katkısı düzenli yapıldığında güçlü öğrenme ortamı oluşturur.

Benchmark Paylaşmak

Benchmark sonucu paylaşırken test ortamı, dataset ve metodoloji açık olmalıdır. Sadece tek throughput rakamı yanıltıcı olabilir. CPU, memory ve latency değerleri birlikte verilmelidir. Kaynak kodu paylaşmak sonucun tekrarlanabilirliğini artırır. Topluluk geri bildirimi test hatalarını fark etmeye yardımcı olur.

Diyarbakır Yazılım Topluluğu ile Node.js Asenkron Sistemler

Diyarbakır Yazılım Topluluğu içinde Node.js asenkron sistemler üzerine ortak çalışma yapmak, kavramları yalnızca teorik düzeyde bırakmadan gerçek projelere taşımak için güçlü bir yöntemdir. Event loop, stream, Worker Threads ve queue sistemleri workshop formatında küçük deneylerle öğrenilebilir. Ortak açık kaynak projeleri farklı geliştiricilerin aynı mimari kararları tartışmasını sağlar. Proje ve topluluk çalışmalarını https://www.diyarbakiryazilim.com.tr/projects ve https://www.diyarbakiryazilim.com.tr/about sayfalarından incelemek mümkündür. Node.js performans ve backend danışmanlığı yakınımda gibi yerel niyetli aramalarda da teknik topluluklarla doğrudan iletişim kurmak, deneyim paylaşımı ve öğrenme açısından verimli bir başlangıç noktası olabilir.

Diyarbakır Yazılım Topluluğu İçinde Backend Çalışmaları

Backend çalışmaları yalnızca REST endpoint geliştirmekle sınırlı tutulmamalıdır. Concurrency, database, queue ve observability aynı proje içinde birlikte ele alınabilir. Katılımcılar farklı çözüm alternatiflerini benchmark üzerinden karşılaştırabilir. Kod review toplantılarında event loop bloklayan noktalar incelenebilir. Bu tür çalışmalar production düşünme alışkanlığı kazandırır.

Event Loop Workshopları

Event loop workshop'unda setTimeout, Promise, nextTick ve setImmediate sıraları küçük örneklerle test edilebilir. Daha sonra büyük senkron döngünün timer latency'sine etkisi ölçülebilir. monitorEventLoopDelay ile sonuç sayısallaştırılabilir. Katılımcılar yalnızca çıktıyı ezberlemek yerine nedenini tartışabilir. Bu yöntem kavramın kalıcı biçimde anlaşılmasını sağlar.

Stream ve Backpressure Laboratuvarları

Büyük CSV dosyası üzerinden stream pipeline laboratuvarı kurulabilir. readFile ve stream memory farkı ölçülebilir. Yavaş writable oluşturarak backpressure davranışı gözlenebilir. highWaterMark değiştirilip throughput ve memory karşılaştırılabilir. Sonuçlar ortak benchmark raporuna dönüştürülebilir.

Worker Thread Atölyeleri

CPU-heavy örnek önce main thread üzerinde çalıştırılıp event loop delay ölçülebilir. Aynı görev Worker Thread'e taşınarak fark gözlenir. Ardından her task için worker açmanın maliyeti test edilir. Son aşamada basit worker pool yazılabilir. Böylece teori doğrudan ölçümle pekişir.

Queue ve Messaging Projeleri

Katılımcılar küçük job queue ve event-driven proje geliştirebilir. Retry, backoff, idempotency ve DLQ gerçek kod üzerinde uygulanır. RabbitMQ prefetch veya Redis queue concurrency değerleri test edilebilir. Failure senaryoları bilinçli olarak oluşturulur. Proje sonunda mimari karar dokümanı hazırlanabilir.

Open Source ve İşbirliği

Ortak repository üzerinden issue, pull request ve code review akışı oluşturulabilir. Katılımcılar yalnızca kendi kodunu değil başkasının yaklaşımını da değerlendirir. Benchmark sonuçları repository içinde dokümante edilebilir. Küçük görevler yeni başlayanların katkısını kolaylaştırır. Maintainer rolü teknik liderlik deneyimi kazandırır.

Diyarbakır'daki En İyi Yazılımcılarla Teknik Deneyim Paylaşımı

Teknik yetkinliği yalnızca bireysel çalışma üzerinden geliştirmek zaman alabilir. Deneyimli geliştiricilerin gerçek incident ve performans problemlerini anlatması teorik bilgiyi bağlama oturtur. Bir yaklaşımın neden production'da başarısız olduğunu tartışmak önemli öğrenme sağlar. Kod ve benchmark üzerinden yapılan geri bildirim daha somut olur. Topluluk ortamı düzenli teknik iletişimi destekleyebilir.

Ortak Proje Fikirleri

Asenkron Node.js konularını öğrenmek için küçük ama ölçülebilir ortak projeler seçmek faydalıdır. Her proje belirli bir problemi hedeflemelidir. Memory, throughput ve p99 latency için ortak metrik seti belirlenebilir. Farklı mimari seçenekler aynı workload üzerinde karşılaştırılabilir. Sonuçların açık kaynak paylaşılması topluluğun bilgi birikimini artırır.

Büyük CSV Processing Pipeline

Bu proje object storage, queue, stream parser ve batch database write bileşenlerini bir araya getirebilir. Büyük dosya RAM'e alınmadan işlenir. Progress ve cancellation desteği eklenebilir. Backpressure ile memory profili ölçülebilir. Sonuçlar gerçek bir import servisi için güçlü referans oluşturur.

BullMQ Worker Sistemi

BullMQ tabanlı örnek sistem e-posta, rapor veya webhook job'larını işleyebilir. Retry, backoff, priority ve delayed job özellikleri uygulanabilir. Worker crash ve Redis kesintisi test edilebilir. Queue dashboard metrikleri hazırlanabilir. Job idempotency için database unique constraint kullanılabilir.

RabbitMQ Event Processing

RabbitMQ projesinde exchange, routing key ve birden fazla consumer queue kullanılabilir. Prefetch değerleri farklı worker hızlarında test edilir. Consumer crash sonrası redelivery gözlenir. Duplicate mesaj için idempotent handler yazılır. DLQ ve retry topology ayrıca eklenebilir.

Worker Thread Benchmark Projesi

Aynı CPU-heavy görev main thread, tek worker ve worker pool üzerinde karşılaştırılabilir. Throughput, event loop delay ve memory değerleri kaydedilir. Worker sayısı CPU core seviyelerine göre artırılır. Transferable ArrayBuffer etkisi ayrı test edilir. Sonuçlar hangi task boyutunda worker'ın anlamlı hale geldiğini gösterebilir.

Sık Sorulan Sorular

Node.js asenkron sistemleriyle ilgili sorular genellikle event loop, Promise concurrency, stream, Worker Threads ve queue semantiği etrafında yoğunlaşır. Tek bir yöntemin her probleme uygun olmadığını baştan kabul etmek cevapları daha net hale getirir. I/O ve CPU ayrımı çoğu kararın başlangıç noktasıdır. Büyük veri için backpressure, uzun iş için durable queue ve duplicate teslim için idempotency temel prensiplerdir. Aşağıdaki cevaplar bu kararları kısa ve pratik biçimde özetler.

Node.js asenkron nasıl çalışır?

Node.js JavaScript kodunu ana event loop üzerinde yürütür ve I/O işlemlerini event-driven altyapıyla koordine eder. İşlem tamamlandığında callback veya Promise devamı çalıştırılır. Bu sırada diğer bağlantılar ilerleyebilir. CPU-heavy senkron kod ana thread'i yine bloke eder. Bu nedenle asenkronluk non-blocking I/O ile birlikte düşünülmelidir.

Node.js gerçekten tek thread midir?

Ana JavaScript execution temel olarak tek thread üzerinde ilerler. Ancak Node.js process'i yalnızca tek işletim sistemi thread'inden oluşmaz. libuv thread pool ve Worker Threads ek thread'ler kullanabilir. Bazı native kütüphaneler de kendi thread yapılarına sahiptir. "Tek thread" ifadesi esas olarak ana JavaScript event loop'unu anlatır.

Event loop nedir?

Event loop hazır callback ve görevlerin JavaScript üzerinde ne zaman yürütüleceğini koordine eder. Timers, poll ve check gibi fazları vardır. Promise microtask'leri ayrıca yüksek öncelikli queue davranışı gösterir. Uzun senkron kod event loop dönüşünü geciktirebilir. Bu gecikme kullanıcı latency'sine doğrudan yansıyabilir.

Concurrency ile parallelism arasındaki fark nedir?

Concurrency birden fazla işin aynı zaman aralığında ilerlemesidir. Parallelism ise hesaplamaların gerçekten farklı işlem kaynaklarında aynı anda yürütülmesidir. Node.js I/O işlerinde concurrency konusunda güçlüdür. Worker Threads CPU işlemlerinde gerçek parallelism sağlayabilir. İki kavram farklı performans problemlerini çözer.

Promise.all paralel çalışır mı?

Promise.all Promise'leri birlikte takip eder ve I/O görevleri concurrent ilerleyebilir. Bu gerçek CPU parallelism anlamına gelmez. Saf JavaScript CPU fonksiyonları ana thread üzerinde sırayla yürütülmeye devam eder. Worker Threads farklı CPU çekirdeklerinde paralellik sağlayabilir. Bu nedenle "paralel" kelimesini iş türüne göre dikkatli kullanmak gerekir.

Promise.all ne zaman kullanılmamalıdır?

Çok büyük görev listelerinde sınırsız Promise.all kullanılmamalıdır. Binlerce HTTP isteği, database sorgusu veya büyük response memory ve bağlantı sınırlarını aşabilir. Bounded concurrency daha güvenlidir. Partial success gerekiyorsa allSettled değerlendirilebilir. Downstream capacity her zaman hesaba katılmalıdır.

Promise concurrency nasıl sınırlandırılır?

Promise pool veya p-limit benzeri araçlarla aynı anda çalışan görev sayısı sınırlandırılabilir. Sabit worker sayısı queue'dan sıradaki görevi alır. Bir görev bitmeden yenisi başlamaz. Limit database pool veya API rate limit değerleriyle uyumlu seçilir. Benchmark optimum değeri bulmaya yardımcı olur.

Node.js stream nedir?

Stream veriyi tamamını RAM'e almadan parça parça işlemek için kullanılan Node.js soyutlamasıdır. Readable, Writable, Duplex ve Transform türleri bulunur. Büyük dosya ve network akışlarında özellikle değerlidir. Backpressure tüketici hızına göre producer'ı kontrol eder. pipeline API hata ve cleanup yönetimini kolaylaştırır.

Backpressure nedir?

Backpressure hızlı producer'ın yavaş consumer karşısında veri üretim hızını azaltmasını sağlayan mekanizmadır. Aksi halde buffer sürekli büyüyebilir. Stream write false sonucu bunun pratik sinyallerinden biridir. Queue sisteminde de backlog benzer kapasite dengesizliğini gösterir. Backpressure memory ve downstream servisleri korur.

pipeline() ne işe yarar?

stream.pipeline birden fazla stream aşamasını güvenli biçimde birbirine bağlar. Backpressure zincir boyunca korunur. Hata propagation ve resource cleanup daha kolay yönetilir. Promise sürümü async/await ile kullanılabilir. Büyük veri pipeline'larında manuel .pipe zincirine göre daha güvenli bir varsayımdır.

Async iterator nedir?

Async iterator değerleri zaman içinde Promise tabanlı biçimde üreten iterator modelidir. for await...of ile tüketilebilir. Paginated API, database cursor ve stream gibi lazy veri kaynaklarında kullanışlıdır. Tüm veriyi baştan array'e toplamak gerekmez. Cancellation ve cleanup doğru uygulanmalıdır.

Worker Threads ne zaman kullanılmalı?

Worker Threads uzun süren CPU-heavy JavaScript görevlerinde kullanılmalıdır. Görüntü dönüşümü, matematiksel hesaplama veya büyük parsing buna örnektir. Normal HTTP veya database I/O için genellikle gerekli değildir. Production sisteminde worker pool kullanmak daha verimlidir. Task dispatch maliyeti benchmark edilmelidir.

Worker Threads database sorgularını hızlandırır mı?

Genellikle hayır, çünkü database sorgusu I/O-bound bir işlemdir. Sorgunun süresini database query planı ve kaynakları belirler. Worker'a taşımak network veya database işlemini hızlandırmaz. Connection pool ve sorgu optimizasyonu daha doğru alandır. Yalnızca sonuç üzerinde ağır CPU transform varsa worker yararlı olabilir.

Worker Thread ile child process arasındaki fark nedir?

Worker Thread aynı process içinde ayrı JavaScript thread'i çalıştırır. Child process işletim sistemi seviyesinde ayrı proses oluşturur. Child process daha güçlü izolasyon ve harici executable çalıştırma avantajı sunar. Worker Threads veri paylaşımında daha düşük maliyetli seçeneklere sahiptir. Seçim isolation ve workload türüne göre yapılır.

BullMQ nedir?

BullMQ Redis tabanlı Node.js job queue çözümüdür. Retry, delayed job, priority ve worker concurrency gibi özellikler sunar. Background e-posta veya rapor işlemlerinde kullanılabilir. Redis kapasitesi ve persistence ayrıca yönetilmelidir. Idempotency business uygulamasında yine gereklidir.

BullMQ ve RabbitMQ arasındaki fark nedir?

BullMQ job processing odaklı Redis tabanlı bir Node.js çözümüdür. RabbitMQ genel amaçlı message broker olarak exchange, routing ve acknowledgement modelleri sunar. RabbitMQ prefetch consumer backpressure kontrolünde güçlüdür. BullMQ Node.js job lifecycle kullanımını sadeleştirebilir. Seçim routing ve operasyon gereksinimlerine göre yapılmalıdır.

Kafka ile job queue arasındaki fark nedir?

Kafka durable event log ve replay modeline odaklanır. Job queue belirli görevin worker tarafından tamamlanması, retry edilmesi ve failed state'e gitmesi modelini öne çıkarır. Kafka üzerinde task processing yapılabilir. Ancak completion semantiği daha fazla uygulama kodu gerektirebilir. Geçmiş event replay önemliyse Kafka güçlü adaydır.

Dead Letter Queue nedir?

DLQ normal retry sınırını aşan veya işlenemeyen mesajların ayrı alanda tutulduğu queue'dur. Poison message ana iş akışını sürekli meşgul etmez. DLQ alarm üretmelidir. Sorun düzeltildikten sonra kontrollü replay yapılabilir. Mesajların burada unutulmaması gerekir.

Idempotent job nedir?

Idempotent job birden fazla kez çalıştırılsa bile business etkisi bir kez gerçekleşmiş gibi kalan görevdir. Retry ve redelivery sistemlerinde bu özellik kritiktir. Unique constraint veya idempotency key kullanılabilir. Harici side effect'ler de aynı korumaya sahip olmalıdır. Duplicate teslim normal sistem davranışı olarak test edilmelidir.

Transactional Outbox nedir?

Transactional Outbox database değişikliği ile event yayınlama arasındaki dual-write sorununu azaltır. Business kayıt ve outbox event aynı transaction içinde yazılır. Ayrı publisher event'i broker'a gönderir. Duplicate publish ihtimali nedeniyle consumer idempotent olmalıdır. Event kaybına karşı güçlü bir pattern sunar.

Event Loop Delay nasıl ölçülür?

Node.js perf_hooks içindeki monitorEventLoopDelay gibi araçlarla event loop gecikmesi ölçülebilir. P50, p95, p99 ve max değerler izlenmelidir. CPU-heavy işlem veya garbage collection tail değerlerini yükseltebilir. HTTP latency ile korelasyon faydalıdır. Production baseline alarm threshold belirlemeye yardımcı olur.

Node.js için JavaScript mi TypeScript mi?

Her ikisi de Node.js üzerinde çalışacak uygulamalar geliştirmek için kullanılabilir. TypeScript büyük codebase, API contract ve job payload modellerinde ek tip güvenliği sağlar. Runtime validation yine gereklidir. Küçük proje JavaScript ile rahatça ilerleyebilir. Kurumsal ekiplerde TypeScript çoğu zaman daha güçlü varsayılandır.

Node.js yazılımcısı olmak için ne öğrenilmeli?

JavaScript, TypeScript ve Promise temeli ilk adımdır. Event loop, stream ve backpressure konuları mutlaka öğrenilmelidir. Database, queue ve Worker Threads backend kapasitesini genişletir. Observability ve distributed systems bilgisi production becerisini geliştirir. Açık kaynak proje ve gerçek benchmark çalışmaları öğrenmeyi hızlandırır.

Open source ve işbirliği Node.js kariyerine nasıl katkı sağlar?

Açık kaynak gerçek issue ve code review süreçlerini görmeyi sağlar. Kütüphane davranışını yalnızca dokümantasyon düzeyinde değil, tasarım kararlarıyla birlikte öğrenirsiniz. Pull request teknik iletişim becerisini geliştirir. Benchmark ve örnek proje paylaşımı görünür teknik portföy oluşturur. Topluluk çalışması farklı deneyim seviyelerinden geri bildirim alma fırsatı sunar.

Node.js sistemlerinde asenkron veri işleme nasıl çalışır ve hangi yöntemler kullanılır?

Node.js asenkron veri işleme temel olarak event loop, non-blocking I/O ve Promise tabanlı kontrol akışına dayanır. Kısa I/O görevlerinde async/await, büyük veri için stream, lazy kaynaklar için async iterator kullanılabilir. CPU-heavy görevler Worker Threads ile ayrılabilir. Uzun süreli işler durable queue üzerinde background worker'lara taşınabilir. Seçim veri boyutu, işlem süresi ve dayanıklılık ihtiyacına göre yapılmalıdır.

Promise async/await callback ve event-driven yaklaşımlar arasındaki farklar nelerdir?

Callback işlemin tamamlanmasından sonra çağrılacak fonksiyonu doğrudan verir. Promise sonucu gelecekte tamamlanacak değer olarak modeller ve async/await bu Promise akışını daha okunabilir hale getirir. Event-driven yaklaşım tek bir sonuçtan ziyade zaman içinde oluşan birden fazla olaya uygundur. Bu mekanizmaların hiçbiri CPU hesabını otomatik olarak başka thread'e taşımaz. Modern uygulamalarda Promise ve async/await çoğu tek sonuçlu I/O işlemi için iyi varsayılandır.

Yüksek hacimli veri işlemlerinde Node.js event loop performansı nasıl optimize edilir?

Öncelikle event loop üzerinde uzun süren senkron CPU işlemleri belirlenmelidir. Büyük veri stream edilmeli, concurrency sınırlandırılmalı ve gereksiz büyük JSON işlemleri azaltılmalıdır. CPU-heavy görevler Worker Thread pool'a taşınabilir. Event loop delay ile ELU p95 ve p99 seviyesinde izlenmelidir. Optimizasyon kararı gerçek load test ve profiling sonuçlarına dayanmalıdır.

Worker Threads Streams ve message queue yapıları asenkron veri işleme süreçlerinde nasıl kullanılmalıdır?

Streams büyük veriyi düşük memory ile taşımak ve backpressure uygulamak için kullanılır. Worker Threads CPU-heavy JavaScript hesaplamasını ana event loop'tan ayırır. Message queue ise uzun süreli veya retry gerektiren işi HTTP lifecycle'dan çıkarır. Bu araçlar birbirinin alternatifi olmak zorunda değildir. Örneğin queue worker büyük dosyayı stream edip CPU dönüşümünü Worker Thread pool'a gönderebilir.

Node.js asenkron veri işleme ve performans optimizasyonu konusunda yakınımda danışmanlık veya eğitim nerede bulabilirim?

Yerel teknik topluluklar, workshoplar ve ortak açık kaynak projeleri bu alanda uygulamalı öğrenmek için iyi başlangıç noktalarıdır. Diyarbakır Yazılım Topluluğu'nun çalışmalarını https://www.diyarbakiryazilim.com.tr üzerinden takip edebilirsiniz. Backend, performans, veritabanı ve bağlantı optimizasyonu gibi tamamlayıcı konularda https://www.diyarbakiryazilim.com.tr/posts/mongodb-atlas-sunucu-baglanti-optimizasyonu-ve-sorun-giderme içeriği de teknik bağlam sunar. Uygulamalı eğitimde event loop ölçümü, stream pipeline, queue retry ve Worker Thread benchmark gibi somut çalışmaların bulunmasına dikkat etmek gerekir. En verimli öğrenme modeli gerçek proje verileriyle ölçüm yapıp sonuçları deneyimli geliştiricilerle birlikte değerlendirmektir.

Sonuç: Node.js'te Doğru Asenkron Strateji Nasıl Seçilir?

Node.js Sistemlerinde Asenkron Veri İşleme Stratejileri tek bir Promise yöntemi ya da queue kütüphanesi seçmekten çok daha geniş bir sistem tasarımı konusudur. Önce iş yükünün I/O mu CPU mu olduğu belirlenmeli, ardından concurrency, veri büyüklüğü ve dayanıklılık gereksinimi değerlendirilmelidir. Büyük veri stream edilmeli, CPU işi Worker Thread pool'a taşınmalı ve uzun görevler HTTP request lifecycle'dan çıkarılmalıdır. Queue kullanılan sistemlerde retry, idempotency, DLQ, graceful shutdown ve observability en baştan tasarlanmalıdır. Bu yaklaşım yalnızca daha hızlı değil, yük altında davranışı tahmin edilebilen backend sistemleri kurmaya yardımcı olur.

Önce İş Yükünü I/O ve CPU Olarak Sınıflandırın

Her optimizasyon kararının ilk sorusu sürenin nerede geçtiğidir. Database veya ağ beklemesi I/O-bound davranırken yoğun hesaplama CPU-bound davranır. I/O için async API ve controlled concurrency daha uygundur. CPU işi için Worker Threads değerlendirilebilir. Yanlış sınıflandırma gereksiz altyapı veya event loop bloklanması üretir.

Bağımsız I/O İşlerinde Concurrency Kullanın ama Sınırlandırın

Bağımsız HTTP veya database operasyonları aynı anda başlatılarak waterfall latency azaltılabilir. Ancak concurrency downstream kapasitesini aşmamalıdır. Promise pool bu dengeyi yönetir. Rate limit ve connection pool doğal üst sınırlar sunar. En uygun değer benchmark ile belirlenmelidir.

Büyük Veriyi RAM'e Almak Yerine Stream Edin

Büyük dosya veya response'ları tek Buffer içinde toplamak memory riskidir. Stream veriyi kademeli işleyerek aktif veri miktarını sınırlar. pipeline backpressure ve error propagation sağlar. Object mode kullanımında nesne boyutu ayrıca izlenmelidir. Multi-GB veri için streaming temel tasarım kararı olmalıdır.

Producer ve Consumer Arasında Backpressure Uygulayın

Producer tüketiciden hızlı olduğunda sistemin yavaşlama mekanizması bulunmalıdır. Stream write false ve drain bunun yerel örneğidir. Queue depth ve rate limiting dağıtık sistemde benzer görevi görür. Sınırsız buffer yalnızca problemi erteler. Backpressure sistem kapasitesinin doğal ifadesidir.

CPU İşlerini Worker Pool'a Taşıyın

Uzun hesaplamalar ana event loop üzerinde çalıştırılmamalıdır. Worker Thread pool CPU çekirdeklerinden paralel yararlanabilir. Her görev için yeni worker oluşturmak yerine reuse tercih edilmelidir. Pool queue ve memory sınırı içermelidir. Gerçek kazanç profiling ve benchmark ile doğrulanmalıdır.

Uzun Süreli İşleri HTTP Request Lifecycle'dan Çıkarın

Dakikalar süren işler 202 Accepted ve background queue modeliyle ayrılmalıdır. Kullanıcıya job ID ve status endpoint sunulabilir. Worker deployment ve crash durumundan bağımsız retry yapabilir. Sonuç notification veya polling ile iletilebilir. Bu tasarım kullanıcı deneyimi ile sistem dayanıklılığını birlikte iyileştirir.

Queue Sistemlerinde Retry, Idempotency ve DLQ'yu Baştan Tasarlayın

Queue kullanmak otomatik olarak güvenilir işlem anlamına gelmez. Duplicate teslim olabileceği için job idempotent olmalıdır. Retry yalnızca geçici hatalara uygulanmalı ve backoff ile jitter içermelidir. Kalıcı başarısızlık DLQ'ya taşınmalıdır. Transactional Outbox database ile event publish tutarlılığını güçlendirebilir.

Event Loop, Queue Lag ve End-to-End Latency'yi Sürekli Ölçün

HTTP request latency tek başına yeterli değildir. Event loop delay, ELU, queue wait, processing duration ve end-to-end job latency birlikte izlenmelidir. P95 ve p99 değerleri ortalamadan daha fazla bilgi verebilir. Deployment sonrası regresyon metriklerle hızla fark edilmelidir. Ölçüm olmadan yapılan performans ayarı tahminden öteye geçmez.

Asenkronluğu Kod Stili Değil Uçtan Uca Sistem Tasarımı Olarak Ele Alın

async/await okunabilir kod yazmayı kolaylaştırır fakat güvenilir asenkron sistem tek başına bu sözdiziminden oluşmaz. Concurrency sınırı, backpressure, cancellation, retry, idempotency ve observability aynı tasarımın parçalarıdır. İyi sistem normal yükte hızlı olduğu kadar hata ve peak yük altında da kontrollü davranmalıdır. Node.js performans ve backend mimarisi üzerine topluluk çalışmalarına, projelere ve teknik paylaşımlara ulaşmak için https://www.diyarbakiryazilim.com.tr adresini ziyaret edebilirsiniz. Sağlam bir async mimari kurmanın en iyi yolu küçük varsayımları gerçek ölçümle doğrulamak ve sistemi baştan itibaren failure senaryolarına hazır tasarlamaktır.

share
share:

İletişim

Birlikte inşa edelim

İşbirliklerine, ilginç sorunlara ve kod, tasarım ile diğer konular hakkında sohbetlere açığız.

bize ulaş→

Bizi başka yerlerde bulun

GitHub
@diyarbakir-yazilim
Twitter
@diyaryazilim
LinkedIn
diyarbakir-yazilim-toplulugu
Instagram
@diyarbakiryazilim
YouTube
@diyarbakiryazilim
Slack
diyarbakiryazilim
WhatsApp
Topluluğa Katıl
Email
info@diyarbakiryazilim.org
Sevgiyle ve kodla inşa ediliyor

© 2026 Diyarbakır Yazılım Topluluğu — Tüm hakları saklıdır.