7 dk

Postgres transaction'ları çok adımlı iş akışları için: pratik desenler

Postgres transaction'larını çok adımlı iş akışları için öğrenin: güncellemeleri güvenle nasıl gruplayacağınız, kısmi yazmaları nasıl önleyeceğiniz, retry'leri nasıl ele alacağınız ve veriyi nasıl tutarlı tutacağınız.

Postgres transaction'ları çok adımlı iş akışları için: pratik desenler

Çok adımlı güncellemeler neden sıklıkla tutarsızlaşır

Çoğu gerçek özellik tek bir veritabanı güncellemesi değildir. Bunlar kısa bir zincirdir: bir satır ekle, bir bakiyeyi güncelle, bir durumu işaretle, bir denetim kaydı yaz, belki bir işi kuyruğa al. Kısmi yazma, bu adımlardan sadece bazıları veritabanına ulaşınca ortaya çıkar.

Bu durum zinciri bir şey kestiğinde görülür: bir sunucu hatası, uygulama ile Postgres arasındaki zaman aşımı, adım 2'den sonra bir çökme veya adım 1'i tekrar çalıştıran bir retry. Her ifade kendi başına doğrudur. İş akışı yarıda durduğunda işler bozulur.

Genellikle bunu hızlıca fark edebilirsiniz:

  • Bir satır mevcut, ama ilişkili satır eksik (sipariş oluşturulmuş, öğeler yok)
  • Para taşınmış ama durum değişmemiş (ödendi, hâlâ ödenmemiş olarak işaretli)
  • Bir olması gereken iki kayıt (retry sonrası çoğaltılmış abonelikler)
  • Bayraklar birbirini tutmuyor (kullanıcı "aktif" ama hiç planı yok)
  • Yalnızca yük altında veya hatalarda ortaya çıkan durumlar

Somut bir örnek: bir plan yükseltmesi müşterinin planını günceller, bir ödeme kaydı ekler ve kullanılabilir kredileri artırır. Uygulama ödeme kaydını sakladıktan sonra kredileri eklemeden çökerse destek tarafı bir tabloda "ödendi" diğerinde "kredi yok" görür. İstemci retry yaparsa ödemeyi iki kez kaydedebilirsiniz.

Amaç basit: iş akışını tek bir anahtar gibi ele alın. Ya her adım başarılı olur, ya hiçbiri olmaz; böylece yarım kalmış iş saklamazsınız.

Transaction nedir, basitçe

Transaction, veritabanının şu demesinin yoludur: bu adımları tek bir iş birimi gibi ele al. Ya tüm değişiklikler olur, ya hiçbiri. Bu, birden fazla güncelleme gerektiren iş akışlarının tamamında önemlidir: bir satır oluşturma, bakiye güncelleme, denetim kaydı yazma gibi.

Parayı bir hesaptan diğerine taşımayı düşünün. Hesaptan düşmeli ve diğerine eklemelisiniz. Uygulama ilk adımın ardından çökerse, sistem sadece düşmeyi "hatırlamamalıdır".

Commit vs rollback

Commit yaptığınızda Postgres'e şöyle dersiniz: bu transaction'da yaptığım her şeyi tut. Tüm değişiklikler kalıcı olur ve diğer oturumlardan görünür.

Rollback yaptığınızda Postgres'e şöyle dersiniz: bu transaction'da yaptığım her şeyi unut. Postgres değişiklikleri transaction hiç olmamış gibi geri alır.

Postgres'in garantileri (ve garantilemedikleri)

Bir transaction içinde, Postgres commit etmeden önce yarım kalmış sonuçları diğer oturumlara göstermeyeceğini garanti eder. Bir şey başarısız olup rollback yapılırsa veritabanı o transaction'ın yazımlarını temizler.

Transaction kötü iş akışı tasarımını düzeltmez. Yanlış miktarı çıkartırsanız, yanlış kullanıcı kimliğini kullanırsanız veya gerekli bir kontrolü atladıysanız Postgres yanlış sonucu olduğu gibi commit eder. Transaction'lar ayrıca doğru kısıtlar, kilitler veya izolasyon seviyesi eşlik etmedikçe her iş düzeyi çatışmayı (örneğin stok fazlası satışı) otomatik olarak engellemez.

Hangi iş akışları gruplanmalı

Gerçek dünya eylemini tamamlamak için birden fazla tabloyu (veya birden fazla satırı) güncellediğiniz her durumda transaction adayı vardır. Nokta aynı: ya her şey yapılmalı, ya hiçbiri.

Sipariş akışı klasik örnektir. Bir sipariş satırı oluşturabilir, stoğu rezerve edebilir, ödeme alabilir, sonra siparişi "ödendi" olarak işaretleyebilirsiniz. Ödeme başarılı ama durum güncellemesi başarısız olursa paranız alınır ama sipariş hâlâ ödenmemiş görünür. Sipariş satırı oluşturulmuş ama stok rezerve edilmemişse satışını yaptığınız ürünler aslında yok olabilir.

Kullanıcı kaydı da sessizce aynı şekilde bozulur. Kullanıcıyı oluşturmak, profil kaydı eklemek, rolleri atamak ve bir karşılama e-postasının gönderilmesi gerektiğini kaydetmek tek bir mantıksal eylemdir. Gruplanmazsa, oturum açabilen ama izinleri olmayan bir kullanıcı veya kullanıcı olmayan bir profile sahip bir kayıt ortaya çıkabilir.

Back-office işlemleri genellikle sıkı "denetim izi + durum değişikliği" davranışı gerektirir. Bir isteği onaylamak, denetim kaydı yazmak ve bakiyeyi güncellemek birlikte başarılı olmalıdır. Bakiye değişir ama denetim kaydı eksikse kim neyi, neden değiştirdiğini gösteren kanıtı kaybedersiniz.

Arka plan işleri de fayda sağlar; özellikle bir iş öğesini işlerken birden fazla adım varsa: öğeyi sahiplen, işçi çatışmasını önle, iş güncellemesini uygula, raporlama ve retry için sonucu kaydet, sonra öğeyi tamamlandı veya başarısız (sebep ile) olarak işaretle. Bu adımlar ayrılırsa retry'ler ve eşzamanlılık karmaşa yaratır.

SQL yazmadan önce iş akışını tasarlayın

Çok adımlı özellikler, onları bağımsız güncellemeler yığını gibi ele aldığınızda kırılır. Bir veritabanı istemcisi açmadan önce iş akışını tek bir kısa hikaye olarak yazın: kullanıcı için "tamam" sayılacak kesin bitiş çizgisi nedir?

Önce adımları yalın dilde listeleyin, sonra tek bir başarı koşulu tanımlayın. Örneğin: "Sipariş oluşturuldu, stok rezerve edildi ve kullanıcı bir sipariş onay numarası görüyor." Bu koşulun altında kalan her şey başarısızlıktır, tablolardan bazıları güncellenmiş olsa bile.

Sonra veritabanı işi ile dış iş arasına sert bir çizgi çizin. Veritabanı adımları transaction ile koruyabileceğiniz adımlardır. Kart ödemeleri, e-posta gönderimleri veya üçüncü taraf API çağrıları gibi dış çağrılar yavaş ve tahmin edilemez şekilde başarısız olabilir ve genellikle geri alınamaz.

Basit bir planlama yaklaşımı: adımları (1) tamamen birlikte olması gereken, (2) commit'ten sonra olabilecek olarak ayırın.

Transaction içine ne koyacağınıza karar verin

Transaction içinde birlikte tutmanız gereken adımları sadece şunlarla sınırlayın:

  • Temel satırları oluşturmak veya güncellemek (sipariş, fatura, hesap bakiyesi)
  • Paylaşılan kaynakları rezerve etmek (stok, koltuk, kota)
  • Dayanıklı "bir sonraki ne yapılacak" olayı kaydetmek (outbox tablosu)
  • Kuralları kısıtlarla uygulamak (unique key, foreign key)

Yan etkileri dışarı taşıyın. Örneğin, önce siparişi commit edin, sonra outbox kaydına göre onay e-postasını gönderin.

Her adım için rollback beklentilerini yazın

Her adım için, bir sonraki adım başarısız olursa ne olması gerektiğini yazın. "Rollback" ya veritabanı rollback'i anlamına gelebilir, ya da telafi edici bir işlem anlamına.

Örnek: ödeme başarılı ama stok rezerve edilemediyse, önceden geri ödeme yapmaya mı karar vereceksiniz, yoksa siparişi "ödeme alındı, stok bekleniyor" olarak mı işaretleyip bunu asenkron olarak mı halledeceksiniz?

Adım adım: bir iş akışını transaction'a sarma

Transaction, Postgres'e şu demek için kullanılan bir yoldur: bu adımları bir birim olarak ele al. Ya hepsi olur, ya hiçbiri. Bu, kısmi yazmaları önlemenin en basit yoludur.

Temel akış

Başlangıçtan bitişe tek bir veritabanı bağlantısı (tek oturum) kullanın. Adımları farklı bağlantılara yayarsanız Postgres tümüyle ya hep ya hiç sonucunu garanti edemez.

Sıra basittir: BEGIN, gerekli okuma ve yazmaları çalıştırın, her şey başarılıysa COMMIT, aksi halde ROLLBACK yapın ve net bir hata döndürün.

İşte SQL ile minimal bir örnek:

BEGIN;

-- reads that inform your decision
SELECT balance FROM accounts WHERE id = 42 FOR UPDATE;

-- writes that must stay together
UPDATE accounts SET balance = balance - 50 WHERE id = 42;
INSERT INTO ledger(account_id, amount, note) VALUES (42, -50, 'Purchase');

COMMIT;

-- on error (in code), run:
-- ROLLBACK;

Kısa ve hata ayıklaması kolay tutun

Transaction'lar çalışırken kilit tutar. Ne kadar uzun açık tutarsanız, diğer işleri o kadar engellersiniz ve zaman aşımı veya deadlock yaşama olasılığınız artar. Transaction içinde gerekli olanı yapın ve yavaş işleri (e-posta gönderimi, ödeme sağlayıcı çağrıları, PDF üretimi) dışarı taşıyın.

Bir şey başarısız olduğunda, hassas verileri sızdırmadan sorunu yeniden üretebilecek yeterli bağlamı loglayın: iş akışı adı, order_id veya user_id, ana parametreler (tutar, para birimi) ve Postgres hata kodu. Tam payload'ları, kart verilerini veya kişisel detayları loglamaktan kaçının.

Eşzamanlılık temelleri: kilitler ve izolasyon, jargon olmadan

Yan etkileri dışarı taşıyın
Outbox event'leri ve background job'ları modelleyin, böylece yan etkiler commit sonrası gerçekleşir.

Eşzamanlılık, aynı anda iki şeyin gerçekleşmesidir. Son konser bileti için iki müşteri aynı anda ödeme yapmaya çalışsın. Her iki ekranda da "1 kaldı" görünür, ikisi de "Öde"ye basar ve şimdi uygulamanız kime vereceğine karar vermeli.

Koruma olmadan, iki istek aynı eski değeri okuyup her ikisi de güncelleme yazabilir. Böylece negatif stok, çoğaltılmış rezervasyonlar veya siparişi olmayan bir ödeme ortaya çıkar.

Satır kilitleri en basit korumadır. Değiştireceğiniz satırı kilitlersiniz, kontrolleri yapar, sonra güncellersiniz. Aynı satıra dokunan diğer transaction'lar sizin commit veya rollback yapmanıza kadar bekler, bu da çift güncellemeyi engeller.

Yaygın bir desen: bir transaction başlatın, stok satırını FOR UPDATE ile seçin, stok olup olmadığını doğrulayın, azaltın, sonra siparişi insert edin. Bu kritik adımları tamamlarken "kapıyı tutar."

İzolasyon seviyeleri, eşzamanlı transaction'ların ne kadar çakışmasına izin verdiğinizi kontrol eder. Genel olarak güvenlik ile hız arasında bir takas vardır:

  • Read Committed (varsayılan): hızlıdır, ama ifadeler arasında diğerlerinin commit ettiği değişiklikleri görebilirsiniz.
  • Repeatable Read: transaction kararlı bir anlık görüntü görür, tutarlı okumalar için iyidir, daha fazla retry gerektirebilir.
  • Serializable: en güçlü güvenlik; Postgres sonuçları tek tek çalışmış gibi tutmak için bir transaction'ı abort edebilir.

Kilitleri kısa tutun. Bir transaction dış API çağrısı yaparken veya kullanıcı aksiyonunu beklerken açık oturursa uzun beklemeler ve zaman aşımı olur. Açık bir başarısızlık yolu tercih edin: bir lock timeout ayarlayın, hatayı yakalayın ve istekleri asla takılmaya bırakmayıp "lütfen tekrar deneyin" döndürün.

Veritabanı dışında iş yapmanız gerekiyorsa (örneğin kart çekme), iş akışını bölün: hızlıca rezerve edin, commit edin, sonra yavaş kısmı yapın ve kısa başka bir transaction ile sonlandırın.

Duplicate oluşturmayan retry'ler

Retry'ler Postgres tabanlı uygulamalarda normaldir. Bir istek doğru kodla bile başarısız olabilir: deadlock'lar, statement timeout'lar, kısa ağ kopmaları veya daha yüksek izolasyon seviyelerinde serialization error. Aynı handler'ı tekrar çalıştırırsanız ikinci bir sipariş oluşturma, iki kez ücretlendirme veya duplicate "event" satırları riski vardır.

Çözüm idempotency: aynı girdiyle işlemi iki kez çalıştırmak güvenli olmalı. Veritabanı bu isteğin "aynı" istek olduğunu tanıyabilmeli ve tutarlı cevap vermeli.

Pratik bir desen: her çok adımlı iş akışına bir idempotency anahtarı (çoğunlukla istemci tarafından oluşturulan request_id) ekleyin ve bunu ana kayda depolayın, sonra bu anahtar için unique constraint koyun.

Örneğin: checkout'ta kullanıcı Pay'e tıkladığında request_id oluşturun, sonra siparişi o request_id ile insert edin. Retry olursa ikinci deneme unique kısıtı vurur ve yeni bir tane oluşturmak yerine mevcut siparişi döndürürsünüz.

Genellikle önemli olanlar:

  • Duplicate'ları engellemek için (request_id) veya (user_id, request_id) üzerinde unique constraint kullanın.
  • Kısıt vurulduğunda mevcut satırı alıp aynı sonucu döndürün.
  • Yan etkiler için de aynı kuralı uygulayın: bir sipariş için yalnızca bir payment intent, sipariş için yalnızca bir "order confirmed" eventi.
  • Destek için request_id'yi loglayın.

Retry döngüsünü transaction dışına taşıyın. Her deneme yeni bir transaction ile başlamalı ve iş birimini baştan tekrar çalıştırmalı. Başarısız bir transaction içinde retry yapmak işe yaramaz çünkü Postgres o transaction'ı aborted olarak işaretler.

Küçük bir örnek: uygulamanız sipariş oluşturup stoğu rezerve etmeye çalışır ama COMMIT'ten hemen sonra zaman aşımı olur. İstemci retry yapar. Idempotency anahtarı ile ikinci istek zaten oluşturulmuş siparişi döndürür ve ikinci bir rezervasyon yapmaz.

Kuralları sadece kodunuzda değil veritabanında da uygulayın

Hata senaryolarını güvenle test edin
İterasyon yaparken transaction'lar ve rollback'leri güvenle denemek için snapshot'larla deneyin.

Transaction'lar çok adımlı iş akışını bir arada tutar, ama veriyi otomatik olarak doğru yapmaz. Hata olursa bile "yanlış" durumları veritabanında zorlamak veya imkânsız kılmak, uygulama kodunda bir hata olsa bile kısmi yazma etkilerini azaltmanın güçlü bir yoludur.

Temel güvenlik bariyerleri ile başlayın. Foreign key'ler referansların gerçek olmasını sağlar (sipariş satırı eksik bir siparişe işaret edemez). NOT NULL yarım satırları durdurur. CHECK kısıtları mantığa aykırı değerleri yakalar (örneğin quantity > 0, total_cents >= 0). Bu kurallar her yazmada çalışır, hangi servis veya script veritabanına dokunsa.

Daha uzun iş akışları için durum değişikliklerini açıkça modelleyin. Birçok boolean flag yerine tek bir status sütunu (pending, paid, shipped, canceled) kullanın ve yalnızca geçerli geçişlere izin verin. Bunu kısıtlar veya trigger'larla enforce edebilir, veritabanının illegal geçişleri reddetmesini sağlayabilirsiniz (ör. shipped -> pending gibi).

Benzersizliği başka bir doğruluk biçimi olarak kullanın. Duplicate'lar iş akışınızı bozacaksa unique constraint ekleyin: order_number, invoice_number veya retry'ler için kullanılan idempotency_key. Eğer uygulamanız aynı isteği tekrar gönderirse Postgres ikinci insert'i engeller ve siz "zaten işlendi" döndürerek güvenle devam edersiniz.

İzlenebilirlik gerektiğinde bunu açıkça depolayın. Kim neyi ne zaman değiştirdiğini kaydeden bir audit (veya history) tablosu, "esrarengiz güncellemeleri" olay sırasında sorgulanabilir olgulara çevirir.

Kısmi yazmalara yol açan yaygın hatalar

Çoğu kısmi yazma "kötü SQL" yüzünden değil. Onları kolaylaştıran iş akışı kararları yüzünden ortaya çıkar.

Gerçek uygulamalarda görülen tuzaklar

  • Transaction açıkken yavaş dış işleri yapmak. Bir ödeme sağlayıcısını çağırmak, e-posta göndermek veya bir dosya yüklemek transaction içinde gereğinden fazla kilit tutar. API yavaşsa veya zaman aşımına uğrarsa diğer kullanıcılar açık transaction'ın arkasında kuyruğa girer.
  • Transaction dışında okuma yapmak, sonra daha sonra yazmak. Örnek: bir kullanıcının bakiyesini okuyup ekranda gösterirsiniz, sonra o eski değere dayanarak düşersiniz. Başka bir oturum aradaki zamanda bakiyeyi değiştirmiş olabilir.
  • Hata yakalayıp yine de bir şeyi commit etmek. Yaygın bir desen: "adım 1 dene, adım 2 dene, hatayı logla, yine de başarı döndür." Eğer kod COMMIT'e ulaşırsa ama bir adım başarısız olduysa veritabanını kasten tutarsız hale getirmiş olursunuz.
  • Farklı kod yollarında tabloları farklı sırayla güncellemek. Bir istek accounts sonra orders güncellerken, başka bir istek tam tersi sırayı kullanıyorsa yük altında deadlock ihtimalini artırırsınız.
  • Transaction'ları çok uzun açık tutmak. Uzun transaction'lar yazmaları engeller, vacuum temizliğini geciktirir ve kafa karıştırıcı zaman aşımı durumları yaratır.

Somut örnek: checkout sırasında stoğu rezerve eder, siparişi oluşturur, sonra kartı çalarsınız. Kartı transaction içinde çalarsanız, ağ beklerken stok kilidini tutuyor olabilirsiniz. Eğer çekim başarılı ama transaction daha sonra rollback olursa müşteriyi karttan çektiniz ama sipariş yok.

Daha güvenli bir desen: transaction içinde sadece veritabanı durumuna odaklanın (stok rezerve et, sipariş oluştur, ödeme girişini pending olarak kaydet), commit edin, sonra dış API'yi çağırın ve sonucu yeni kısa bir transaction ile geri yazın. Birçok ekip bunu pending durumu ve arka plan job ile uygular.

Her şeyi ya hiçini yapan işlemler için hızlı kontrol listesi

Bir iş akışı birden fazla adım içerdiğinde (insert, update, charge, send), amaç basittir: ya her şey kaydedilsin, ya hiçbiri.

Transaction sınırları

Gerekli tüm veritabanı yazmalarını tek bir transaction içinde tutun. Bir adım başarısız olursa rollback yapın ve veriyi tam olarak öncekine döndürün.

Başarı koşulunu açıkça belirtin. Örneğin: "Sipariş oluşturuldu, stok rezerve edildi ve ödeme durumu kaydedildi." Buın dışındaki her şey hata yoludur ve transaction'ı abort etmelidir.

  • Gerekli tüm yazmalar tek bir BEGIN ... COMMIT bloğu içinde olmalı.
  • Veritabanında açık bir "tamam" durumu olmalı (sadece uygulama hafızasında değil).
  • Her hata ROLLBACK ile sonuçlanmalı ve çağırana net bir başarısızlık döndürülmeli.

Retrylerin zarar vermemesi için güvenlik rayları

Aynı isteğin tekrar edilebileceğini varsayın. Veritabanı sadece-bir-kez kurallarını uygulamanıza yardımcı olmalı.

  • Tek seferlik eylemleri unique constraint ile destekleyin (bir sipariş için tek bir ödeme kaydı veya bir öğe için tek bir rezervasyon).
  • Retry'leri güvenli ve tekrarlanabilir yapın (aynı girdi aynı nihai durumu üretir, duplicate değil).

Transaction'ları kısa tutun

Transaction içinde minimum işi yapın, ağ çağrıları sırasında kilit tutmaktan kaçının.

  • Transaction'ları kısa tutun ve takılmamaları için timeout ayarlayın.
  • Yavaş işleri dışarı taşıyın, sonra sonucu yeni kısa transaction ile kaydedin.

Hataları gözlemleyin

Nerede kırıldığını göremezseniz tahmin etmeye devam edersiniz.

  • Her hatada iş akışı adımını ve request id'yi loglayın.
  • Rollback oranlarını ve kilit zaman aşımı metriklerini izleyin, böylece kısmi yazma riskini erken yakalarsınız.

Örnek: hata altında tutarlı kalan bir checkout akışı

Retryleri zararsız hale getirin
Retry'leri güvenli hale getirmek için idempotency anahtarları kullanan Postgres destekli bir uygulama oluşturun.

Bir checkout birkaç adımı birlikte hareket ettirmelidir: siparişi oluştur, stoğu rezerve et, ödeme denemesini kaydet, sonra sipariş durumunu işaretle.

Kullanıcı 1 adet ürün için Satın Al'a tıkladığını düşünün.

Güvenli bir akış (DB işi bir birim)

Tek bir transaction içinde yalnızca veritabanı değişikliklerini yapın:

  • orders tablosuna pending_payment durumuyla bir satır ekleyin.
  • Stoğu rezerve edin (örneğin inventory.available'ı azaltın veya bir reservations satırı oluşturun).
  • İstemci tarafından sağlanan idempotency_key (unique) ile bir payment_intents satırı ekleyin.
  • "order_created" gibi bir outbox satırı ekleyin.

Herhangi bir ifade başarısız olursa (stok yok, kısıt hatası, çökme), Postgres tüm transaction'ı rollback eder. Sipariş ama rezervasyon yok; veya rezervasyon ama sipariş yok gibi durumlar oluşmaz.

Ödeme yarıda kalırsa ne olur?

Ödeme sağlayıcı veritabanınızın dışında olduğu için onu ayrı bir adım olarak ele alın.

Sağlayıcı çağrısı commit'ten önce başarısız olursa transaction'ı iptal edin ve hiçbir şey yazılmasın. Sağlayıcı çağrısı commit'ten sonra başarısız olursa yeni bir transaction ile ödeme denemesini başarısız olarak işaretleyin, rezervasyonu serbest bırakın ve siparişi iptal edilmiş olarak ayarlayın.

İkinci bir sipariş yaratmadan retry

İstemcinin her checkout denemesi için bir idempotency_key göndermesini sağlayın. Bunu payment_intents(idempotency_key) üzerinde unique indeks ile zorlayın (veya isterseniz orders üzerinde). Retry durumunda kod mevcut satırları bulup devam eder; yeni sipariş oluşturmaz.

E-postalar ve bildirimler

E-postaları transaction içinde göndermeyin. Aynı transaction içinde bir outbox kaydı yazın, commit'ten sonra arka plan worker e-postayı göndersin. Böylece rollback olan bir sipariş için asla e-posta gönderilmez.

Sonraki adımlar: bu hafta bir iş akışına uygulayın

Bir haftada birden fazla tabloya dokunan bir iş akışı seçin: kayıt + karşılama e-postası kuyruğa alma, checkout + stok, fatura + defter kaydı, veya proje oluşturma + varsayılan ayarlar.

Önce adımları yazın, sonra her zaman doğru olması gereken kuralları (invariants) tanımlayın. Örnek: "Bir sipariş ya tamamen ödeli ve rezerve edilmiş olur, ya ödenmemiş ve rezerve edilmemiş. Asla yarım rezerve olmaz." Bu kuralları tek bir ya hep ya hiç birimine çevirin.

Basit bir plan:

  • Sırasıyla tam olarak hangi SQL operasyonlarının yapılacağını listeleyin (okumalar, insertler, güncellemeler, silmeler).
  • Eksik olan veritabanı kısıtlarını önce ekleyin (unique key, foreign key, check constraint).
  • Retry'ler duplicate oluşturmasın diye isteğe bir idempotency anahtarı ekleyin.
  • Adımları tek bir transaction içine sarın ve başarı noktasını açık hale getirin (tüm kontroller geçmeden commit yapmayın).
  • Güvenli bir retry'in nasıl görünmesi gerektiğine karar verin (aynı idempotency anahtarı, aynı sonuç).

Sonra kasıtlı olarak çirkin durumları test edin. Adım 2'den sonra çökme simüle edin, commit'ten hemen önce zaman aşımı simüle edin, UI'dan çift gönderim yapın. Hedef sıkıcı sonuçlar: yetim satırlar yok, iki kat ücretlendirme yok, sonsuza kadar bekleyen pending yok.

Hızlı prototipte, handler'lar ve şema üretmeden önce iş akışını planlama-öncelikli bir araçla taslak çıkarmak yardımcı olabilir. Örneğin, Koder.ai (koder.ai) Planning Mode'a sahip ve snapshot ve rollback destekliyor; transaction sınırları ve kısıtlar üzerinde iterasyon yaparken kullanışlı olabilir.

Bu hafta bir iş akışı için bunu yapın. İkinci olan çok daha hızlı olacaktır.

SSS

Postgres işlemini ne zaman kullanmalıyım?

Bir kullanıcı işlemi, birbiriyle tutarlı kalması gereken birkaç satırı veya tabloyu değiştiriyorsa işlem kullanın. Yaygın örnekler arasında ödeme, hesap transferleri, rol atamalı kayıt işlemleri ve stok rezervasyonları bulunur.

COMMIT Postgres'te ne yapar?

COMMIT, işlemdeki tüm değişiklikleri kalıcı hale getirir ve diğer veritabanı oturumlarının görmesini sağlar. Yalnızca gereken tüm kontroller ve yazma işlemleri başarılı olduktan sonra COMMIT çalıştırın.

ROLLBACK yaptığımda ne olur?

ROLLBACK işlemi iptal eder ve onaylanmamış değişikliklerini kaldırır. Kodunuz, başarısız olan zorunlu bir adımda geri alma yapmalı, ardından çağıranın işleyebileceği bir hata döndürmelidir.

Tüm işlem sorguları aynı bağlantıyı mı kullanmalı?

Bu iş akışındaki veritabanı okumalarını ve yazmalarını BEGIN'den COMMIT veya ROLLBACK'a kadar aynı veritabanı bağlantısında tutun. Ayrı bağlantılar ayrı işlemler oluşturur.

Bir işlem içinde ödeme veya e-posta API'lerini çağırmalı mıyım?

Hayır. İşlemler veritabanı değişikliklerini bir arada tutar, ancak karttan çekilen tutarı, e-postayı veya üçüncü taraf API çağrısını geri almaz. Önce veritabanı durumunuzu kaydedin, bir giden kutusu olayı veya bekleyen durum kaydı oluşturun, ardından harici işlemi ayrı olarak çalıştırın.

İki kullanıcının son ürünü satın almasını nasıl engellerim?

Kullanılabilir stok gibi paylaşılan bir değeri kontrol etmeden ve güncellemeden önce satırı kilitleyin. Örneğin, stok satırını FOR UPDATE ile seçin, stoğu doğrulayın, miktarı azaltın ve işlemi onaylamadan önce siparişi oluşturun.

Yeniden denemeler yinelenen siparişleri nasıl önleyebilir?

Her isteğe sabit bir idempotency anahtarı verin ve bunu benzersiz bir kısıtlamayla zorunlu kılın. Yeniden deneme bu kısıtlamaya takıldığında, başka bir sipariş veya ödeme kaydı eklemek yerine mevcut sonucu yükleyip döndürün.

Yeniden denemeler neden yeni bir işlemle başlamalı?

Yeniden denemeye yeni bir işlemde en baştan başlayın. Bir ifade hatasından sonra Postgres geçerli işlemi iptal edilmiş olarak işaretler; bu nedenle geri alma yapılana kadar güvenle devam edemez.

Tutarsız verileri önlemeye hangi veritabanı kısıtlamaları yardımcı olur?

Geçerli referanslar için foreign key'leri, zorunlu alanlar için NOT NULL'u, geçerli değerler için CHECK kısıtlamalarını ve yalnızca bir kez bulunması gereken kayıtlar için benzersiz kısıtlamaları kullanın. Bu kurallar, uygulama kodunda hata olsa bile verileri korur.

İşlemler neden kısa sürmeli?

İşlemleri hızlı veritabanı çalışmalarıyla sınırlı tutun. Uzun işlemler kilitleri daha uzun süre tutar, bekleme sürelerini artırır ve özellikle bir ağ çağrısını bekliyorlarsa kilitlenmelere veya zaman aşımlarına yol açabilir.

Related posts