Dışarıdaki vs İçerideki Veri — Pat Helland'dan Uygulama Dersleri
Pat Helland'ın "dışarıdaki vs. içerideki veri" fikrini öğrenin: net sınırlar belirleyin, idempotent çağrılar tasarlayın ve ağ hatalarında durumu uzlaştırmayı planlayın.

“Dışarıdaki vs İçerideki” ne demek, sade dille
Bir uygulama kurarken isteklerin düzgünce, tek tek ve doğru sırada geldiğini hayal etmek kolaydır. Gerçek ağlar böyle çalışmaz. Kullanıcı ekran donduğu için "Öde"ye iki kez tıklar. Mobil bağlantı düğmeden hemen sonra kopar. Bir webhook geç gelir ya da iki kez gelir. Bazen hiç gelmez.
Pat Helland'ın dışarıdaki vs içerideki veri fikri bu karmaşayı düşünmenin net bir yoludur.
“Dışarı” nasıl görünür
“Dışarı” sisteminizin kontrol etmediği her şeydir. Başkalarıyla ve diğer sistemlerle konuştuğunuz yer, teslimatın belirsiz olduğu yer: tarayıcılardan ve mobil uygulamalardan gelen HTTP istekleri, kuyruk mesajları, üçüncü taraf webhook'lar (ödeme, e‑posta, gönderim) ve istemciler, proxy'ler veya arka plan işleri tarafından tetiklenen yeniden denemeler.
Dışarıda mesajların gecikebileceğini, çoğaltılabileceğini veya yanlış sırada gelebileceğini varsayın. "Genelde güvenilir" olsa bile bir gün güvenilmez olacağını tasarlayın.
“İçeride” ne anlama gelir
“İçeride” sisteminizin güvenilir kılabildiği şeydir. Sakladığınız dayanıklı durum, uyguladığınız kurallar ve daha sonra kanıtlayabileceğiniz gerçekler şunlardır:
- Veritabanı kayıtları ve geçmişleri
- İş kuralları (örneğin: “bir sipariş yalnızca bir kez ödenebilir”)
- Durum için bir tek gerçek kaynağı (beklemede, ödendi, iptal edildi gibi)
İçeride değişmezlikleri (invariants) korursunuz. "Sipariş başına bir ödeme" diye söz veriyorsanız, bu söz içeride uygulanmalıdır çünkü dışarı güvenilemez.
Zihniyet değişimi basittir: mükemmel teslimat veya mükemmel zamanlama varsaymayın. Her dış etkileşimi tekrar edilebilecek güvenilmez bir öneri gibi ele alın ve içeriğin buna güvenli şekilde tepki vermesini sağlayın.
Bu küçük ekipler ve basit uygulamalar için bile önemlidir. Bir ağ sorunu ilk kez çift ücretlendirme veya takılı bir sipariş yarattığında, bu teori olmaktan çıkar ve iade, destek bileti ve güven kaybı olur.
Somut bir örnek: kullanıcı "Siparişi ver" diyor, uygulama bir istek gönderiyor ve bağlantı kopuyor. Kullanıcı tekrar deniyor. İçeride "bu aynı deneme" olduğunu tanıma yolunuz yoksa iki sipariş oluşturabilir, stoğu iki kez rezerve edebilir veya iki onay e‑postası gönderebilirsiniz.
Pat Helland'dan temel ders
Helland'ın noktası açık: dış dünya belirsizdir, ama sisteminizin içi tutarlı kalmalıdır. Ağ paketleri düşer, telefonlar sinyal kaybeder, saatler kayar ve kullanıcılar yenileyebilir. Uygulamanız bunların hiçbirini kontrol edemez. Kontrol edebileceği şey, verinin net bir sınırı aşıp içeride "doğru" olarak kabul edildiği zamandır.
Günlük bir anda zaman ve belirsizlik
Birinin kötü Wi‑Fi olan bir binada yürürken telefondan kahve siparişi verdiğini hayal edin. "Öde"ye tıklar. Dönen yükleniyor göstergesi görünür. Ağ kopar. Tekrar tıklar.
Belki ilk istek sunucunuza ulaştı ama yanıt geri dönmedi. Ya da belki hiçbiri ulaşmadı. Kullanıcının gözünden her iki olasılık da aynı görünür.
İşte zaman ve belirsizlik: henüz ne olduğunu bilmiyorsunuz ve sonra öğrenebilirsiniz. Sisteminiz beklerken makul davranmalı.
Yeniden denemeler, kopyalar ve yeniden sıralama
Dışarının güvenilmez olduğunu kabul ettikten sonra bazı “garip” davranışlar normalleşir:
- Yeniden denemeler kopyalar yaratır (iki "Öde" isteği).
- Mesajlar yanlış sırada gelir (önce "iptal" sonra "öde" gibi).
- Bir istek işlendi ama istemci yanıtı hiç görmez.
Dış veri bir iddiadır, bir gerçek değil. "Ödedim" demek, güvenilmez bir kanal üzerinden gönderilen bir beyanattır. İçeride kalıcı, tutarlı şekilde kaydettiğinizde gerçek olur.
Bu sizi üç pratik alışkanlığa iter: net sınırlar tanımlayın, yeniden denemeleri idempotent yapın ve gerçeklik uyuşmadığında uzlaşma planlayın.
Net sınırlar: sisteminizin sahip olduğu ve olmadığı şeyler
“Dışarı vs içeride” fikri pratik bir soruyla başlar: sisteminizin doğruluğu nerede başlar ve nerede biter?
Sınırın içinde güçlü garantiler verebilirsiniz çünkü veriyi ve kuralları siz kontrol edersiniz. Sınırın dışında ise en iyi çabayı gösterir ve mesajların kaybolabileceğini, çoğaltılabileceğini, gecikebileceğini veya yanlış sırada gelebileceğini varsayarsınız.
Gerçek uygulamalarda bu sınır genellikle şu yerlerde görünür:
- Veritabanınıza bir kayıt yazan bir API uç noktası
- Bir olayı saklanan bir değişikliğe çeviren kuyruk tüketicisi
- Bir sağlayıcının ne olduğunu söylediğini kaydeden callback handler'ı
- Kendi durumunuzu commit ettikten sonra başka bir sistemi bilgilendiren sender
O çizgiyi çektikten sonra hangi değişmezliklerin tartışılamaz olduğunu karar verin. Örnekler:
- Bir sipariş kimliği veritabanınızda benzersizdir.
- Bir bakiye asla negatif olmaz.
- Bir durum yalnızca ileri hareket eder (created -> paid -> shipped).
- Kabul ettiğiniz her dış istek bir denetim izi ile saklanır.
Sınır ayrıca "nerede olduğumuz" için net bir dil gerektirir. Birçok hata "sizi duyduk" ile "bitirdik" arasındaki boşlukta yaşar. Yararlı bir desen üç anlamı ayırmaktır:
- Alındı (Received): mesaj kenarınıza ulaştı (henüz kaydedilmemiş olabilir)
- Kabul edildi (Accepted): kaydettiniz ve işi daha sonra güvenle yeniden deneyebilirsiniz
- İşlendi (Processed): amaçlanan iş tamamlandı ve sonucu kaydettiniz
Takımlar bunu atladığında yalnızca yük altında veya kısmi kesintiler sırasında meydana gelen hatalar çıkar. Bir sistem "ödendi" derken paranın çekildiğini; bir diğeri ödeme girişiminin başlatıldığını kastedebilir. Bu uyumsuzluk çiftler, takılı siparişler ve kimsenin çoğu kez yeniden üretemediği destek biletleri yaratır.
Idempotency: yeniden denemeleri güvenli kılmak
Idempotency şu anlama gelir: aynı istek iki kez gönderilse bile sistem onu tek bir istekmiş gibi ele alır ve aynı sonucu döndürür.
Yeniden denemeler normaldir. Zaman aşımı olur. İstemciler kendilerini tekrarlar. Dışarı tekrar edebiliyorsa, içeri bunun sonucunu sabit duruma dönüştürmelidir.
Basit bir örnek: bir mobil uygulama "20$ öde" gönderir ve bağlantı kopar. Uygulama yeniden dener. Idempotency yoksa müşteri iki kez ücretlendirilebilir. Idempotency ile ikinci istek ilk ücretlendirme sonucunu döndürür.
Idempotency'yi uygulamanın yaygın yolları
Çoğu ekip şu desenlerden birini (bazen karışık) kullanır:
- Idempotency key: istemci her niyet için benzersiz bir anahtar gönderir (örneğin
Idempotency-Key: ...). Sunucu anahtarı ve nihai yanıtı kaydeder. - De-duplication tablosu: (client_id, key) veya (order_id, operation) ile anahtarlanan bir satır saklayın ve ikinci yan etkiyi reddedin.
- Doğal anahtarlar: zaten benzersiz olan iş kimliklerini kullanın, böylece "ödeme oluştur" yalnızca bir kez var olabilir.
Bir kopya geldiğinde en iyi davranış genellikle "409 conflict" veya genel bir hata değil; ilk kez verdiğinizle aynı sonucu geri döndürmektir; aynı kaynak kimliği ve durum dahil. Bu, istemciler ve arka plan işleri için yeniden denemeleri güvenli kılar.
Kaydı nerede tutmalı (ve ne kadar süre)
Idempotency kaydı sınırınız içinde kalıcı depolamada olmalıdır, bellekte değil. API'niz yeniden başlarsa ve unutursa, güvenlik garantisi ortadan kalkar.
Kayıtları gerçekçi yeniden denemeleri ve gecikmiş teslimleri kapsayacak kadar uzun tutun. Pencere iş riskine bağlıdır: düşük riskli yaratımlar için dakikalar–saatler, çift maliyeti olan ödemeler/e‑postalar/gönderimler için günler ve partnerlerin uzun süre yeniden deneyebileceği durumlarda daha uzun.
“Dağıtık işlem” tuzaklarından kaçınma
Dağıtık işlemler teselli edici gelebilir: servisler, kuyruklar ve veritabanları arasında tek bir büyük commit. Pratikte genellikle kullanılamaz, yavaştır veya güvenilmezdir. Bir ağ sıçraması olduğunda her şeyin birlikte commit edildiğini varsayamazsınız.
Yaygın bir tuzak her adımın hemen şimdi başarılı olmasıyla çalışan bir iş akışı inşa etmektir: siparişi kaydet, kartı çek, stoğu rezerve et, onay gönder. 3. adım zaman aşımına uğradığında başarısız mı oldu yoksa oldu mu? Yeniden denersem çift ücretlendirir veya çift rezerve eder miyim?
Bunu önlemenin iki pratik yolu vardır:
- Outbox/inbox: durum değişikliğinizle aynı işlem içinde gönderilecek mesaj niyetini (outbox satırı) yazın, ardından bir worker mesajı gönderir. Alıcı tarafta inbox'u mesaj ID'siyle anahtarlayın, böylece aynı mesaj tekrar gelirse güvenle işlenir.
- Saga tarzı adımlar ve telafi eylemleri: iş akışını bağımsız olarak tamamlanan küçük adımlara bölün. Sonraki adım başarısız olursa, tarihi geri almaya çalışmak yerine telafi edici bir işlem çalıştırın (örneğin stoğu serbest bırak veya ödenmemiş siparişi iptal et).
Her iş akışı için bir stil seçin ve ona sadık kalın. "Bazen outbox kullanıyoruz" ile "bazen senkron başarı varsayıyoruz"u karıştırmak test edilmesi zor kenar durumlar yaratır.
Basit bir kural yardımcı olur: sınırların ötesinde atomik olarak commit edemiyorsanız, yeniden denemeler, kopyalar ve gecikmeler için tasarlayın.
Uzlaşma: gerçek sistemler uyuşmazlıklardan nasıl kurtulur
Uzlaşma temel bir gerçeği kabul etmektir: uygulamanız ağ üzerinden diğer sistemlerle konuştuğunda bazen ne olduğunu konusunda anlaşmazlık olacaktır. İstekler zaman aşımına uğrar, callback'ler geç gelir ve insanlar eylemleri yeniden dener. Uzlaşma, uyuşmazlıkları tespit edip zaman içinde düzeltme yönteminizdir.
Dış sistemleri bağımsız gerçek kaynakları olarak ele alın. Uygulamanız kendi iç kaydını tutar, ama partnerlerin, sağlayıcıların ve kullanıcıların aslında ne yaptığını karşılaştırma yolunuz olmalıdır.
Yaygın uzlaşma mekanizmaları
Çoğu ekip küçük ama etkili araçlar kullanır (sıkıcı olmak iyidir): bekleyen eylemleri yeniden deneyen bir worker, tutarsızlıklar için zamanlanmış bir tarama ve destek ekibinin yeniden denemesi, iptal etmesi veya incelendi olarak işaretlemesi için küçük bir onarım aracı.
Ne karşılaştırmalı ve ne kaydetmeli
Uzlaşma yalnızca neyi karşılaştıracağınızı biliyorsanız işe yarar: iç defter vs sağlayıcı defteri (ödemeler), sipariş durumu vs gönderim durumu (fulfillment), abonelik durumu vs faturalama durumu.
Durumları onarılabilir yapın. "created"dan doğrudan "completed"a atlamak yerine, bekleme gibi ara durumlar kullanın. Bu "emin değiliz" demeyi güvenli kılar ve uzlaşmanın iniş yapacağı net bir yer verir.
Önemli değişikliklerde küçük bir denetim izi yakalayın:
- Bir isteği ne zaman gönderdiğiniz ve en son ne zaman cevap aldığınız
- Kayıdınızı dış bir olaya/reference bağlayan korelasyon ID'leri
- Son bilinen dış durum (ve nereden geldiği)
- Manuel geçersiz kılmalara ilişkin neden alanı (kim, ne, neden)
Örnek: uygulamanız bir gönderim etiketi isterken ağ koparsa, içerde "etiket yok" olabilir ama taşıyıcı aslında bir etiket oluşturmuş olabilir. Bir recon worker korelasyon ID'si ile arama yapıp etiketi bulabilir ve siparişi ilerletebilir (veya ayrıntılar uyuşmuyorsa inceleme için işaretleyebilir).
Ağ hatasını atlatan bir iş akışı tasarlama adım adım
Ağı başarısız olacak varsayımıyla hedef değişir. Amacınız artık her adımın tek denemede başarılı olması değil. Amacınız her adımı tekrar etmek için güvenli ve onarımı kolay yapmak.
Pratik bir iş akışı
-
Bir cümlelik bir sınır beyanı yazın. Sisteminizin neyin kaynağı olduğunu, neyi yansıttığınızı ve yalnızca başkalarından ne istediğinizi açıkça belirtin.
-
Mutluluk yolundan önce hata modlarını listeleyin. En azından: zaman aşımı (işleyip işlemediğini bilmiyoruz), tekrar eden istekler, kısmi başarı (bir adım oldu, sonraki olmadı) ve sırasız olaylar.
-
Her girdi için bir idempotency stratejisi seçin. Senkron API'ler için genelde idempotency anahtarı artı saklanan sonuç; mesajlar/olaylar için genelde benzersiz mesaj ID'si ve "bunu işledim mi?" kaydı.
-
Niyeti kalıcı hale getirip sonra harekete geçin. Önce
PaymentAttempt: pendingveyaShipmentRequest: queuedgibi dayanıklı bir şey saklayın, sonra dış çağrıyı yapın, ardından sonucu kaydedin. Yeniden denemelerin aynı niyete işaret etmesi için stabil bir referans ID döndürün. -
Uzlaşma ve onarım yolunu oluşturun ve görünür hale getirin. Uzlaşma "çok uzun süredir bekleyen" kayıtları yeniden kontrol eden bir iş olabilir. Onarım eylemi güvenli bir admin işlemi (yeniden dene, iptal et veya çözülmüş olarak işaretle) ve bir denetim notu olabilir. Temel gözlemlenebilirlik ekleyin: korelasyon ID'leri, net durum alanları ve birkaç sayaç (bekleyen, yeniden deneme, başarısızlık).
Örnek: checkout zaman aşımına uğradıktan hemen sonra ödeme sağlayıcısına çağrı yaptıysanız, tahminde bulunmayın. Denemeyi saklayın, deneme ID'si döndürün ve kullanıcı aynı idempotency anahtarıyla yeniden denerse aynı denemeye işaret etsin. Daha sonra uzlaşma sağlayıcının ücretleyip ücretlemediğini doğrulayabilir ve denemeyi çift ücretlendirmeden güncelleyebilir.
Örnek senaryo: yeniden denemeler ve gecikmiş callback'lerle bir sipariş akışı
Müşteri "Siparişi ver"e tıklar. Servisiniz sağlayıcıya bir ödeme isteği gönderir, ama ağ dalgalıdır. Sağlayıcının kendi gerçeği, veritabanınızın ise sizin gerçeğiniz vardır. Bunlar tasarlanmazsa sapar.
Dışarıda neler olur (kontrol etmediğiniz olaylar)
Sizin açıdan dışarı şu tip mesaj akışıdır: geç, tekrar veya eksik gelebilir:
- "Siparişi gönder" API'nıza gelir.
- Ödeme isteğiniz sağlayıcıya gider.
- Sağlayıcı "authorized" diyen bir webhook gönderir.
- Sağlayıcı webhook'u tekrar gönderir ve aynı callback'i yollar.
- İstemciniz zaman aşımına uğrar ve "Siparişi ver"i tekrar gönderir.
Bu adımların hiçbiri "tam olarak bir kez" garantilemez. Sadece "belki" garantiler.
İçeride ne tutarsınız (kontrol ettiğiniz kayıtlar)
Sınırınızın içinde dayanıklı gerçekleri ve dış olayları bunlara bağlamak için gereken minimumu saklayın.
Müşteri ilk siparişi verdiğinde order kaydı pending_payment gibi net bir durumda oluşturun. Ayrıca benzersiz bir sağlayıcı referansı ve müşterinin eylemine bağlı bir idempotency_key ile bir payment_attempt kaydı oluşturun.
İstemci zaman aşımına uğrayıp yeniden denediğinde API ikinci bir sipariş oluşturmak yerine idempotency_keyi arayıp aynı order_id ve mevcut durumu döndürmelidir. Bu tek tercih ağ hatalarında çoğalmayı önler.
Şimdi webhook iki kez gelir. İlk callback payment_attempt'ı authorized olarak günceller ve siparişi paida taşır. İkinci callback aynı handler'a gelir ama sağlayıcı event ID'sini saklayarak veya mevcut durumu kontrol ederek zaten işlendiğini tespit edersiniz ve hiçbir şey yapmazsınız. Yine 200 OK dönebilirsiniz çünkü sonuç zaten doğrudur.
Son olarak, uzlaşma dağınık vakaları halleder. Sipariş bir gecikme sonra hâlâ pending_payment ise, bir arka plan işi saklı referansı kullanarak sağlayıcıyı sorgular. Sağlayıcı "authorized" diyorsa ama webhook'u kaçırdıysanız kayıtları güncellersiniz. Sağlayıcı "failed" diyor ama siz paid olarak işaretlediyseniz inceleme için işaretleyin veya telafi edici eylem (iade) başlatın.
Çoğalmaya ve takılı durumlara yol açan yaygın hatalar
Çoğu çoğaltılmış kayıt ve "takılı" iş akışı, dış sistemde ne olduğuyla (bir istek geldi, bir mesaj alındı) içeride güvenle commit ettiğiniz şeyleri karıştırmaktan kaynaklanır.
Klasik bir hata: istemci "sipariş ver" gönderir, sunucunuz işi başlatır, ağ kopar ve istemci yeniden dener. Her yeniden denemeyi tamamen yeni gerçek olarak ele alırsanız çift ücretlendirme, çift siparişler veya birden fazla e‑posta gönderirsiniz.
Yaygın nedenler:
- Gelen isteğe çok erken güvenmek: veritabanı commit'i kalıcı olmadan önce e‑posta göndermek veya "sipariş oluşturuldu" kaydı tutmak.
- Yeniden denemelerin yeni satırlar oluşturması: her denemede yeni bir sipariş ID'si üretmek yerine denemeleri tek bir sonuca eşlememek.
- "Tam olarak bir kez" teslimat varsaymak: kuyruklar ve callback'ler böyle bir garanti vermez. Kopyalar, gecikmeler ve yeniden sıralama olur.
- Kararlı tanımlayıcı yok: "bu kesin niyeti daha önce gördüm mü?" sorusuna cevap veremiyorsanız çoğaltmayı engelleyemezsiniz.
- Sadece başarı/başarısızlık, ara durum yok: bekleme/ara durumları yoksa zaman aşımı gizemleri çıkar ve kullanıcı tekrar tıklar.
Bir hatayı daha kötü yapan şey: denetim izi yok. Alanları üzerine yazar ve yalnızca en son durumu saklarsanız, daha sonra uzlaştırmak için gereken kanıtı kaybedersiniz.
İyi bir kontroller sorusu: "Bu handler'ı iki kez çalıştırırsam aynı sonucu alıyor muyum?" Cevap hayırsa, çoğaltmalar nadir bir kenar durum değil, garantilidir.
Hızlı bir kontrol listesi ve pratik sonraki adımlar
Hatırlamanız gereken tek şey: uygulamanız mesajlar geç geldiğinde, iki kez geldiğinde veya hiç gelmediğinde bile doğru kalmalıdır.
Bu kontrol listesi, zayıf noktaları çoğaltılmış kayıtlar, eksik güncellemeler veya takılı iş akışlarına dönüşmeden önce bulmanıza yardımcı olur:
- Gerçeğin kaynağı açık: her iş akışı için bir yerin "gerçek" olduğunu gösterebiliyorsunuz (çoğunlukla veritabanınız).
- Her yazma güvenle yeniden denenebilir: her komut/API çağrısının bir idempotency anahtarı (veya doğal benzersiz anahtar) var.
- Uçtan uca stabil ID'ler ve korelasyon ID'leri mevcut: bir iş eylemini loglarda, tablolarda ve callback'lerde takip edebilirsiniz.
- Uzlaşma otomatik çalışıyor: "bizim inancımız" ile "gerçekleşen"i düzenli karşılaştırıp onarır veya açık bir alarm yükseltir.
- Rollback durumları durumu bozmaz: durum değişiklikleri denetlenebilir ve sürümler arası uyumludur.
Bunlardan birine hızlı cevap veremiyorsanız, genellikle sınırın belirsiz olduğu veya bir durum geçişinin eksik olduğu anlamına gelir.
Pratik sonraki adımlar:
-
Önce sınırları ve durumları çizin. Her iş akışı için küçük bir durum seti tanımlayın (örneğin: Created, PaymentPending, Paid, FulfillmentPending, Completed, Failed).
-
En çok riskli yazmalara idempotency ekleyin. En yüksek riskli işlemlerle başlayın: sipariş oluşturma, ödeme alma, iade etme. Idempotency anahtarlarını PostgreSQL'de benzersiz kısıtla saklamak çoğaltmaları güvenli şekilde reddeder.
-
Uzlaşmayı normal bir özellik olarak ele alın. "Çok uzun süredir bekleyen" kayıtları arayan bir job planlayın, dış sistemleri yeniden kontrol etsin ve yerel durumu onarsın.
-
Güvenle yineleyin. Geçerli istekleri bilerek yeniden göndererek ve aynı olayı yeniden işleyerek geçişleri ve yeniden deneme kurallarını test edin.
Eğer Koder.ai (koder.ai) gibi sohbet tabanlı bir platformda hızlıca inşa ediyorsanız, bu kuralları üretilen servislere erkenden yerleştirmek yine de faydalıdır: hız otomasyonla gelir, ama güvenilirlik net sınırlar, idempotent handler'lar ve uzlaşmadan gelir.
SSS
“data on the outside vs inside” basitçe ne demek?
"Dışarıda" sistemin kontrol etmediği her şeydir: tarayıcılar, mobil ağlar, kuyruklar, üçüncü taraf webhook'lar, yeniden denemeler ve zaman aşımları. Mesajların gecikebileceğini, çoğaltılabileceğini, kaybolabileceğini veya yanlış sırada gelebileceğini varsayın.
"İçeride" ise sizin kontrolünüzde olan şeydir: saklanan durumunuz, uyguladığınız kurallar ve daha sonra kanıtlayabileceğiniz gerçekler (genellikle veritabanınızda).
Gelen isteklerin veya webhook'ların tam olarak bir kez gerçekleşeceğine neden güvenemem?
Çünkü ağ size yalan söyler.
Bir istemcinin zaman aşımına uğraması, sunucunuzun isteği işlememiş olduğu anlamına gelmez. Bir webhook'un iki kez gelmesi, sağlayıcının işlemi iki kez yaptığı anlamına gelmez. Her mesajı "yeni gerçek" olarak ele alırsanız, çift siparişler, çift ücretlendirmeler ve takılı iş akışları üretirsiniz.
Tipik bir uygulamada “sınırı” nerede çekmeliyim?
Net bir sınır, güvenilmez bir mesajın kalıcı bir gerçek haline geldiği noktadır.
Yaygın sınırlar:
- Veritabanınıza kaydeden bir API uç noktası
- Bir olayı saklanan değişikliğe dönüştüren bir kuyruk tüketicisi
- Sağlayıcının ne olduğunu söylediğini kaydeden bir callback handler'ı
Veri sınırı geçtikten sonra, içeride (örneğin “sipariş yalnızca bir kez ödenebilir” gibi) değişmezlikleri uygulayın.
Kullanıcılar “Öde”ye yeniden tıkladığında çift ücretlendirmeyi nasıl durdururum?
Idempotency kullanın. Varsayılan kural: aynı niyet birden çok gönderilse bile aynı sonuç üretilmelidir.
Pratik desenler:
- İstemci her eylem için benzersiz bir idempotency anahtarı gönderir
- Sunucu anahtarı ve nihai sonucu kalıcı depolamada saklar
- Tekrarlar geldiğinde, ilk istekte döndürülen aynı kaynak ID/durum döndürülür
Idempotency kayıtlarını nerede saklamalıyım ve ne kadar süre saklamalıyım?
Bunu sadece bellekte tutmayın. Koruma garantisinin devam etmesi için idempotency kaydını sınırınız içinde (örneğin PostgreSQL) saklayın.
Saklama kuralı:
- Düşük riskli işlemler: dakikalar–saatler
- Yüksek maliyetli işlemler (ödeme, iade, gönderim, e-posta): günler veya daha uzun
Gerçekçi yeniden denemeleri ve gecikmiş callback'leri kapsayacak kadar uzun tutun.
“Emin değiliz” hatalarını önlemek için hangi durumları eklemeliyim?
Belirsizliği kabul eden durumlar kullanın.
Basit, pratik bir set:
pending_*(niyeti kabul ettik ama sonucu bilmiyoruz)succeeded/failed(nihai sonucu kaydettik)needs_review(insan veya özel bir iş gerektiren uyuşmazlık tespit edildi)
Bu, zaman aşımı sırasında tahmin yapmayı engeller ve uzlaşmayı kolaylaştırır.
Neden dağıtık işlemler (distributed transactions) genelde tuzaktır?
Çünkü ağ üzerinden birden fazla sistem arasında atomik taahhüt yapmak genellikle mümkün değildir.
“Siparişi kaydet → kartı ücretlendir → stoğu ayır” gibi senkron iş akışları, bir adım zaman aşımına uğradığında ne yapılacağını belirsiz bırakır. Yeniden denemek çift işleme, denememek ise yarım kalmış iş bırakır.
Bunun yerine niyeti önce saklayın, dış çağrıları ayrı adımlarda yapın ve gerektiğinde telafi edici adımlar uygulayın.
Outbox/inbox deseni nedir ve ne zaman kullanmalıyım?
Outbox/inbox deseni ağı mükemmelmiş gibi göstermeden çapraz sistem iletişimini güvenilir hale getirir.
- Outbox: durum değişikliğinizle aynı transaction içinde göndermek istediğiniz mesajı bir satır olarak yazın.
- Bir worker outbox'u okur ve mesajı gönderir.
- Inbox (alıcı taraf): işlenen mesaj ID'lerini saklayın, yeniden teslimler yan etki oluşturmasın.
Uzlaşma (reconciliation) nedir ve basit bir uygulaması nasıl olur?
Uzlaşma, kayıtlarınız ile dış bir sistem arasında uyuşmazlık olduğunda nasıl toparlanacağınızdır.
Basit bir yaklaşım:
- “Çok uzun süredir bekleyen” öğeleri yeniden kontrol eden zamanlanmış bir iş
- Karşılaştırma adımı (iç durum vs sağlayıcı durumu)
- Onarma eylemi: yeniden dene, iptal et, iade et veya
needs_reviewolarak işaretle
Ödemeler, gönderimler ve abonelikler için uzlaşma zorunludur.
Koder.ai gibi hızlı bir platformla inşa ediyorsam bu hala önemli mi?
Evet. Hızlı inşa etmek ağı bozmaz—sadece sorunla daha çabuk karşılaşırsınız.
Koder.ai ile servisler üretirken bu varsayımları erkenden katın:
- Net sınır (niyetin ne zaman kalıcı olduğu)
- Oluştur/capture/refund tarzı eylemler için idempotent handler'lar
- Dış referanslarla korelasyon ID'lerinin saklanması
- Beklemede olan kayıtlar için bir uzlaşma işi
Böylece yeniden denemeler ve çift callback'ler pahalı yerine sıradan hale gelir.