Aikido

Kilitlenmeleri önlemek için istisna durumlarında bile kilitleri neden serbest bırakmalısınız?

Hata Riski

Kural
Serbest Bırakma kilitleri hatta kilitler istisna yollarda. 
Her kilit kilit mutlaka içermelidir bir garantili
serbest bırakma, hatta istisnalar istisnalar meydana geldiğinde bile. 

Desteklenen diller:** Java, C, C++, PHP, JavaScript,
TypeScript, Go, Python

Giriş

Serbest bırakılmamış kilitler, üretim ortamındaki Node.js uygulamalarında kilitlenme ve sistem donmalarının en yaygın nedenlerinden biridir. Kilidin alınması ile serbest bırakılması arasında bir istisna meydana geldiğinde, kilit süresiz olarak tutulmaya devam eder. Bu kilidi bekleyen diğer asenkron işlemler sonsuza kadar askıda kalır ve sistem genelinde zincirleme arızalara yol açar. Tek bir serbest bırakılmamış mutex, olay döngüsünün tıkanmasına ve isteklerin birikmesine neden olarak tüm API’yi çökertebilir. Bu durum, aşağıdaki gibi kütüphanelerde görülür: async-mutex, mutexify, ya da kilidin otomatik olarak açılmadığı herhangi bir manuel kilit sistemi.

Neden önemli?

Sistem kararlılığı ve erişilebilirliği: Serbest bırakılmayan kilitler, Node.js’de asenkron işlemleri donduran kilitlenmelere (deadlock) neden olur. Express veya Fastify sunucularında bu durum, kullanılabilir iş parçacıklarını tüketir ve uygulamanın yeni istekleri işleyememesine yol açar. Tek çözüm, işlemi yeniden başlatmaktır; bu da hizmet kesintisine neden olur. Mikro hizmet mimarilerinde, bir hizmetteki serbest bırakılmayan kilitler, yanıt beklerken zaman aşımına uğrayan bağımlı hizmetlerde arka arkaya arızalara yol açabilir.

Performans düşüşü: Tam bir kilitlenme durumuna gelinmeden önce, serbest bırakılmamış kilitler ciddi performans sorunlarına yol açar. Asenkron işlemler, kilitli kaynaklar için rekabet eder ve hiçbir zaman sonuçlanmayan bekleyen vaatlerden oluşan bir kuyruk oluşturur. Kilit rekabeti, kullanıcı deneyimini bozan öngörülemez gecikme artışlarına neden olur. Yük altında eşzamanlı istek sayısı arttıkça, bu rekabet katlanarak artar.

Hata ayıklama karmaşıklığı: Serbest bırakılmamış kilitlerden kaynaklanan kilitlenmelerin, üretim ortamındaki Node.js uygulamalarında hata ayıklanması oldukça zordur. Belirtiler asıl sorunun kaynağından oldukça uzakta ortaya çıkar; işlemler askıda kalır ve bekleyen vaatler gösterilir, ancak kilidi serbest bırakmada hangi istisna yolunun başarısız olduğu belli olmaz. Kilitlenmeyi tetikleyen istisnaların tam sırasını geliştirme ortamlarında yeniden oluşturmak genellikle imkânsızdır.

Kaynak tükenmesi: Kilitlerin kendilerinin ötesinde, kilitlerin serbest bırakılmaması genellikle veritabanı bağlantıları, Redis istemcileri veya dosya tanıtıcıları gibi diğer kaynakların serbest bırakılmamasıyla bağlantılıdır. Bu durum sorunu daha da ağırlaştırarak, yük altında sistemlerin daha hızlı çökmesine neden olan çok sayıda kaynak sızıntısına yol açar.

Kod örnekleri

❌ Uygun değil:

const { Mutex } = require('async-mutex');
const accountMutex = new Mutex();

async function transferFunds(from, to, amount) {
    await accountMutex.acquire();

    if (from.balance < amount) {
        throw new Error('Insufficient funds');
    }

    from.balance -= amount;
    to.balance += amount;

    accountMutex.release();
}

Neden güvenli değil: Eğer “yetersiz bakiye” hatası veriliyorsa, accountMutex.release() asla yürütülmez ve mutex sonsuza kadar kilitli kalır. Bundan sonraki tüm transferFunds() mutex’i beklerken takılı kalacak ve tüm ödeme sistemini donduracaktır.

✅ Uygunluk:

const { Mutex } = require('async-mutex');
const accountMutex = new Mutex();

async function transferFunds(from, to, amount) {
    const release = await accountMutex.acquire();
    try {
        if (from.balance < amount) {
            throw new Error('Insufficient funds');
        }
        
        from.balance -= amount;
        to.balance += amount;
    } catch (error) {
        logger.error('Transfer failed', { 
            fromId: from.id, 
            toId: to.id, 
            amount,
            error: error.message 
        });
        throw error;
    } finally {
        release();
    }
}

Neden güvenlidir: Şu yakalamak block, hatayı yeniden atmadan önce bağlamıyla birlikte günlüğe kaydeder ve sonunda Bu blok, işlemin başarılı olması, bir hata oluşması ya da hatanın catch bloğundan yeniden atılması durumlarında bile mutex serbest bırakma işlevinin yürütülmesini garanti eder. Kilit her zaman serbest bırakılır ve bu sayede kilitlenme durumları önlenir.

Sonuç

Kilidin açılması, işlemin başarılı bir şekilde gerçekleştirilmesine bağlı olmamalı, garanti altına alınmalıdır. Şunu kullanın: try-finally JavaScript’teki bloklar ya da runExclusive() şu tür kütüphaneler tarafından sağlanan yardımcı işlevler: async-mutex. Her kilit edinimi, aynı kod bloğu içinde görülebilen koşulsuz bir serbest bırakma yoluna sahip olmalıdır. Doğru kilit yönetimi isteğe bağlı bir şey değildir; bu, kararlı bir sistem ile yük altında rastgele donan bir sistem arasındaki farkı belirler.

Sık Sorulan Sorular

Sorularınız mı var?

JavaScript'te kilidin kesin olarak açılmasını sağlamak için doğru yöntem nedir?

Use try-finally blocks with explicit release in finally. Store the release function returned by acquire() and call it in the finally block. Better yet, use the runExclusive() method provided by libraries like async-mutex which handles acquisition and release automatically: await mutex.runExclusive(async () => { /* your code */ }). This eliminates the chance of forgetting the finally block.

Kilit serbest bırakma işlemi için try-catch-finally mi, yoksa sadece try-finally mi kullanmalıyım?

İstisnaların çağırana yayılmasını istiyorsanız try-finally kullanın. Kilidin serbest bırakılmasını garanti ederken hatayı yerel olarak ele almanız gerekiyorsa try-catch-finally kullanın. Her iki durumda da finally bloğu yürütülür, ancak catch bloğu size hatayı günlüğe kaydetme, dönüştürme veya bastırma imkânı sunar. release() komutunu her zaman finally bloğuna yerleştirin, asla catch bloğuna koymayın; çünkü catch bloğu hatayı yeniden atsa bile finally bloğu yine de yürütülür.

Peki ya “promise” yerine geri çağırma işlevleri kullanan asenkron kilitler?

Öncelikle geri arama tabanlı kodu promise'lere dönüştürün, ardından try-finally ile async/await kullanın. Bu mümkün değilse, her geri arama yolunun (başarı, hata, zaman aşımı) kilidi serbest bırakma işlevini çağırdığından emin olun. Bu yöntem hataya açık olduğundan, promise tabanlı kilitler tercih edilir. Kilitleri serbest bırakmak için asla çöp toplama işlemine güvenmeyin; bu işlem belirleyici değildir ve kilitlenmelere neden olur.

Birlikte elde edilmesi gereken birden fazla kilidi nasıl yönetebilirim?

Herhangi bir iş mantığı uygulamasından önce tüm kilitleri alın ve bunları tek bir finally bloğu içinde ters sırayla serbest bırakın. Daha iyi bir yaklaşım: Döngüsel bağımlılıkları önlemek için kilitlerin her zaman aynı sırayla alındığı bir kilit hiyerarşisi kullanın. Karmaşık durumlarda, işlem koordinatörü desenini veya herhangi bir hata durumunda otomatik serbest bırakma özelliğiyle birden fazla kaynağın kilitlenmesini destekleyen async-lock gibi kütüphaneleri kullanmayı düşünün.

Bir kilidi artık kullanmayacağımı biliyorsam, onu erken serbest bırakabilir miyim?

Evet, ancak son derece dikkatli olun. Serbest bırakma işlemi gerçekleştirildikten sonra, eşzamanlı erişime karşı hiçbir korumanız kalmaz. Yaygın bir yaklaşım, kritik bölümden sonra ancak günlük kaydı veya harici API çağrıları gibi yavaş işlemlerden önce serbest bırakma işlemini gerçekleştirmektir. Ancak, erken serbest bırakma işleminden sonra ancak fonksiyonun sonlandırılmasından önce herhangi bir istisna meydana gelirse, tutarsız durum riskiyle karşı karşıya kalırsınız. Erken serbest bırakmanın neden güvenli olduğunu açıkça belgelendirin.

JavaScript kodunda henüz serbest bırakılmamış kilitleri tespit edebilen araçlar nelerdir?

Statik analiz araçları, karşılık gelen `finally` blokları bulunmayan kilit edinme işlemlerini işaretleyebilir. JavaScript’te yerleşik bir kilitlenme algılama özelliği bulunmadığından, çalışma zamanında bunu tespit etmek daha zordur. Sürekli askıda kalmak yerine hızlı bir şekilde hata vermesi için kilit edinme işlemlerinde zaman aşımı süreleri uygulayın (çoğu kütüphane bunu destekler). Kilit çekişmesi sorunlarını tespit etmek için üretim ortamında söz verilerin reddedilme oranlarını ve olay döngüsü gecikmelerini izleyin.

async-mutex gibi kütüphaneler bu sorunu nasıl önler?

async-mutex provides runExclusive() which acquires the lock, runs your function, and releases the lock automatically even if exceptions occur. It's essentially a built-in try-finally wrapper. Use this when possible: await mutex.runExclusive(async () => { /* your code */ }). This eliminates manual release management and prevents the most common mistake of forgetting the finally block.

Şimdi güvenliğinizi sağlayın

Kodunuzu, bulutunuzu ve çalışma zamanınızı tek bir merkezi sistemde güvenceye alın.
Güvenlik açıklarını otomatik olarak hızla bulun ve düzeltin.

Kredi kartı gerekmez | Tarama sonuçları 32 saniyede.