PDA

View Full Version : هماهنگ نبودن transaction



rexi1r
پنج شنبه 24 تیر 1395, 11:52 صبح
سلام
دوستان من یه فیلد دارم به اسم count که مثلا مقدار 100 داره و هر کاربری که به این دسترسی پیدا میکنه من یسری عملیات انجام میدم و یکی از این عملیات ها هم کم کردن یک مقدار از همین عدد 100 میشه.یعنی با هر بار دسترسی من از 100 یکی کم میکنم تا زمانی که به صفر میرسه .وقتی به صفر رسید من یه فیلد دیگه رو مقدار دهی میکنم (مثلا تموم شده) و دیگه اجازه دسترسی به این رکورد رو نمیدم و کل این عملیات رو داخل بلاک کد زیر انجام میدم.مشکل اینجاست که ممکنه در لحظه بیش از 100 نفر درخواست دسترسی بدن(مثلا 150 نفر) اینجاست که همه با هم به رکورد دسترسی پیدا میکنن و مقدار فیلد count در نهایت خیلیی کمتر از صفر میشه (به ازای اون 50 نفر اضافه میشه 50-).اینجاست که محاسبات من میریزه به هم .چیکار باید کنم که حتی اگه هزار نفر هم همزمان دسترسی میخواستن انجام نشه در حقیقت مثلا یه صف باشه .مثل عملیات بانکی .من تا جایی که میدونستم باید از transaction استفاده میکردم که همین کار هم کردم ولی خب نتیجه نداده .
using (TransactionScope scope = new TransactionScope())

این رو فراموش کردم که اضافه کنم حالتی پیش میاد که که فیلد count هنوز به صفر نرسیده ولی مقدار اون یکی فیلد تموم شده ثبت میشه.سوال اینجاست این بخاطر اینه که به صورت همزمان تعداد زیادی کاربر به این رکورد دسترسی پیدا میکنه؟؟؟؟؟؟چطور ممکنه وقی شرط من اینه که اگر مقدار فیلد count کمتر از صفر بود مقدار اون یکی فیلد تموم شده بشه، بعد قبل از اینکه اون صفر بشه این تموم شده میشه؟؟؟؟؟!!!!!!!

rexi1r
شنبه 26 تیر 1395, 08:21 صبح
دوستان کسی تجربه پیاده سازی یه همچین چیزی رو نداشته؟

En_MK
شنبه 26 تیر 1395, 14:02 عصر
سلام من فکر میکنم اگر شما قبل از هربار کم کردن از count چک کنید که مقدارش اگر بزرگتر از صفر هست اینکارو انجام بدید درغیراینصورت برید سراغ فیلد بعدی حتی اگر میلیونها کاربرهم فعال باشند مشکلی نباید پیش بیاد

rexi1r
شنبه 26 تیر 1395, 14:43 عصر
سلام ممنون بابت پاسخ من دقیقا همین کار رو انجام میدم ببین.
using (TransactionScope scope = new TransactionScope())
{

User tbluser;

tbluser = db.Channels.Where(a => a.Id == User.Id && a.tgChannelId == User.tgChannelId).FirstOrDefault();
tbluser.remainCount -= 1;
if (tbluser.remainCount <= 0)
{
tbluser.status = "تموم شده";
tbluser.finishDate = OurPersianDate.shamsi(DateTime.Now) + " " + OurPersianDate.shamsi_time(DateTime.Now);
}

db.Entry(tbluser).State = EntityState.Modified;
db.SaveChanges();
........
........

}

ولی بازم وقتی تعداد زیادی کاربر همزمان درخواست میدن اشتباه پیش میاد.مثلا حتی حالتی دارم که متغیرcount مقدار منفی 400 داره !!!!!!!!!!!!!!!!

En_MK
شنبه 26 تیر 1395, 15:45 عصر
من فکر میکنم شما اگر دو خط زیر را در else قرار بدی مشکلتون باید حل بشه


else {
db.Entry(tbluser).State = EntityState.Modified; db.SaveChanges();

}




این موقع عا شما یه break point بذار و Debugکن ببینی چطور مقادیر تغییر میکنه

rexi1r
شنبه 26 تیر 1395, 17:00 عصر
دقیقا همین که میگی درسته.من هیچ شرطی برای متوقف کردن نذاشتم و اشتباه کردم . این رو اصلاح میکنم و درستش میکنم و احتمالا درست میشه.

یه سوال داشتم شما فک کنید 500 نفر وارد این دستور transaction بالا میشه و هر کدوم مقدار remainCount رو بخونه خب تو اون لحظه شما فرض کنید مقدار remainCount =0 باشه واسه همه. و واسه همه عملیات ذخیره سازی انجام میشه.یعنی چون واسه همه ی 500 نفر مقدار remainCount بزرگتر از 0 بوده باید عملیات ذخیره سازی واسه همشون انجام بشه!!!!!!!!!؟؟؟؟؟؟؟؟
سوالم اینه که transaction موقعی که میخواد complete بشه (دقیقا لحظه complete شدن) شرط ها رو مجددا چک میکنه؟؟؟چون اگه اینکارو انجام بده عین یه صف میشه که میخواد 500 عملیات ذخیره سازی رو پشت سر هم انجام بده و تو هر ذخیره سازی شرط ها مجدد چک بشه بنابراین اولین عملیات ذخیره که انجام شد مقدار remainCount کمتر از صفر میشه و دیگه 499 عملیات بعدی انجام نمیشه.

اما اگه تو لحظه complete شرط ها چک نشه ، چون از اول مقدار remainCount=0 بوده پس هر 500 عملیات انجام میشه و مقدار نهایی remainCount = -500 میشه.

نمیدونم تونستم سوالم رو خوب توضیح بدم؟؟؟؟؟

rexi1r
دوشنبه 04 مرداد 1395, 08:35 صبح
دوستان این transaction همچنان برای من درست انجام نمیشه تازه جدیدا فهمیدم بعضی جاها بعضی از دستورات داخل transaction رو انجام میده و بعضی ها رو انجام نمیده. کد زیر رو ببینید:

public HttpResponseMessage PostCurrepted(User user, [FromUri] string phonenumber, [FromUri] string reason)
{

if (!reason.Contains("NOT_FOUND"))
{
int coin = 0;
string persiandate = OurPersianDate.shamsi(DateTime.Now) + " " + OurPersianDate.shamsi_time(DateTime.Now);
using (TransactionScope scope = new TransactionScope())
{

User databaseUser;
try
{
try
{

databaseUser = db.Users.Where(a => a.Id == User.Id && a.tgUserId == User.tgUserId).FirstOrDefault();
databaseUser.reasonState = 1;
if (String.IsNullOrEmpty(reason))
databaseUser.reason = "معیوب";
else
{
if (reason.Contains("USERNAME_NOT_OCCUPIED"))
databaseUser.reason = "کاربر معیوب است.";
else if (reason.Contains("NOT_FOUND"))
databaseUser.reason = "کاربر یافت نشد.";
}

db.Entry(databaseUser).State = EntityState.Modified;
db.SaveChanges();
}
catch (Exception d)
{
var jsonerror =
new
{
result = "server User",
message = d.Message,
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}


RequestMember reqmem;

try
{

try
{
reqmem = db.RequestMembers.Where(a => a.Userincreamentalid == databaseUser.Id && a.phonenumber == databaseUser.phonenumber && a.tgUserid == databaseUser.tgUserId).FirstOrDefault();

}
catch (Exception d)
{
var jsonerror =
new
{
result = "server RequestMember before",
message = d.Message,
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}
try
{
reqmem.reasonState = 1;
if (String.IsNullOrEmpty(reason))
reqmem.reason = "معیوب";
else
{
if (reason.Contains("USERNAME_NOT_OCCUPIED"))
reqmem.reason = "کاربر معیوب است.";
else if (reason.Contains("NOT_FOUND"))
reqmem.reason = "کاربر یافت نشد.";
}

db.Entry(reqmem).State = EntityState.Modified;
db.SaveChanges();

}
catch (Exception d)
{
var jsonerror =
new
{
result = "server RequestMember after",
message = d.Message,
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}


}
catch (Exception d)
{
var jsonerror =
new
{
result = "server",
message = d.Message,
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}




}
catch (Exception e)
{
var jsonerror =
new
{
result = "server",
message = e.Message,
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}

scope.Complete();


}

var json =
new
{
result = "OK",
message = "",
error = "",
method = "PostCurrepted",
data = coin
};
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK, json);
return result;
}
else
{
var jsonerror =
new
{
result = "server",
message = "",
error = "خطایی رخ داده است.",
method = "PostCurrepted",
data = ""
};
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, jsonerror);
return res;
}
}




اگه دقت کنید من داخل transaction روی دوتا جدول تغییرات انجام میدم ولی گاها پیش میاد که تغییرات روی یکی ذخیره میشه ولی روی دومی انجام نمیشه!!!!!!!!!!!!!!!!!!!؟؟؟؟ :ناراحت::ناراحت::ناراحت:

کلا به نظرتون چطور میشه این کد رو بهینه تر کرد؟؟؟؟