Dağıtık Sistemler Kapsamlı Rehberi
Temel Kavramlar, Desenler (Pattern) ve En İyi Pratikler
Güvenilir, ölçeklenebilir dağıtık sistemler inşa etmek için kullanılan temel teori ve sahada kanıtlanmış mühendislik desenlerini derinlemesine inceleyen bir referans rehber.
İçindekiler
- CAP Teoremi
- Tutarlılık (Consistency)
- Nihai Tutarlılık (Eventual Consistency)
- Güçlü Tutarlılık (Strong Consistency)
- Replikasyon (Replication)
- Sharding
- Partitioning (Bölümleme)
- Lider Seçimi (Leader Election)
- Dağıtık Kilit (Distributed Lock)
- Konsensüs (Consensus)
- Quorum
- Hata Toleransı (Fault Tolerance)
- Hata Tespiti (Failure Detection)
- İdempotentlik (Idempotency)
- Retry (Yeniden Deneme)
- Exponential Backoff (Üstel Geri Çekilme)
- Circuit Breaker (Devre Kesici)
- Timeout (Zaman Aşımı)
- Rate Limiting (Hız Sınırlama)
- Backpressure (Geri Basınç)
- Graceful Shutdown (Zarif Kapanış)
- Disaster Recovery (Felaket Kurtarma)
1. CAP Teoremi
Tanım
CAP teoremi (Eric Brewer, 2000), bir ağ bölünmesi (network partition) yaşandığında dağıtık bir veri sisteminin aşağıdaki üç özellikten aynı anda sadece ikisini garanti edebileceğini söyler:
- Consistency (Tutarlılık, C) — her okuma isteği en son yazılan veriyi ya da bir hata döner. Tüm node’lar aynı anda aynı veriyi görür.
- Availability (Erişilebilirlik, A) — her istek, en son yazılan veriyi içerdiği garanti edilmese bile, bir (hatasız) yanıt alır.
- Partition Tolerance (Bölünme Toleransı, P) — node’lar arasındaki mesajların bir kısmı kaybolsa veya gecikse dahi sistem çalışmaya devam eder.
Gerçek Trade-off
Gerçek dünyadaki her dağıtık sistemde ağ bölünmeleri mutlaka yaşanacağı için P, opsiyonel değildir — buna göre tasarım yapmak zorundasınız. Asıl seçim, bölünme sırasında C ile A arasındadır:
- CP sistemleri: eski/tutarsız veri döndürmek yerine istekleri reddeder (veya bekletir). Örnekler: ZooKeeper, etcd, HBase, senkron replikasyon modunda çalışan çoğu ilişkisel veritabanı.
- AP sistemleri: bazı node’lar senkronize olamasa bile isteklere yanıt vermeye devam eder, geçici tutarsızlığı kabul eder. Örnekler: Cassandra, DynamoDB, Riak, CouchDB.
PACELC Genişletmesi
CAP yalnızca bölünme sırasındaki davranışı tanımlar. PACELC (Daniel Abadi) bunu genişletir: “eğer bir bölünme (Partition) varsa A veya C seç; aksi halde (Else), Latency (gecikme) veya Consistency (tutarlılık) seç.” Bu, ağ sağlıklıyken bile var olan gecikme-tutarlılık trade-off’unu yakalar.
En İyi Pratikler
- CAP’i ikili bir pazarlama etiketi olarak görmeyin (“biz AP’yiz” / “biz CP’yiz”) — gerçek sistemler operasyon bazında farklı modlar kullanır (örn. DynamoDB hem nihai tutarlı hem güçlü tutarlı okuma sunar).
- C ile A arasındaki tercihi tüm sistem yerine iş yeteneği bazında verin. Ödeme işlemleri CP gerektirebilir; ürün önerileri AP’ye tolerans gösterebilir.
- Tasarım gözden geçirmelerinde bölünme senaryolarını açıkça modelleyin: “bu endpoint için DB replika bağlantısı 30 saniye kopsa ne olur?”
- PACELC yaklaşımını da hesaba katın — çoğu kesinti aslında tam bir bölünme değil, gecikme problemidir.
Yaygın Hatalar
- Üç özelliğin de aynı anda elde edilebileceğine inanmak (“CAP bir tasarım hedefi değil, bir kısıttır”).
- Tek bir node’un aslında önemsiz biçimde CA olduğunu unutmak — CAP ancak birden fazla node’un anlaşması gerektiğinde anlam kazanır.
2. Tutarlılık (Consistency)
Tanım
Tutarlılık, verinin replikalar/node’lar arasında ne kadar “taze” ve “üzerinde anlaşılmış” göründüğünü tanımlar. İkili (boolean) değil, bir spektrum üzerindedir.
Tutarlılık Modelleri (en zayıftan en güçlüye)
- Nihai Tutarlılık (Eventual Consistency) — yeni yazma olmadığında ve yeterli zaman geçtiğinde replikalar yakınsar.
- Nedensel Tutarlılık (Causal Consistency) — birbiriyle nedensel ilişkili işlemler tüm node’lar tarafından aynı sırada görülür; ilişkisiz (“eşzamanlı”) işlemler farklı sıralarda görülebilir.
- Read-Your-Writes — bir istemci her zaman kendi önceki yazdıklarını görür.
- Monotonic Reads — bir istemci bir değeri gördükten sonra, sonraki okumalar asla daha eski bir değer döndürmez.
- Session Consistency — read-your-writes + monotonic reads’in bir oturum kapsamında birleşimi.
- Sequential Consistency — tüm işlemler, her istemcinin program sırasıyla tutarlı bir sıralı düzende gerçekleşiyormuş gibi görünür (ama mutlaka gerçek zamanlı sırayla değil).
- Linearizability (Doğrusallaştırılabilirlik / Güçlü Tutarlılık) — işlemler, çağrı ile yanıt arasındaki bir anda anlık gerçekleşmiş gibi görünür ve tüm istemciler arasında gerçek zamanlı sıralamayla tutarlıdır.
En İyi Pratikler
- İş gereksinimini karşılayan en zayıf tutarlılık modelini seçin — daha güçlü tutarlılık her zaman gecikme ve/veya erişilebilirlik maliyeti getirir.
- Her API endpoint’i/alanı için tutarlılık garantisini açıkça dokümante edin (örn. “bu alan nihai tutarlıdır, en fazla 5sn gecikmeyle”).
- “Cevaptan önce yorum” sıralamasının önemli olduğu ama tam linearizability’nin gereksiz olduğu sosyal-grafik/yorum sistemlerinde nedensel tutarlılık kullanın.
- Finansal defterler, envanter sayıları veya doğruluk değişmezleri (negatif bakiye olmaması gibi) olan her şey için yazma yolunda linearizable/güçlü tutarlılığa yönelin.
Yaygın Hatalar
- Gündelik konuşmada “tutarlılık"ın her zaman “güçlü tutarlılık” anlamına geldiğini varsaymak — her zaman hangi modelden bahsedildiğini netleştirin.
- Tek bir işlem (transaction) içinde farkında olmadan farklı tutarlılık seviyelerini karıştırmak (örn. eski bir replikadan okuyup buna dayanarak yazmak — bir read-then-write yarışı).
3. Nihai Tutarlılık (Eventual Consistency)
Tanım
Belirli bir veri öğesine yeni güncelleme yapılmadığı sürece, tüm replikaların eninde sonunda aynı değere yakınsayacağı bir tutarlılık modeli. Modelin kendisi bir eskime (staleness) üst sınırı garanti etmez (pratikte genelde saniyenin altından birkaç saniyeye kadar sürer).
Bunu Sağlayan Mekanizmalar
- Gossip protokolleri — node’lar rastgele eşlerle periyodik olarak durum değişimi yaparak yakınsamaya ulaşır (Cassandra, DynamoDB, Consul’da kullanılır).
- Read repair — okuma sırasında tespit edilen tutarsızlıklar asenkron olarak düzeltilir.
- Anti-entropy / Merkle ağaçları — replika hash ağaçlarının arka planda karşılaştırılıp farkların verimli biçimde bulunup düzeltilmesi.
- CRDT’ler (Conflict-free Replicated Data Types) — çakışma olmadan otomatik olarak birleşecek şekilde tasarlanmış veri yapıları (örn. G-Counter, OR-Set, LWW-Register).
- Vektör saatler / versiyon vektörleri — eşzamanlı (çakışan) güncellemeleri tespit etmek için nedensel geçmişi takip eder.
Çakışma Çözümleme Stratejileri
- Last-Write-Wins (LWW) — en basit yöntem, zaman damgası kullanır; veriyi sessizce kaybetme riski taşır.
- Uygulama seviyesinde birleştirme (merge) — örn. alışveriş sepeti “birleşim (union)” mantığı (Amazon Dynamo’nun orijinal yaklaşımı).
- CRDT’ler — merkezi koordinasyon olmadan matematiksel olarak garanti edilen yakınsama.
- Manuel/versiyon çakışması yüzeye çıkarma — çakışan tüm versiyonları çözmesi için istemciye/uygulamaya döndürme (Riak’taki “sibling"ler).
En İyi Pratikler
- Yüksek yazma verimi, yüksek erişilebilirlik gerektiren işler için nihai tutarlılık kullanın: görüntülenme sayıları, beğeniler, aktivite akışları, önbellek katmanları, DNS, CDN edge verisi.
- Yakınsama sırasındaki retry’ların çift uygulamaya sebep olmaması için idempotent yazmalarla eşleştirin.
- Bir eskime (staleness) SLA’sı belirleyip iletişimini yapın (örn. “p99’da 2 saniye içinde replike edilir”) ki alt akış ekipleri buna göre tasarım yapabilsin.
- Read-repair + anti-entropy’yi birlikte kullanın — hem reaktif hem proaktif yakınsama.
Yaygın Hatalar
- Ek güvenlik önlemi almadan katı değişmezleri (invariant) olan veriler için (örn. bir uçuş için “kalan koltuk sayısı”) nihai tutarlılık kullanmak — fazla satışa (overselling) yol açabilir.
- Nihai tutarlılığın silme (delete) işlemlerini de etkilediğini unutmak — tombstone doğru uygulanmazsa “hayalet” bir yazma silinen veriyi diriltebilir.
4. Güçlü Tutarlılık (Strong Consistency)
Tanım
Herhangi bir okumanın, sanki verinin tek bir kopyası varmış gibi (linearizability) en son yazılan veriyi döndüreceğini, ya da tüm node’ların işlemleri aynı global sırada göreceğini (sequential consistency, biraz daha zayıf ama ilişkili bir garanti) garanti eder.
Nasıl Sağlanır
- Senkron replikasyon — yazma işlemi, ancak tüm (veya bir quorum’daki) replikalar onayladıktan sonra onaylanır.
- Konsensüs protokolleri — Raft, Paxos, Multi-Paxos, Zab (bkz. Bölüm 10).
- Tek liderli yazma + linearizable okuma — okumalar ya lidere gider ya da tazeliği garanti etmek için bir “read index”/lease mekanizması kullanır.
- Dağıtık transaction’lar — Two-Phase Commit (2PC), Three-Phase Commit (3PC), veya Percolator tarzı teknikler (Spanner, CockroachDB).
Maliyeti
- Daha yüksek yazma gecikmesi (quorum/lider onayını beklemek gerekir, genelde AZ/bölge arası round-trip’ler).
- Bölünmeler sırasında düşük erişilebilirlik (CAP’e göre, bir CP sistemi tutarsızlık riski almaktansa yazma/okumayı reddeder).
- Daha karmaşık hata yönetimi (lider failover, log replikasyonunu yakalama).
En İyi Pratikler
- Güçlü tutarlılığı mümkün olan en küçük kritik yol için ayırın: örn. sadece “envanter rezerve et” işlemi için, tüm ürün kataloğu için değil.
- Gecikme bütçenize ve coğrafi dağılımınıza göre Spanner tarzı TrueTime / hibrit mantıksal saatler veya daha basit lider-tabanlı yaklaşımlar kullanın.
- Failover’ı açıkça test edin: staging’de lideri öldürün ve erişilemezlik penceresini ölçün.
- Her okumayı konsensüs üzerinden yönlendirmek yerine, log üzerindeki yükü azaltmak için lider lease’i üzerinden linearizable okuma düşünün.
Yaygın Hatalar
- “Güvende olmak için” güçlü tutarlılığı tüm sisteme uygulamak, gereksiz yere throughput ve erişilebilirliği düşürmek.
- “Senkron replikasyon"u “güçlü tutarlılık” ile karıştırmak — otomatik failover’lı tek bir standby’a senkron replikasyon, uygun konsensüs/fencing ile birleştirilmedikçe failover sırasında yine de bir tutarsızlık penceresi taşıyabilir.
5. Replikasyon (Replication)
Tanım
Erişilebilirliği, dayanıklılığı, hata toleransını ve okuma ölçeklenebilirliğini artırmak için aynı verinin birden fazla kopyasını farklı node’larda tutmak.
Replikasyon Topolojileri
- Tek liderli (primary-replica) — bir node yazmaları kabul eder, follower’lara yayar. Basittir, ama failover gerçekleşene kadar lider bir darboğaz/SPOF’tur.
- Çoklu liderli (Multi-leader) — birden fazla node yazma kabul eder, birbirine replike eder; çakışma çözümlemesi gerektirir (çoklu veri merkezi kurulumlarında yaygın).
- Lidersiz (Leaderless) — herhangi bir replika okuma/yazma kabul edebilir; tutarlılık için quorum kullanır (W + R > N) (Dynamo, Cassandra, Riak).
Replikasyon Modları
- Senkron — lider, istemciye onay vermeden önce replika onayını bekler. Güçlü dayanıklılık, daha yüksek gecikme.
- Asenkron — lider hemen onaylar, arka planda replike eder. Düşük gecikme, lider hata verirse veri kaybı riski.
- Yarı-senkron — en az bir replikanın (hepsinin değil) onayını bekler — bir ara nokta.
En İyi Pratikler
- Gecikme ve dayanıklılığı dengelemek için AZ/bölge içinde senkron, bölgeler arası asenkron replikasyon kullanın.
- Replikasyon gecikmesini (lag) birinci sınıf bir SLO/metrik olarak izleyin — okuma ölçekleme güvenliği için en yararlı sinyaldir.
- Read-your-write hassasiyeti olan istekleri lidere ya da güncel olduğu bilinen bir replikaya yönlendirin (sticky session, causal token).
- Split-brain’i (eski liderin geri dönüp yazma kabul etmesi) önlemek için fencing token’lı otomatik failover uygulayın.
Yaygın Hatalar
- İstemciye herhangi bir eskime sinyali vermeden okumaları geride kalmış replikalara ölçeklendirmek, “yazdığım şey kayboldu” gibi kafa karıştırıcı hatalara yol açmak.
- Sağlam bir çakışma çözümleme stratejisi olmadan çoklu-lider replikasyonu — sessiz veri bozulmasına yol açar.
- Failover’da fencing olmaması — iki “lider” aynı anda yazma kabul edebilir (split-brain), birleştirilemeyen sapmış bir duruma yol açar.
6. Sharding
Tanım
Büyük bir veri setini yatay olarak birden fazla bağımsız veritabanı örneğine/node’a (“shard”) bölmek; her shard verinin ayrık bir alt kümesini tutar. Öncelikle bir ölçekleme tekniğidir (yazma verimi, depolama boyutu), partitioning’in daha geniş organizasyonel anlamının aksine (ayrım için bkz. Bölüm 7).
Sharding Stratejileri
- Aralık (range) tabanlı sharding — veri anahtar aralıklarına göre bölünür (örn. kullanıcı ID 1–1M shard A’da). Basittir, aralık sorgularını destekler, ama yazmalar kümelenirse (artan ID/zaman damgaları gibi) hotspot riski taşır.
- Hash tabanlı sharding —
shard = hash(key) % N. Yükü eşit dağıtır, ama aralık sorguları pahalı hale gelir (tüm shard’lara yayılım gerekir) ve yeniden sharding acı vericidir (N’i değiştirmek neredeyse her şeyi karıştırır). - Consistent hashing (tutarlı hash) — hem node’ları hem anahtarları bir hash halkası üzerine eşler; bir node eklemek/çıkarmak yalnızca anahtarların küçük bir kısmını yeniden eşler. Dynamo, Cassandra, CDN’ler, memcached istemcileri tarafından kullanılır.
- Dizin (directory) tabanlı sharding — bir arama servisi anahtar → shard eşlemesini açıkça yapar, ekstra bir hop ve dizin için potansiyel SPOF pahasına maksimum esneklik (kolay yeniden dengeleme) sunar.
- Coğrafi sharding (geo-sharding) — veri yerelliği ve düzenleyici (veri egemenliği) sebepleriyle coğrafya/kiracı/bölgeye göre sharding.
En İyi Pratikler
- Yüksek kardinaliteli ve eşit erişim dağılımına sahip bir shard anahtarı seçin — anahtarları tuzlamıyor/hashlemiyorsanız zaman veya sıralı ID’lerle ilişkili anahtarlardan kaçının.
- İlk günden yeniden sharding’i düşünerek tasarlayın (consistent hashing, vnode’lar, veya bir dizin katmanı) — er ya da geç yeniden sharding yapmanız gerekecek.
- Pahalı shard’lar arası join/transaction’lardan kaçınmak için ilişkili veriyi aynı shard’da birlikte tutun (örn. bir kiracının tüm satırları).
- Daha düzgün yeniden dengeleme ve heterojen donanım desteği için sanal node’lar (vnode) kullanın — her fiziksel node birçok küçük hash aralığına sahip olur.
- Shard başına yükü sürekli izleyin; tek bir sıcak shard sharding’in amacını boşa çıkarır.
Yaygın Hatalar
- Dengesiz “balina” shard’lar yaratan düşük kardinaliteli bir anahtarla (örn.
ülke) sharding yapmak. - Rutin olarak shard’lar arası transaction/join ihtiyacı — shard anahtarının veya veri modelinin yeniden düşünülmesi gerektiğinin işareti.
- Net bir geçiş planı olmadan canlı yeniden sharding’in (çift yazma, backfill, cutover) operasyonel maliyetini hafife almak.
7. Partitioning (Bölümleme)
Tanım
Veriyi veya iş yükünü ayrı segmentlere bölmenin genel kavramı. Bu rehberde şu ayrımı yapıyoruz:
- Partitioning = genel/mantıksal kavram (Kafka topic’leri, veritabanı tablo partition’ları gibi kullanımlarda).
- Sharding = partitioning’in, bir veritabanını bağımsız sunucular üzerinde yatay olarak ölçeklendirmek için spesifik bir uygulaması.
Partition’lar tek bir node içinde (örn. sorgu performansı/bakım için tarihe göre PostgreSQL tablo partition’ı) veya node’lar arasında (broker’lar arasında dağıtılmış Kafka partition’ları) var olabilir.
Türleri
- Yatay (horizontal) partitioning — satırları partition’lar arasında bölme (aynı şema, satırların bir alt kümesi) — genelde “sharding” bununla kastedilir.
- Dikey (vertical) partitioning — sütunları/özellikleri farklı depolarda bölme (örn. kullanıcı profili bir DB’de, kullanıcı aktivite logları başka bir DB’de).
- Fonksiyonel partitioning — servis/bounded context’e göre bölme (mikroservislerin doğal bir sonucu, her biri kendi veritabanına sahip olur).
Kafka Tarzı Partitioning (log-partitioning)
- Bir topic, sıralı, ekleme-yalnızca (append-only) partition’lara bölünür; her partition tamamen sıralıdır, ama partition’lar arasında global bir sıra yoktur.
- Partition key, bir mesajın hangi partition’a düşeceğini belirler (
hash(key) % numPartitions), her anahtar için sıralamayı garanti eder. - Partition sayısı maksimum consumer paralelliğini sınırlar (bir partition, bir consumer group içinde aynı anda yalnızca bir consumer tarafından aktif okunabilir).
En İyi Pratikler
- Gerekli yerlerde sıralamayı garanti eden partition anahtarları seçin (örn. varlık-bazlı sıralama için Kafka olaylarını
userIdveyaorderId‘ye göre partition’layın). - Partition sayısını başlangıçta ölçülü şekilde fazla belirleyin — Kafka partition sayısını sonradan artırmak mevcut veriyi yeniden bölümlemez ve mevcut anahtarlar için sıralama garantisini bozabilir.
- DB tablo partitioning’de partition sınırlarını baskın sorgu deseninize göre hizalayın (örn. çoğu sorgu tarih aralığına göre filtreliyorsa aya göre partition’layın).
Yaygın Hatalar
- “Daha fazla partition = daha fazla paralellik” ilkesini sınırsız uygulamak — çok fazla partition metadata yükünü, yeniden dengeleme süresini ve dosya tanıtıcısı (file handle) kullanımını artırır (özellikle Kafka’da).
- Bazı partition’lar boşta kalırken bir kaçının “sıcak partition” olmasına yol açan çarpık (skewed) partition anahtarları.
8. Lider Seçimi (Leader Election)
Tanım
Dağıtık sistemdeki node’ların, çakışan eşzamanlı kararları önlemek için belirli bir fonksiyon (yazma kabul etme, olayları sıralama, iş atama) için tek bir koordinatör/lider node üzerinde anlaşma sürecidir.
Algoritmalar ve Mekanizmalar
- Bully Algoritması — daha yüksek ID’ye sahip node’lar düşük olanları “kovar”; en yüksek ID’li canlı node lider olur. Basittir ama en kötü durumda O(n²) mesaj gerektirir.
- Ring (Halka) Algoritması — seçim mesajları mantıksal bir halka üzerinde dolaşır; daha basit topoloji varsayımı gerektirir.
- Raft lider seçimi — node’lar Follower/Candidate/Leader durumlarındadır; rastgele seçim zaman aşımları bölünmüş oyları azaltır; lider belirli bir term için çoğunluk oyuyla seçilir.
- ZooKeeper tabanlı seçim — node’lar bir yol altında sıralı geçici (ephemeral) znode’lar oluşturur; en düşük sıra numarasına sahip node liderdir; diğerleri kendinden hemen önceki node’u izler (sürü etkisini önler).
- etcd/Consul lease tabanlı seçim — bir node lease-destekli bir anahtar edinir; liderlik lease yenilemeye (heartbeat) bağlıdır; lease kaybedilirse liderlik de kaybedilir.
En İyi Pratikler
- Lider seçimini elle yazmak yerine kanıtlanmış bir koordinasyon servisi (ZooKeeper, etcd, Consul) kullanmayı tercih edin — kenar durumları (ağ bölünmeleri, saat kayması, GC duraklamaları) doğru yapmak son derece zordur.
- Lider seçimini her zaman lease/fencing token’ları ile eşleştirin — her seçimde verilen, tek yönlü artan bir token; alt akış kaynaklar bunu kontrol ederek eski liderden gelen yazmaları reddeder.
- Sisteminizi “liderim sanan ama artık olmayan bir node” (GC duraklaması veya ağ gecikmesi bunu tetikleyebilir) durumuna dayanıklı tasarlayın — fencing’in, seçim algoritmasının kendisinden daha önemli olmasının sebebi budur.
- Lider sorumluluklarını minimumda tutun — çok fazla iş yapan bir lider hem darboğaz hem de failover sırasında daha büyük etki alanı olur.
Yaygın Hatalar
- Yalnızca lider seçiminin split-brain’i önlediğini varsaymak — kaynak katmanında zorunlu tutulan fencing token’ları olmadan bu doğru değildir.
- Uzun seçim zaman aşımlarının gereksiz yere uzun erişilemezlik pencerelerine yol açması; çok kısa zaman aşımlarının ise normal jitter altında seçim “çırpınmasına” (flapping) sebep olması.
9. Dağıtık Kilit (Distributed Lock)
Tanım
Bellek paylaşmayan süreçler/node’lar arasında karşılıklı dışlama (mutual exclusion) sağlayan bir mekanizma — kritik bölgeye/kaynağa küme genelinde aynı anda yalnızca bir sahip erişebilir.
Uygulama Yaklaşımları
- ZooKeeper ephemeral sequential znode’lar — klasik, iyi anlaşılmış bir yöntem; oturum süresi dolduğunda otomatik serbest bırakma.
- etcd/Consul lease tabanlı kilitler — TTL lease’li bir anahtar; sahibi çökerse/takılırsa kilit otomatik olarak sona erer.
- Redis tabanlı kilitler (
SET key value NX PX ttl) — hızlı ve basittir, ama saf tek-instance Redis kilitleme belirli hata durumlarına karşı güvenli değildir. - Redlock (Redis) — daha yüksek güvenlik için N bağımsız Redis örneğinin çoğunluğu üzerinde kilit almaya çalışan algoritma; tartışmalıdır — Martin Kleppmann’ın eleştirisi, fencing token’ları olmadan GC duraklamaları/saat sıçramaları altında hâlâ güvensiz olduğunu gösterdi, ancak Redis’in yazarları eleştirinin bazı kısımlarına itiraz etmektedir.
- Veritabanı tabanlı kilitler —
SELECT ... FOR UPDATE, ya da benzersiz kısıt + TTL/heartbeat sütunu olan özel birlockstablosu.
Kritik Güvenlik Gereksinimleri
- Karşılıklı dışlama — bir seferde yalnızca bir istemci kilidi tutar.
- Deadlock’tan bağımsızlık — sahip çökse bile kilit eninde sonunda serbest bırakılır (TTL/lease süresi dolması).
- Fencing — her kilit edinme işlemi tek yönlü artan bir token döner; korunan kaynak, o istemci hâlâ kilidi tuttuğunu “sansa” bile eski (daha düşük) bir token’dan gelen işlemleri reddetmelidir.
En İyi Pratikler
- Her zaman fencing token kullanın — dağıtık kilitlemede öğrenilecek en önemli tek ders budur. Bu olmadan, duraklamış/yavaş bir istemci kilidi “süresi dolduktan” sonra uyanıp veriyi bozabilir.
- Kritik bölgeyi mümkün olduğunca kısa tutun ve derinlemesine savunma önlemi olarak idempotent hale getirin.
- Süresi dolmayan kilitler yerine (sahip çökerse kalıcı deadlock riski taşır) TTL + heartbeat yenilemeli lease’leri tercih edin.
- Kilitleme için Redis kullanıyorsanız, kaynakta fencing ile eşleştirilmedikçe bunu bir doğruluk garantisi değil, bir verimlilik optimizasyonu olarak görün.
Yaygın Hatalar
- Aslında bir veritabanı benzersiz kısıtı veya optimistic concurrency control (compare-and-swap) ile daha basit ve güvenli çözülebilecek doğruluk sorunları için dağıtık kilit kullanmak.
- Fencing olmadan “kilidi tutuyorum"un “kritik bölgeyi yürüten tek benim” anlamına geldiğini varsaymak — bu, Kleppmann’ın Redlock eleştirisinin tam olarak işaret ettiği hata modudur.
- NTP sıçramaları veya VM duraklamaları altında bozulan saat tabanlı TTL varsayımları.
10. Konsensüs (Consensus)
Tanım
Hatalara ve ağ güvenilirsizliğine rağmen bir dizi dağıtık node’un aşağıdakileri sağlayarak tek bir değer (veya işlemlerin tek bir sıralı dizisi) üzerinde anlaşmasını sağlama problemi:
- Agreement (Anlaşma) — tüm doğru node’lar aynı değere karar verir.
- Validity (Geçerlilik) — karar verilen değer gerçekten bir node tarafından önerilmiştir.
- Termination (Sonlanma) — tüm doğru node’lar eninde sonunda karar verir (liveness/canlılık).
Başlıca Algoritmalar
- Paxos — orijinal, kanıtlanabilir doğru konsensüs algoritması (Lamport). Doğru anlamak/uygulamak son derece zordur; Proposer/Acceptor/Learner rolleri, iki fazı vardır (Prepare/Promise, Accept/Accepted).
- Multi-Paxos — kararlı bir lider/ayrıcalıklı proposer seçerek tekrarlanan konsensüs turlarını (replike log için gerekli olduğu gibi) optimize eder, sonraki girişler için Faz 1’i atlar.
- Raft — açıkça anlaşılabilirlik için tasarlanmıştır (Ongaro & Ousterhout). Lider seçimi, log replikasyonu ve güvenlik olarak ayrıştırılmıştır. Yaygın olarak uygulanmıştır (etcd, Consul, CockroachDB, TiKV).
- Zab (ZooKeeper Atomic Broadcast) — Raft’a benzer, ZooKeeper’ın primary-backup replike durum makinesi için özel olarak tasarlanmıştır.
- Byzantine Fault Tolerant (BFT) konsensüs — PBFT, Tendermint, HotStuff — yalnızca çökme değil, keyfi/kötü niyetli davranan node’lara da tolerans gösterir. f Bizans hatasına tolerans göstermek için 3f+1 node gerektirir. Blockchain sistemlerinde kullanılır.
FLP İmkânsızlığı
Fischer-Lynch-Paterson (1985) sonucu, tamamen asenkron bir sistemde, bir node bile hata verebiliyorsa hiçbir konsensüs algoritmasının hem güvenliği hem sonlanmayı garanti edemeyeceğini kanıtlar — deterministik konsensüs teoride imkânsızdır. Gerçek sistemler bunu zaman aşımları, rastgeleleştirme (rastgele seçim zamanlayıcıları) ve kısmi senkronluk varsayımlarıyla aşar.
En İyi Pratikler
- Konsensüsü yeniden icat etmeyin — Raft (etcd, Hashicorp Raft kütüphanesi) veya mevcut bir koordinasyon servisi kullanın.
- Replike durum makinesini küçük tutun — konsensüs verimi, her işlemin log replikasyonuyla sınırlıdır; tüm uygulama durumunuzu ham konsensüs üzerinden geçirmeyin (metadata/koordinasyon için kullanın, toplu veri için değil).
- Quorum boyutu trade-off’larını anlayın: 3 node 1 hataya, 5 node 2 hataya tolerans gösterir — daha fazla node hata toleransını artırır ama gecikmeyi de artırır (daha fazla onay gerekir) ve ağ trafiğini artırır.
- Bölgeler arası konsensüs için daha yüksek gecikme bekleyin — gerçekten global konsensüse mi, yoksa bölgesel konsensüs + asenkron replikasyona mı ihtiyacınız olduğunu değerlendirin.
Yaygın Hatalar
- “Dağıtık kilit” ile “konsensüs"ü karıştırmak — kilitler karşılıklı dışlamayı çözer, konsensüs bir değer/log üzerinde anlaşmayı çözer; ilişkilidirler ama aynı değildirler.
- Çift sayıda konsensüs node’u dağıtmak (4, 6) — bir alttaki tek sayıya kıyasla hata toleransını artırmaz, sadece gecikme ekler.
- FLP kaynaklı gerçekleri görmezden gelmek: sürekli ağ bölünmesi ortasında bile bir konsensüs sisteminin her zaman ilerleyeceğini beklemek (imkânsız — tutarsızlık riski almaktansa doğru bir şekilde karar vermeyi reddedecektir).
11. Quorum
Tanım
Bir okuma veya yazma işleminin geçerli/başarılı sayılması için katılması gereken minimum node sayısıdır; lidersiz/çoklu-replikalı sistemlerde tutarlılığı garanti etmek için kullanılır.
Temel Formül
N replika, W = yazma quorum’u, R = okuma quorum’u için:
W + R > N ise, okuma ve yazma en az bir node’da örtüşür — bu da okumanın en son yazmayı görmesini garanti eder (o işlem için güçlü tutarlılık).
Yaygın konfigürasyonlar:
N=3, W=2, R=2→W+R=4 > 3✅ güçlü tutarlı, 1 node hatasına tolerans gösterir.N=3, W=1, R=1→ hızlıdır ama tutarlılık garantisi yoktur (AP’ye yakın).N=3, W=3, R=1→ tüm yazmaların başarılı olması gerekir (node hatasına karşı kırılgan) ama okumalar hızlı ve tutarlıdır.
En İyi Pratikler
- W/R’yi işlem tipine göre ayarlayın: kritik yazmalar
W=N(hepsi onaylamalı) veya çoğunluk quorum’u kullanabilir; okuma-ağırlıklı düşük kritiklikteki yollarR=1kullanabilir. - Çoğunluk quorum’unun (
⌊N/2⌋+1) konsensüs sistemleri (Raft/Paxos) için standart olduğunu unutmayın — herhangi iki quorum’un örtüşmesini garanti eder. N‘i (replika sayısı) artırmak dayanıklılığı ve okuma ölçeklenebilirliğini iyileştirir ama yazma gecikmesini ve depolama maliyetini artırır — bedava öğle yemeği yoktur.- Bazı node’lara ulaşılamadığında bile yazmaları erişilebilir tutmak için (Dynamo tarzı) sloppy quorum + hinted handoff kullanın, ama bunun katı quorum tutarlılığını erişilebilirlik lehine değiş tokuş ettiğini unutmayın.
Yaygın Hatalar
W + R ≤ Nayarlamak, sonra eski okumalara şaşırmak (“ama replikasyonum var!”).- Quorum tutarlılığının node hatalarına karşı koruma sağladığını, quorum’un kendisini bölen ağ bölünmelerine karşı korumadığını unutmak — bir bölünme sizi yine de erişilebilir bir quorum’suz bırakabilir, bu (doğru şekilde) bir erişilemezliktir, tutarsızlık değil.
12. Hata Toleransı (Fault Tolerance)
Tanım
Bazı bileşenleri hata verse bile bir sistemin doğru şekilde (tam veya düşük performansla) çalışmaya devam etme özelliği.
Temel Teknikler
- Redundancy (Yedeklilik) — tek bir bileşenin SPOF (Single Point of Failure - Tek Hata Noktası) olmaması için hesaplamayı, veriyi ve ağ yollarını replike edin.
- İzolasyon / Bulkhead’ler — kaynakları (thread pool’lar, bağlantı havuzları, hatta tüm servisler) bölümleyerek bir alandaki hatanın başka bir alanın ihtiyaç duyduğu kaynakları tüketmesini engelleyin (gemi gövdesi bölmelerinden esinlenmiştir).
- Zarif bozulma (graceful degradation) — bir bağımlılık çöktüğünde sert bir hata yerine azaltılmış/önbelleklenmiş/yaklaşık bir yanıt sunun.
- Bağımsız hata alanları — korelasyonlu hataların (tek bir raf’ın gücünü kaybetmesi gibi) tüm replikaları götürmemesi için replikaları AZ’lere/bölgelere/raflara/güç kaynaklarına yayın.
- Chaos engineering (Kaos mühendisliği) — gerçek hatalar gelmeden önce hata toleransı varsayımlarını doğrulamak için production/staging’de proaktif olarak hata enjekte edin (Netflix’in Chaos Monkey’i, Gremlin).
- Kendi kendini iyileştirme (self-healing) — otomatik tespit + düzeltme (otomatik yeniden başlatma, otomatik ölçeklenen değiştirme, otomatik failover).
En İyi Pratikler
- Hata toleransı hedefinize uygun N+1 veya N+2 yedeklilik için tasarlayın (kaç eşzamanlı hataya dayanmanız gerekiyor?).
- Her bileşenin hata modunu açıkça tanımlayın ve test edin — bu bağımlılık çöktüğünde istemci ne görür? (Hata? Eski veri? Sessiz takılma?)
- Servisler/kiracılar arasında bulkhead’i agresif şekilde kullanın — gürültücü bir komşu veya kötü davranan bir bağımlılık asla tüm sistemi aç bırakamamalıdır.
- Sadece bir kez değil, düzenli olarak game day / chaos deneyleri yapın.
- MTTR’yi (Mean Time To Recovery - Ortalama Kurtarma Süresi) MTBF (Mean Time Between Failures - Ortalama Hatalar Arası Süre) kadar ciddiye alın — modern hata toleransı stratejilerinin çoğu hataların olacağını kabul eder ve hızlı, otomatik kurtarma için optimize eder.
Yaygın Hatalar
- Gerçekte bağımsız olmayan yedeklilik (örn. “3 replika” hepsi aynı AZ/raf’ta, hepsi aynı güç/ağ kaderini paylaşıyor).
- Failover’ı pratikte hiç test etmemek — ilk gerçek test, gerçek bir olay sırasında gerçekleşir, bu da bozuk olduğunu keşfetmek için en kötü zamandır.
- Gerekli olmayan bileşenler için hata toleransını aşırı mühendislik etmek (karmaşıklık/maliyet pahasına), asıl kritik yolu ise yetersiz mühendislik etmek.
13. Hata Tespiti (Failure Detection)
Tanım
Dağıtık bir sistemin bir node’un/bileşenin hata verdiğini (çöktüğünü, takıldığını veya erişilemez hale geldiğini) belirleme mekanizmasıdır — failover, lider yeniden seçimi veya node’u yük dengeleme havuzundan çıkarmayı tetiklemek için bir ön koşuldur.
Temel Problem
Asenkron bir ağda, “node ölü” ile “node/ağ sadece yavaş” durumunu ayırt edemezsiniz. Bu yüzden hata detektörleri doğası gereği olasılıksal/sezgiseldir, şunlarla karakterize edilir:
- Completeness (Tamlık) — çöken her node eninde sonunda tüm doğru node’lar tarafından şüphelenilir.
- Accuracy (Doğruluk) — doğru node’lardan (yanlışlıkla) şüphelenilmez. Gerçekten asenkron bir ağda mükemmel doğruluk + tamlık imkânsızdır (FLP ile ilişkilidir); gerçek detektörler ikisi arasında denge kurar.
Mekanizmalar
- Heartbeat’leme — node’lar periyodik olarak “canlıyım” sinyalleri gönderir; bir zaman aşımı süresi boyunca sinyal gelmemesi ⇒ hata şüphesi. Basittir, ama zaman aşımı ayarı tespit hızı ile yanlış pozitif arasında bir dengedir.
- Phi Accrual Failure Detector (Cassandra, Akka’da kullanılır) — ikili canlı/ölü yerine, geçmiş heartbeat varış aralıklarına dayanarak sürekli bir şüphe seviyesi (
φ) hesaplar, ağ jitter’ına otomatik olarak uyum sağlar. - Gossip tabanlı tespit (SWIM protokolü) — node’lar rastgele eşleri yoklar ve şüphe/hata bilgisini gossip yoluyla yayar, merkezi tek-node’a-heartbeat şemalarından daha iyi ölçeklenir; Consul, Cassandra (benzer fikirler yoluyla), Serf’te kullanılır.
- Sağlık kontrolleri (aktif yoklama) — harici bir yoklayıcı (yük dengeleyici, orkestratör) belirli aralıklarla canlılık/hazır olma yoklaması (HTTP/TCP/gRPC sağlık kontrolü) gönderir.
En İyi Pratikler
- Liveness’ı (süreç çalışıyor mu?) readiness’tan (şu anda doğru şekilde trafik sunabilir mi, örn. DB bağlantı havuzu ısınmış mı?) ayırın — Kubernetes bunu ayrı probe’larla açıkça modeller.
- Sabit zaman aşımlarının değişken gecikme altında aşırı yanlış pozitife yol açtığı büyük veya coğrafi olarak dağıtık kümelerde adaptif detektörler (Phi Accrual, SWIM) kullanın.
- Zaman aşımı değerlerini ortalamaya değil p99.9 gecikmesine göre ayarlayın — yük artışları sırasındaki yanlış pozitifler kademeli failover fırtınalarına yol açar.
- Bir node’u ölü ilan etmeden önce birden fazla sinyali birleştirin (kaçırılan heartbeat’ler + başarısız sağlık kontrolü + gossip onayı yok) yanlış pozitifleri azaltmak için.
Yaygın Hatalar
- Çok agresif zaman aşımlarının, normal GC duraklamaları veya geçici ağ sıçramaları altında node’ların tekrar tekrar ölü/canlı işaretlenmesine (flapping) yol açması, gereksiz failover’ları tetiklemesi.
- Merkezi heartbeat toplamanın ölçekte kendisinin bir darboğaz/SPOF haline gelmesi.
- “Çökmüş” ile “ağ bölünmüş"ü ayırt etmemek — bölünmüş ama canlı bir node, bazı istemciler tarafından ölü işaretlenirken bazılarına hâlâ (muhtemelen eski) trafik sunuyor olabilir — klasik bir split-brain riski.
14. İdempotentlik (Idempotency)
Tanım
Bir işlem, birden fazla kez gerçekleştirilmesi bir kez gerçekleştirilmesiyle aynı etkiye sahipse idempotent’tir. Dağıtık sistemlerde kritik öneme sahiptir çünkü retry’lar kaçınılmazdır (bir isteğin sunucu tarafından işlenmeden önce mi sonra mı başarısız olduğunu genelde bilemezsiniz — “iki general problemi”).
Uygulama Desenleri
- İdempotentlik anahtarları — istemci, her mantıksal işlem için benzersiz bir anahtar (UUID) üretir; sunucu (anahtar → sonuç) saklar ve yinelenen anahtarlar için yeniden işlemek yerine önbelleklenmiş sonucu döndürür (Stripe, PayPal API’lerinde kullanılır).
- HTTP semantiği yoluyla doğal idempotentlik —
PUTveDELETEtanım gereği doğal olarak idempotent’tir (iki kez uygulamak = aynı durum);POSTdeğildir, bu yüzden idempotentlik anahtarları en çok POST tarzı “oluşturma” işlemlerinde önemlidir. - Koşullu yazmalar / compare-and-swap —
UPDATE ... WHERE version = X, ya daSET x IF x.version = expected— tekrarlanan uygulamayı güvenli hale getirir çünkü sadece ilk uygulama durumu değiştirir. - Tasarım gereği idempotent işlemler —
SET balance = 100idempotent’tir;balance += 10değildir (iki kez uygulamak etkiyi ikiye katlar). Mümkünse artımlı delta’lar yerine mutlak-durum işlemlerini tercih edin. - Deduplication tabloları/pencereleri — mesaj-consumer pipeline’larında yinelemeleri tespit edip atlamak için son işlenen mesaj ID’lerini (TTL ile) saklayın.
En İyi Pratikler
- Dışarıdan tetiklenen her değiştirici (mutating) işlemi sonradan akla gelen bir şey değil, varsayılan bir mühendislik standardı olarak idempotent yapın.
- Ödeme/finansal işlemler için, istemci tarafından sağlanan bir idempotentlik anahtarını her zaman gerektirin; bir mesaj broker’ından “exactly-once” (tam olarak bir kez) teslimatına asla yalnızca güvenmeyin (bir ağ üzerinden gerçek exactly-once teslimat elde edilemez — sonraki bölüme bakın).
- İdempotentlik anahtarlarını makul bir TTL ile saklayın (gerçekçi retry pencerelerini kapsayacak kadar uzun, örn. 24 saat) ve sonrasında temizleyin.
- Mesajlaşma sistemi “exactly-once” iddia etse bile consumer’ları idempotent tasarlayın — derinlemesine savunma, çünkü çoğu “exactly-once” garantisi aslında “effectively-once"tur (at-least-once teslimat + idempotent işleme).
Yaygın Hatalar
- Bir mesaj kuyruğunun “exactly-once” modunun idempotentlik ihtiyacını ortadan kaldırdığını varsaymak — özellikle süreç/consumer yeniden başlatmalarında neredeyse hiçbir zaman tamamen doğru değildir.
- İdempotentlik anahtarlarının TTL olmadan saklanması (depolama şişmesine yol açar) ya da çok kısa TTL ile saklanması (ağ bölünmelerinden sonraki retry’larda amacını boşa çıkarır).
- Birincil veri deposu dışındaki yan etkileri (örn. e-posta gönderme, üçüncü taraf API çağırma) unutmak — idempotentlik sınırı sadece DB yazmasını değil, tüm yan etkileri kapsamalıdır.
15. Retry (Yeniden Deneme)
Tanım
Bazı hataların geçici olduğu (kısa süreli ağ sıçraması, anlık aşırı yük, kısa erişilemezlik) ve tekrar denendiğinde başarılı olacağı varsayımıyla başarısız bir işlemi yeniden denemek.
Ne Zaman Retry Yapılmalı
- Retry yapın: zaman aşımları, bağlantı sıfırlamaları,
503 Service Unavailable,429 Too Many Requests, geçici ağ hataları. - Retry yapmayın (değişiklik olmadan):
400 Bad Request,401/403yetkilendirme hataları,404 Not Found,422 Unprocessable Entity— istek temelden yanlıştır ve yeniden denemek yardımcı olmaz. - Dikkatli retry yapın:
500 Internal Server Error— geçici olabilir ya da kalıcı bir bug olabilir; sınırlı deneme sayısı ve izleme ile birleştirin.
Tasarım Unsurları
- Retry bütçesi — bir kesintiyi büyütmesini önlemek için retry olabilecek isteklerin oranını sınırlayın (örn. toplam trafiğin en fazla %10’u retry olabilir).
- Jitter — birçok istemcinin aynı anda senkronize “sürü etkisi” (thundering herd) retry’ları yapmasını önlemek için retry gecikmesini rastgeleleştirmek (bkz. Bölüm 16).
- Maksimum retry sayısı — retry’ları her zaman sınırlayın; sınırsız retry = potansiyel sonsuz döngü / kaynak sızıntısı.
- Yalnızca idempotent işlemleri retry edin (veya anahtarlarla idempotentliği garanti edin) — idempotent olmayan bir işlemi retry etmek yinelenen yan etkilere sebep olabilir (bir müşteriyi iki kez ücretlendirmek gibi).
En İyi Pratikler
- Retry’ları circuit breaker’larla birleştirin — açıkça çökmüş bir bağımlılığa vurmaya devam etmek yerine, ona karşı retry yapmayı tamamen durdurun (bkz. Bölüm 17).
- Varsayılan retry gecikme stratejisi olarak jitter’lı exponential backoff kullanın (bkz. Bölüm 16) — asla sıkı bir döngüde hemen retry yapmayın.
- Çağrı zincirinde sadece çağrı-başına bir zaman aşımı değil, bir deadline/bütçe yayın ki alt katmandaki retry’lar çağıranın genel zaman bütçesini aşmasın.
- Retry denemelerini ilk denemelerden ayrı olarak logla/metrikle — retry’larda bir artış, genelde oluşmakta olan bir olayın en erken sinyalidir.
Yaygın Hatalar
- Retry fırtınaları / retry amplifikasyonu — servis A servis B’ye yapılan çağrıları retry ederse ve B de C’ye yapılan çağrıları retry ederse, C’deki bir yavaşlama üzerindeki her katmanda büyütülebilir (çarpımsal retry etkisi). Her katmanda retry yapmayarak ya da retry bütçeleriyle azaltın.
- Backoff olmadan retry yapmak, esasında zaten zorlanan bağımlılığınıza kendi DDoS’unuzu yapmak.
- İstemci taraflı retry kütüphanelerinin, sunucu taraflı retry’ların üzerine şeffaf biçimde retry yapması, olaylar sırasında yükü sessizce çarpması.
16. Exponential Backoff (Üstel Geri Çekilme)
Tanım
Ardışık retry denemeleri arasındaki bekleme süresinin üstel olarak arttığı (örn. 1s, 2s, 4s, 8s, 16s…) bir retry-gecikmesi stratejisi; hatalar devam ettikçe zorlanan sistem üzerindeki baskıyı azaltır ve toparlanması için zaman tanır.
Formül
delay = min(base * 2^attempt, max_delay)
Pratik olmayan derecede uzun beklemeleri önlemek için genelde bir max_delay tavanıyla sınırlandırılır.
Jitter Varyantları (senkronize retry’ları / sürü etkisini önlemek için)
- Full Jitter:
delay = random(0, min(max_delay, base * 2^attempt))— AWS’nin önerdiği yaklaşım; retry’ları en eşit şekilde dağıtır. - Equal Jitter:
delay = (base * 2^attempt) / 2 + random(0, (base * 2^attempt) / 2)— rastgelelik eklerken bir miktar backoff büyüme garantisini korur. - Decorrelated Jitter:
delay = min(max_delay, random(base, previous_delay * 3))— AWS’nin alternatifi, katı ikiye katlamayı önlerken retry’ları yine de iyi dağıtır.
En İyi Pratikler
- Her zaman jitter ekleyin — jitter’sız saf exponential backoff, ortak bir kesintiden sonra birçok istemci arasında senkronize retry dalgalarına (sürü etkisi) yol açar.
- Makul bir
max_delaybelirleyin — sınırsız üstel büyüme, sunucuyu belirli bir noktadan sonra anlamlı şekilde korumadan kullanıcı deneyimini bozan pratik olmayan uzun beklemelere yol açar. - Sürekli artan bir gecikmenin yanında maksimum retry sayısı/deadline ile birleştirin — ne zaman pes edip bir hata gösterileceğini bilin.
- Belirli bir sebebiniz olmadıkça sağlam, iyi çalışılmış bir varsayılan olarak AWS’nin Full Jitter algoritmasını kullanın.
Yaygın Hatalar
- Jitter olmadan backoff uygulamak — ölçekte çok yaygın ve maliyetli bir hata.
- Backoff sayacını yanlış sıfırlamak (örn. mantıksal işlem başına değil istek başına), amaçlanandan daha az etkili backoff’a yol açmak.
17. Circuit Breaker (Devre Kesici)
Tanım
Elektrik mühendisliğinden ödünç alınan bir desen; hatalar bir eşiği aştığında çökmekte olan bir bağımlılığa yapılan çağrıları durdurur, “devreyi açarak” büyük olasılıkla başarısız olacak isteklere devam etmek yerine hızlıca başarısız olur — hem çağıranı (takılmak yerine hızlı hata) hem de çağrılanı (toparlanırken yükten kurtulma) korur.
Durumlar
- Closed (Kapalı) — normal çalışma; istekler geçer; hatalar sayılır.
- Open (Açık) — hata eşiği aşıldı; belirlenen bir soğuma süresi boyunca istekler bağımlılığı çağırmadan hemen başarısız olur (veya fallback’e düşer).
- Half-Open (Yarı Açık) — soğuma süresinden sonra, bağımlılığın toparlanıp toparlanmadığını test etmek için sınırlı sayıda deneme isteğine izin verilir. Başarı ⇒ Closed’a geçiş; başarısızlık ⇒ tekrar Open’a.
Konfigürasyon Parametreleri
- Hata eşiği — örn. son 20 istekte %50 hata oranından sonra aç, ya da 5 ardışık hata.
- Soğuma/reset zaman aşımı — Half-Open denemeden önce Open’da ne kadar kalınacağı.
- Half-open deneme hacmi — tekrar Closed mi Open mı denileceğine karar vermeden önce kaç isteğin geçmesine izin verileceği.
- Neyin hata sayılacağı — zaman aşımları ve 5xx genelde sayılır; 4xx istemci hataları genelde sayılmamalıdır (bağımlılığın sağlıksız olduğunun bir işareti değildir).
En İyi Pratikler
- Circuit breaker’ları, son kullanıcıya ham bir hata göstermek yerine fallback davranışıyla eşleştirin — önbelleklenmiş yanıt, varsayılan değer, düşük performanslı özellik.
- Breaker kapsamını global değil bağımlılık/endpoint bazında ayarlayın — bir alt akışın çökmesi ilgisiz çağrılar için breaker’ı tetiklememelidir.
- Durum geçişleri üzerinde metrik/alarm yayın (Closed→Open güçlü bir olay sinyalidir).
- Durum makinesini yeniden icat etmek yerine kanıtlanmış kütüphaneler kullanın (Netflix Hystrix [bakım modunda, ama etkili], resilience4j, Polly [.NET], Envoy/Istio’nun altyapı katmanında yerleşik circuit breaking’i).
Yaygın Hatalar
- Çok hassas circuit breaker eşikleri, normal geçici sıçramalarda tetiklenip gereksiz fallback/düşük performansa yol açması.
- Half-Open kurtarma davranışını test etmemek — bazı uygulamalar Half-Open sırasında çok fazla isteğe izin verir, bağımlılık en kırılgan olduğu (henüz toparlanmakta olduğu) anda korumayı fiilen bozar.
- Breaker’ları yalnızca en dış katmanda uygulamak, hataların genellikle kaynaklarına daha yakın bir yerde tutulması gerektiğini gözden kaçırmak.
18. Timeout (Zaman Aşımı)
Tanım
Bir çağıranın, çağrıyı başarısız saymadan önce bir yanıt için bekleyeceği maksimum süre. Zaman aşımları temeldir — bunlar olmadan, yavaş/takılmış bir bağımlılık sınırsız kaynak tüketimine (süresiz tutulan thread’ler/bağlantılar) ve çağrı zincirinde kademeli hataya yol açabilir.
Türleri
- Bağlantı zaman aşımı — bir TCP/TLS bağlantısı kurmak için maksimum süre.
- İstek/okuma zaman aşımı — istek gönderildikten sonra yanıt için beklenen maksimum süre.
- Toplam/deadline zaman aşımı — retry’lar dahil, tüm işlem için uçtan uca bütçe — bu, servis çağrıları boyunca yayılması gereken zaman aşımıdır.
En İyi Pratikler
- Her ağ çağrısının bir zaman aşımı olmalıdır — istisnasız. Sınırsız bir çağrı, gerçekleşmeyi bekleyen bir kaynak sızıntısıdır.
- Zaman aşımlarını rastgele yuvarlak sayılar yerine, marj bırakarak bağımlılığın ölçülen p99/p99.9 gecikmesine göre belirleyin.
- Sadece hop-başına zaman aşımları değil, çağrı zinciri boyunca bir deadline yayın — örn. gRPC context deadline’ları, gRPC/HTTP header tabanlı deadline yayılımı — bütçesinin %90’ını zaten kullanmış bir isteğin her alt akış hop’unda tekrar tam bir zaman aşımı almaması için.
- Zaman aşımları genelde dış katmanlarda daha kısa, iç katmanlarda daha sıkı olmalı demek tam doğru değil — asıl önemli olan tersi kaygı: bir alt akış çağrısının zaman aşımının, retry/fallback için yer bırakacak şekilde çağıranın kendi zaman aşımından anlamlı ölçüde kısa olmasını sağlamak (yani iç/alt akış zaman aşımı < dış/üst akış zaman aşımı).
- Circuit breaker’lar ve retry’larla birleştirin — tek başına zaman aşımı sadece hızlı başarısız olur; ardından gelebilecek retry fırtınasına karşı koruma sağlamaz.
Yaygın Hatalar
- Hiç zaman aşımı olmaması (OS/kütüphane varsayılanına döner, bazen kelimenin tam anlamıyla sonsuz) — thread pool tükenmesi kesintilerinin klasik bir sebebi.
- Bir çağrı zincirinin her katmanında aynı zaman aşımını ayarlamak, böylece iç katmandaki retry’ların dış katmanın deadline’ını aşarak boşa çalışmasına yol açmak.
- Gerçek p99 gecikmesine göre çok agresif zaman aşımı, normal yük varyansı altında yanlış-pozitif hatalara ve gereksiz retry’lara yol açması.
19. Rate Limiting (Hız Sınırlama)
Tanım
Kaynakları aşırı yüklenmeden korumak, kiracılar arasında adil kullanımı sağlamak ve API sözleşmelerini/fiyatlandırma katmanlarını uygulamak için bir istemcinin (veya sistemin bütününün) belirli bir zaman penceresi içinde gerçekleştirebileceği istek/işlem sayısını kısıtlamak.
Algoritmalar
- Token Bucket (Jeton Kovası) — bir kova en fazla
Ntoken tutar,r/saniye hızında yeniden doldurulur; her istek bir token tüketir; boş kova ⇒ reddet/kuyruğa al. Kova kapasitesine kadar kontrollü patlamalara (burst) izin verir. En yaygın kullanılandır (AWS API Gateway, birçok kütüphane). - Leaky Bucket (Sızan Kova) — istekler bir kuyruğa (kova) girer ve sabit bir hızda işlenir (“sızar”); patlamaları sabit bir çıkış hızına yumuşatır, ama kuyruklama gecikmesi ekler.
- Fixed Window Counter (Sabit Pencere Sayacı) — sabit bir zaman penceresi başına istekleri sayar (örn. dakika başına); basittir, ama pencere sınırlarında 2x patlamaya izin verir (bir pencerenin sonu + bir sonraki pencerenin başında bir patlama).
- Sliding Window Log (Kayan Pencere Logu) — her isteğin zaman damgasını saklar, sondaki pencere içindeki istekleri sayar; doğrudur ama ölçekte bellek yoğundur.
- Sliding Window Counter (Kayan Pencere Sayacı) — mevcut + önceki sabit pencerelerin ağırlıklı sayılarını kullanarak kayan logu yaklaşık olarak hesaplar; iyi bir doğruluk/bellek dengesi sunar, üretimde yaygındır (örn. Cloudflare’ın yaklaşımı).
Nerede Uygulanır
- İstemci/API-anahtarı bazlı — kiracı-başına adalet, fiyatlandırma katmanı uygulaması.
- IP başına — edge’de kötüye kullanım/DDoS azaltımı.
- Endpoint/global bazında — belirli pahalı bir işlemi veya tüm sistemin kapasite tavanını korumak.
- Dağıtık hız sınırlama — limitlerin birçok stateless servis örneği genelinde uygulanması gerektiğinde, paylaşımlı bir depo gerektirir (atomik
INCR+EXPIREile Redis, ya da Redis Lua script’leri ile uygulanan token bucket) — kendi gecikme/tutarlılık trade-off’larını getirir.
En İyi Pratikler
- İyi davranan istemcilerin doğru şekilde geri çekilebilmesi için
Retry-Afterbaşlığıyla429 Too Many Requestsdöndürün. - Hız sınırlamayı birden fazla katmanda uygulayın — edge/CDN (ucuz, kaba), API gateway (kiracı-bazlı), ve uygulama (iş-mantığına-özel limitler).
- Varsayılan olarak token bucket kullanın — sabit pencerelere göre meşru patlamalı trafik desenlerini daha doğal karşılar.
- Dağıtık uygulama için, mükemmel hassasiyet gerekmediğinde, tamamen merkezi senkron kontrol yerine periyodik senkronizasyonlu yaklaşık/yerel hız sınırlamayı tercih edin — bu, çok daha düşük gecikme ve daha yüksek erişilebilirlik karşılığında biraz doğruluktan taviz verir.
Yaygın Hatalar
- Sabit pencere sayaçlarının pencere sınırlarında etkin 2x patlamaya izin vermesi — hassas kapasite planlamasında sürpriz yaratır.
- Merkezi hız sınırlama deposunun kendisinin bir darboğaz/SPOF haline gelmesi.
- “Kötü niyetli kötüye kullanım” ile “meşru patlama” arasında ayrım yapmamak, normal yoğun kullanım sırasında iyi müşterilerin kısıtlanmasına yol açmak.
20. Backpressure (Geri Basınç)
Tanım
Bir sistemin, gelen iş yükü oranına yetişemediğinde yukarı akıştaki üreticilere “yavaşla” sinyali vermesini sağlayan bir mekanizma; sınırsız kuyruk büyümesini, bellek tükenmesini ve kademeli hatayı önler. Backpressure, bileşenler arasındaki akış kontrolü hakkındadır; sınırda kabul kontrolü hakkında olan rate limiting’i tamamlar ama ondan farklıdır.
Mekanizmalar
- Reactive Streams (Java, RxJava, Project Reactor, Akka Streams) — tüketicinin üreticiden açıkça N öğe istediği (
request(n)) resmi bir protokol; backpressure’ı sonradan akla gelen bir şey değil, API sözleşmesinin bir parçası haline getirir. - TCP akış kontrolü — taşıma katmanının kendisi alım pencereleri (receive window) yoluyla backpressure uygular; alıcının tamponu doluyken, gönderici protokol seviyesinde kısıtlanır.
- Sınırlı (bounded) kuyruklar — üretici ve tüketici arasında sabit kapasiteli bir kuyruk kullanmak; dolduğunda üretici ya bloklanır, ya (en-eski/en-yeni-at gibi politikalarla) atar, ya da yeni işi reddeder — hepsi sınırsız bir kuyruktan çok daha güvenlidir.
- Load shedding (Yük atma) — daha yüksek öncelikli işler için kapasiteyi korumak amacıyla aşırı yük altında düşük öncelikli işleri kasıtlı olarak atmak/reddetmek (örn. kullanıcıya yönelik istekleri atmadan önce batch işlerini atmak).
- Kredi tabanlı akış kontrolü — tüketici, üreticiye ne kadar gönderebileceğini temsil eden “kredi” verir; HTTP/2 ve gRPC akış kontrolünde kullanılır.
En İyi Pratikler
- Her yerde sınırlı kuyrukları tercih edin — sınırsız bir kuyruk bir güvenlik ağı değil, gerçek problemi felaket haline gelene kadar gizleyen gecikmeli bir OOM çökmesidir.
- Backpressure’ı mümkün olduğunca yukarı akışa itin — ideal olarak her hop’ta sessizce emilmek yerine (bu sadece problemi geciktirir ve gizler) orijinal istemciye/üreticiye kadar.
- Sürekli aşırı yük altında sınırsız kuyruklama/bloklama yerine zarif bozulma için backpressure’ı load shedding ile birleştirin.
- Akış kontrolü olmayan push-tabanlı sistemler yerine yerel backpressure desteğine sahip protokoller/kütüphaneler kullanın (Reactive Streams, gRPC, Kafka consumer pull-modeli).
Yaygın Hatalar
- Mikroservisler arasında “sonsuz” bellek-içi kuyruklara güvenmek — yük artışları altında OOM-kill kademelenmelerinin klasik bir sebebi.
- Backpressure’ı yalnızca bir hop’ta uygulamak, bunun yerine bir sonraki yukarı akış hop’unun sınırsız bir kuyruk oluşturmasına yol açmak — backpressure uçtan uca yayılmalıdır.
- Backpressure’ı basit retry’larla karıştırmak — backpressure/hız farkındalığı olmadan retry yapmak aşırı yükü kötüleştirebilir (bkz. Bölüm 15’teki “retry fırtınası”).
21. Graceful Shutdown (Zarif Kapanış)
Tanım
Bir süreci/servisi aniden öldürmek (bu düşen istekler, bozuk durum veya sahipsiz kaynaklara yol açar) yerine — devam eden işi tamamlayarak veya güvenli şekilde devrederek, yük dengeleyicilerden/servis keşfinden çıkışı kaydederek ve kaynakları temiz bir şekilde serbest bırakarak kontrollü bir şekilde sonlandırmak.
Kapanış Sırası (tipik)
- Sonlandırma sinyalini alma (orkestratörden
SIGTERM, yakalanamayanSIGKILLdeğil). - Yeni işi kabul etmeyi durdurma — önce servis keşfinden/yük dengeleyiciden kaydı silin, böylece mevcut bağlantılar sunulmaya devam ederken yeni trafik gelmez.
- Devam eden istekleri boşaltma (drain) — bir grace period (mühlet) kadar, şu anda işlenmekte olan isteklerin tamamlanmasını bekleyin.
- Kaynakları temiz kapatma — DB bağlantıları, mesaj consumer offset’lerinin commit edilmesi, dosya tanıtıcılarının flush/close edilmesi.
- Mühlet sonrası zorla çıkış — boşaltma çok uzun sürerse, sonsuza kadar takılı kalmak yerine (bir miktar kayıp kabul ederek) zorla sonlandırın, çünkü orkestratörler (Kubernetes) kendi mühletlerinden sonra ne olursa olsun
SIGKILLgönderecektir.
En İyi Pratikler
SIGTERMalındığında, süreç aslında durmadan önce bile readiness probe’unun hemen başarısız olmasını sağlayın — bu, pod’u yük dengeleyici rotasyonundan DNS/LB yayılımının izin vereceğinden daha hızlı çıkarır, “kapanırken hâlâ trafik alma” penceresini en aza indirir.- Orkestratörün grace period‘unu (örn. Kubernetes
terminationGracePeriodSeconds) tipik en uzun devam eden isteğinizin süresini rahatça aşacak şekilde ayarlayın. - Kuyruk/stream consumer’ları için, offset’lerin/onayların yalnızca başarılı işlemeden sonra commit edilmesini ve kapanış boşaltması başlamadan önce yeni mesaj çekmeyi durdurmayı sağlayın.
- Kapanış davranışını staging’de yük altında açıkça test edin (yük testi sırasında pod’ları öldürün) — bu, sık atlanan ve rutin dağıtımlar sırasında gerçek üretim istek kayıplarına yol açan bir testtir.
Yaygın Hatalar
- Kapanış çıkmadan önce bağlantıları boşaltmadığı için dağıtımların sürekli bir istek kaybı sızıntısına yol açması.
- Yük dengeleyicinin, zaten bağlantı kabul etmeyi durdurmuş bir pod’a hâlâ trafik yönlendirmesi (kayıt silme ile trafik yönlendirme arasındaki bir yarış) — yukarıdaki “önce readiness’ı başarısız et, sonra bekle, sonra dur” sırasıyla azaltılır.
SIGTERM‘i hiç ele almamak, zaman aşımından sonra orkestratörünSIGKILL‘ine güvenmek — bu, her seferinde etkin biçimde zarif olmayan/ani bir kapanıştır.
22. Disaster Recovery (Felaket Kurtarma)
Tanım
Bir sistemin normal hata toleransı mekanizmalarının ötesine geçen felaket niteliğindeki bir olaydan (bölge çapında kesinti, veri bozulması, fidye yazılımı, üretim verisini silen insan hatası) sonra operasyonu yeniden sürdürmesini sağlayan strateji, süreçler ve altyapı.
Temel Metrikler
- RTO (Recovery Time Objective - Kurtarma Süresi Hedefi) — bir felaketten sonra servisi geri yükleme için kabul edilebilir maksimum süre.
- RPO (Recovery Point Objective - Kurtarma Noktası Hedefi) — zaman cinsinden ölçülen, kabul edilebilir maksimum veri kaybı miktarı (örn. “5 dakikalık RPO”, en fazla son 5 dakikanın yazmalarını kaybedebileceğiniz anlamına gelir).
DR Stratejileri (artan maliyet, azalan RTO/RPO)
- Backup & Restore (Yedekleme & Geri Yükleme) — dayanıklı depolamaya (genelde bölgeler arası) periyodik yedeklemeler; talep üzerine geri yükleme. En ucuzudur, ama RTO/RPO’su en yüksektir (saatler).
- Pilot Light — DR bölgesinde her zaman çalışan minimal bir ortam versiyonu (örn. sadece replike olan veritabanı); failover’da hesaplamayı ölçeklendirin. Orta maliyet, onlarca dakikalık RTO.
- Warm Standby — DR bölgesinde sürekli çalışan, ölçeği küçültülmüş ama tamamen işlevsel bir replika ortam; failover’da ölçeklendirin. Daha düşük RTO (dakikalar), daha yüksek maliyet.
- Hot Standby / Multi-Site Active-Active — birden fazla bölgede eşzamanlı olarak tam ölçekli, canlı trafik sunan ortamlar; failover neredeyse anlıktır (saniyeler). En yüksek maliyet ve karmaşıklık, ama en düşük RTO/RPO (sıfıra yakın).
En İyi Pratikler
- İş etkisine dayanarak RTO/RPO’yu servis katmanı bazında tanımlayın — her servisin active-active olması gerekmez; maliyetin nerede haklı olduğu konusunda bilinçli olun.
- DR’yi sadece dokümantasyon incelemesiyle değil, gerçek failover’larla düzenli olarak test edin — test edilmemiş bir DR planı bir plan değil, bir hipotezdir (game day’ler, bölgesel failover tatbikatları).
- Sadece donanım hatasına değil, fidye yazılımına ve kasıtlı/kazara silmeye karşı da korumak için yedeklemeleri değişmezlik/versiyonlama ile saklayın (örn. S3 Object Lock).
- Failover runbook’larını mümkün olduğunca otomatikleştirin — gerçek bir felaket sırasında manuel, panik-güdümlü kurtarma yavaştır ve hataya açıktır; script’ler/otomasyon baskı altında insan hatasını azaltır.
- Yedeklemelerin birincil ortamdan coğrafi olarak ayrı olduğundan emin olun ve geri yüklenebilirliği düzenli olarak doğrulayın (hiç geri yüklemediğiniz bir yedekleme, bir yedekleme değil, bir umuttur).
Yaygın Hatalar
- Hiç test-restore edilmemiş yedeklemeler — bozuk/uyumsuz bir yedeklemeyi gerçek bir felaket sırasında keşfetmek.
- DNS, sırlar/konfigürasyon yönetimi, üçüncü taraf servisler gibi bağımlılıkların da failover gerektirdiğini hesaba katmayan DR planları — uygulama “failover” olur ama konfigürasyon deposuna ulaşamaz.
- Yüksek erişilebilirliği (bir bölge içindeki bileşen hatalarına dayanma) felaket kurtarma (tüm bir bölgenin/verinin kaybını atlatma) ile karıştırmak — farklı mimariler gerektirirler ve genellikle yanlışlıkla aynı yatırım olarak ele alınırlar.
- Bir felaketi ilan etmek ve failover’ı tetiklemek için net bir karar yetkisi/süreci olmaması — buradaki belirsizlik gerçek bir olayda değerli dakikalara mal olur.
Hızlı Referans Özeti
| Konu | Temel Trade-off |
|---|---|
| CAP Teoremi | Bölünme sırasında Tutarlılık vs. Erişilebilirlik |
| Tutarlılık | Tazelik garantisinin gücü vs. gecikme/erişilebilirlik |
| Nihai Tutarlılık | Yüksek erişilebilirlik/verim vs. sınırlı eskime |
| Güçlü Tutarlılık | Doğruluk garantisi vs. gecikme/erişilebilirlik maliyeti |
| Replikasyon | Dayanıklılık/erişilebilirlik vs. yazma gecikmesi & karmaşıklık |
| Sharding | Yatay ölçek vs. shard’lar arası işlem karmaşıklığı |
| Partitioning | Paralellik/sıralama granülerliği vs. operasyonel yük |
| Lider Seçimi | Koordinasyon basitliği vs. failover karmaşıklığı |
| Dağıtık Kilit | Karşılıklı dışlama vs. deadlock/güvenlik riski (fencing gerektirir) |
| Konsensüs | Güçlü anlaşma vs. verim & gecikme |
| Quorum | Tutarlılık (W+R>N) vs. erişilebilirlik/gecikme |
| Hata Toleransı | Yedeklilik maliyeti vs. dayanıklılık |
| Hata Tespiti | Tespit hızı vs. yanlış-pozitif oranı |
| İdempotentlik | Mühendislik disiplini vs. retry’lar altında güvenlik |
| Retry | Dayanıklılık vs. amplifikasyon riski |
| Exponential Backoff | Kurtarma süresi vs. sistem rahatlaması |
| Circuit Breaker | Hızlı-başarısız koruma vs. geçici erişilemezlik |
| Timeout | Yanıt verebilirlik vs. erken hata |
| Rate Limiting | Adalet/koruma vs. meşru patlama toleransı |
| Backpressure | Kararlılık vs. yük altında verim |
| Graceful Shutdown | Dağıtım güvenliği vs. kapanış gecikmesi |
| Disaster Recovery | Hazırlık maliyeti vs. RTO/RPO hedefleri |
Bu rehber kavramsal ve pratik bir referans olarak tasarlanmıştır. Belirli sayıları (zaman aşımları, eşikler, quorum boyutları) körü körüne varsayılanları kopyalamak yerine her zaman kendi sisteminizin ölçülen gecikme/hata karakteristiklerine göre doğrulayın.