Race Condition

အလုပ်မှာ ပြဿနာ တခုဟာ ကိုယ့်နဲ့ မဆိုင်သေးဘူးဆိုရင် ကျွန်တော့် အဖို့က သမန်ကာ လျှံကာ ဖတ်ဖြစ်လိုက်တာပဲရှိတယ်။ သေချာ မလုပ်ဖြစ်ဘူး။ အခုက အလုပ်ကတော့ စစဝင်ချင်းလဲ ဖြစ် များများစားစားလဲ မလုပ်ရသေးဘူးဆိုတော့ အလုပ်နဲ့ ပတ်သတ်ပီး ဖြစ်နိုင်ခြေရှိတဲ့ ပြဿနာလေးတွေ ရှာကြံလေ့လာဖြစ်တယ်။ အဲ့ဒီမှာ တွေ့မိတာတွေထဲက race condition အကြောင်း tutorial လေးပဲဖြစ်ဖြစ် ရေးအုံးမှပဲ စဥ်းစားမိတယ်။ အကြောင်းအမျိုးမျိုး ကြောင့် မရေးဖြစ်တာ ကြာနေပီ ဖြစ်တဲ့ မှတ်စုတိုမှာပဲ ရေးလိုက်တယ် ဆိုပါတော့။

ဘယ်အချိန်မှာဖြစ်တာလဲ ?

Race Condition ဘယ်အချိန်မျိုးမှာ ဖြစ်တာလဲဆိုတော့ ကိုယ့်ရဲ့ application မှာ အလုပ်တခု ထဲကို၂ ခု သို့ ၂ခုထက်ပိုတဲ့  request ကို တပြိုင်ထဲလုပ်လိုက်တဲ့ အချိန်မှာ ဖြစ်တာပါ။ ဥပမာအနေနဲ့ဆိုရရင် သင့် wallet application ကို လူတွေက တချိန်ထဲ ငွေလွဲလိုက်တဲ့ အချိန်မျိုး၊ Inventory Management နဲ့ ချိတ်ထားတဲ့ online shopping ထဲက item တခုကို လူတွေက တချိန်ထဲ ဝယ်လိုက်တာမျိုး၊ App ကရော Web ကပါ အလုပ်တခုတည်းကို တပြိုင်ထဲလုပ်တာတွေ စတာတွေမှာဖြစ်တာပါ။ Database ထဲက data ကို  တပြိုင်ထဲ့ write  လုပ်လိုက်တဲ့ပုံစံမျိုးပါ။

ဘာတွေဖြစ်နိုင်လဲ ?

Attacker တွေအနေတဲ့ ဒီအခြေအနေကို အသုံးချပီး Security issues တွေ Financial problems တွေ Data မမှန်တဲ့ ပြဿနာတွေ စတာတွေဖြစ်ပွားအောင်ပြုလုပ်နိုင်ပါတယ်။ ဒါတင်မဟုတ်ပဲ ကိုယ့် Database ကို deadlock အခြေအနေ ဖြစ်ပေါ်အောင် ဖန်းတီးတဲ့ အခြေအနေမျိုးကို ရောက်ရှိနိုင်ပါတယ်။

ဘယ်လိုဖြေရှင်းကြလဲ ?

ဖြေရှင်းနည်းတွေကတော့ အပိုင်းလိုက်ရှိပါတယ်။ Rate Limit လုပ်တာတွေ Queue လုပ်တာ Lock လုပ်တာတွေ စတာနဲ့ ဖြေရှင်းလို့ရပါတယ်။ ကျွန်တော်ကတော့ interview တွေဖြေနေတုန်းက ကျက်လိုက်ရတဲ့ ACID ကိုအသုံးပြုပီး Mongodb နဲ့ example လေးတခုလုပ်ထားပါတယ်။

ဒီမှာ ကြည့်ကြည့်လို့ရပါတယ်။

ပထမ Test မှာ ကျွန်တော်တို့ အများစု ရေးနေကြအတိုင်း  find နဲ့ရှာပီး count ကို တစ်တိုးလိုက်ပီး database ထဲမှာ save လိုက်ပါတယ်။

    let clickCount = await ClickCount.findOne();

    if (!clickCount) {
        clickCount = new ClickCount();
        clickCount.count = 0;
    }

    clickCount.count += 1;

    await clickCount.save();

အဲ့ဒါက ပုံမှန် အခြေအနေမှာ အဆင်ပြေပေမယ့် concurrent requests လုပ်ကြည့်တဲ့ အချိန်မှာ ပြဿနာတက်ပါတယ်။ count ကို ၁၀ ခါ ခေါ်ပေမယ့် ၁၀ မတိုးတော့ပါ။

အဲ့ဒါကို ဖြေရှင်းဖို့တွက် mongodb မှာ Atomic ဖြစ်တဲ့ findAndModify ကိုပြောင်းပီးရေးလိုက်ပါမယ်။ mongoose သုံးတဲ့ အတွက် findOneAndUpdate ဆိုပီးရေးလိုက်တာပါ။

let clickCount = await ClickCount.findOneAndUpdate({},
        {
            $inc: { count: 1 }
        }, {
        new: true,
        upsert: true 
});

ဒီမှာ စမ်းကြည့်လို့ရပါပီ။

အခု စမ်းတဲ့ count က document တခုထဲကို လုပ်တာပါ။ တကယ်လို တခုထက်ပိုတဲ့ documents တွေကို write လုပ်မယ်ဆိုရင် mongodb က support လုပ်တဲ့ Transaction နဲ့ အုပ်ပီး ရေးလို့ရပါတယ်။ Transaction ထဲမှာ တခုခု ဖြစ်ရင် abortTransaction သုံးပီး rollback ပြန်လုပ်လို့ရပါတယ်။

အသေးစိတ်ဖတ်ချင်တယ်ဆိုရင်တော့ https://www.mongodb.com/basics/acid-transactions

လေ့လာမိသလောက်ကတော့ စမ်းကြည့်တာဖြစ်လို့ တခြား လိုအပ်တာတွေရှိရင်လဲ မှတ်ချက်ပြုပါအုံး။