Bakiye güncellemede race condition: Pessimistic Lock mı, Redlock mı?
Soru
Kullanıcı bakiyesinden para düşen bir kuyruk işimiz var. Kullanıcı hızlı arka arkaya iki işlem tetiklerse kuyrukta aynı kullanıcı için iki job oluşuyor; 10 paralel worker var. Worker 1 ve 2 aynı anda eski bakiyeyi okuyor (100 TL), ikisi de 40 düşüp 60 TL yazıyor (olması gereken 20 TL). Bu race condition'ı önlemek için Pessimistic Locking mi yoksa Redis dağıtık kilit (Redlock) mı kullanmalıyım?
Cevap
Kısa cevap: Yaşadığın şey klasik lost update (kayıp güncelleme) problemi. Para söz konusuysa invariant’ı DB’de tut; en temiz çözüm atomik koşullu bir UPDATE, ikinci tercih SELECT ... FOR UPDATE. Redlock’a koşma.
Senaryonun özü: oku-değiştir-yaz (read-modify-write) döngüsü paralel worker’lar arasında bölünüyor. İkisi de eski değeri okuyor, ikisi de üstüne yazıyor; biri kayboluyor. Çözüm bu döngüyü atomik kılmak.
- En iyisi: atomik koşullu
UPDATE.UPDATE accounts SET balance = balance - 40 WHERE id = ? AND balance >= 40yaz. Okuma ve yazma tek ifadede; uygulama tarafında kilit gerekmez. DB satır kilidini kendi yönetir, hem race’i çözer hem debalance >= 40koşuluyla eksiye düşmeyi reddeder. Etkilenen satır 0 ise işlem yetersiz bakiyeden başarısız demektir. - Alternatif:
SELECT ... FOR UPDATE. Mantığın okuma ile yazma arasında karmaşıksa, transaction içinde bakiye satırınıFOR UPDATEile kilitle. İkinci worker o satıra geldiğinde birincinin commit’ini bekler; sıraya girerler, çakışma olmaz. Pessimistic locking budur. - Düşük çakışmada optimistic locking de iş görür. Satıra bir
versionkolonu ekle; güncellerkenWHERE version = ?koy, eşleşmezse retry et. Çakışma seyrekse ucuzdur, ama yoğun rekabette retry fırtınası yaratır. - Redlock’u yalnızca DB dışı kaynak için kullan. Redis dağıtık kilidi (Redlock), korunan şey tek bir DB satırı olmadığında (servisler/kaynaklar arası koordinasyon) anlamlıdır — ve clock kayması/timeout gibi köşe durumları vardır. Bir bakiye DB’nin transaction’ıyla zaten korunabiliyorken onu dağıtık bir kilide emanet etme.
Sonuç: Atomik koşullu UPDATE (ya da SELECT FOR UPDATE) kullan; Redlock’u sadece DB dışı kaynaklar için sakla. Para için kuralı transaction’ın garanti ettiği yerde — DB’de — tut; dağıtık kilit, veritabanının kendisinin zaten zorlayabileceği bir invariant’ı korumak için fazla kırılgan bir araçtır.
İlgili Yazılar
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.