خطای Connection Pooling در IIS و ADO: دلیل پنهان شکست Pool و راهکار آن
یکی از مشکلاتی که توسعهدهندگان ASP کلاسیک هنگام کار با SQL Server از طریق ADO (ActiveX Data Objects) ممکن است با آن روبرو شوند، تاثیر غیرمنتظره تنظیمات سطح ایزولیشن (Isolation Level) بر Connection Pooling است. این ویژگی که برای بهینهسازی عملکرد و کاهش سربار ایجاد و بستن اتصال به پایگاه داده طراحی شده، میتواند با تغییرات کوچک در کد شما به طرز نامحسوسی از کار بیفتد.
Connection Pooling به IIS اجازه میدهد تا اتصالاتی را که به SQL Server باز کرده است، در یک “Pool” نگهداری کند. هنگامی که برنامه شما به یک اتصال جدید نیاز دارد، به جای ایجاد یک اتصال کاملاً جدید، IIS ابتدا به این Pool نگاه میکند تا ببیند آیا اتصال آمادهای با مشخصات درخواستی (که معمولاً توسط Connection String تعیین میشود) وجود دارد یا خیر. اگر چنین اتصالی پیدا شود، IIS آن را از Pool خارج کرده و به برنامه شما میدهد. پس از اتمام کار، وقتی اتصال بسته میشود، به جای قطع کامل آن، به Pool بازگردانده میشود تا برای درخواستهای بعدی استفاده شود. این مکانیسم بهطور قابل توجهی سرعت و کارایی برنامه را افزایش میدهد.
یک نمونه رایج از Connection String که IIS بر اساس آن اتصالات را در Pool قرار میدهد، به شکل زیر است:
"Provider=SQLOLEDB.1;Data Source=SQLServerName;Initial Catalog=DBName;User ID=UserID;Password=Password;"
معمولاً، ADO از سطح ایزولیشن `Read Committed` به عنوان پیشفرض استفاده میکند. این سطح ایزولیشن به برنامهها اجازه میدهد تا فقط دادههایی را بخوانند که توسط تراکنشها Committed شدهاند و از خواندن دادههای ناتمام (Uncommitted) جلوگیری میکند. اما اگر در بخشی از کد خود سطح ایزولیشن را تغییر دهید، اینجاست که مشکل آغاز میشود.
تصور کنید کد ASP کلاسیک شما به این شکل است:
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open "Provider=SQLOLEDB.1;Data Source=SQLServerName;Initial Catalog=DBName;User ID=UserID;Password=Password;"
' This is the problematic line
objConn.IsolationLevel = adXactReadUncommitted
' Do stuff
objConn.Close
Set objConn = Nothing
در این مثال، خط objConn.IsolationLevel = adXactReadUncommitted سطح ایزولیشن را به `Read Uncommitted` تغییر میدهد. این بدان معناست که اتصال میتواند دادههایی را بخواند که هنوز توسط تراکنشهای دیگر committed نشدهاند. این تغییر، اگرچه به نظر بیضرر میرسد، اما باعث میشود IIS نتواند این اتصال را به درستی در Pool مدیریت کند. به محض اینکه یک اتصال از Pool خارج شود و سطح ایزولیشن آن تغییر یابد، IIS آن را به عنوان یک اتصال “منحصر به فرد” در نظر میگیرد و دیگر آن را به Pool باز نمیگرداند. در نتیجه، به جای بازگرداندن به استخر، IIS آن را کاملاً میبندد و برای هر درخواست بعدی، یک اتصال جدید ایجاد میکند. این روند منجر به کاهش چشمگیر عملکرد و افزایش سربار سرور SQL میشود.
دیگر مقادیر متداول برای خاصیت `IsolationLevel` شامل موارد زیر هستند:
adXactReadCommitted
adXactRepeatableRead
adXactSerializable
همچنین دو مقدار کمتر رایج دیگر نیز وجود دارند:
adXactChaos
adXactBrowse
برای جلوگیری از این مشکل و اطمینان از عملکرد صحیح Connection Pooling، بهترین رویکرد این است که سطح ایزولیشن اتصال را قبل از بسته شدن، به حالت پیشفرض (معمولاً `adXactReadCommitted`) بازگردانید. این کار باعث میشود IIS اتصال را با همان مشخصات اولیه دریافت کرده و بتواند آن را به Pool بازگرداند.
راهکار بهینه برای حل این “گرفتاری” به شکل زیر است:
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open "Provider=SQLOLEDB.1;Data Source=SQLServerName;Initial Catalog=DBName;User ID=UserID;Password=Password;"
' Restore the IsolationLevel property if it's not the default
If objConn.IsolationLevel <> adXactReadCommitted Then
objConn.IsolationLevel = adXactReadCommitted
End If
' Do stuff
objConn.Close
Set objConn = Nothing
با افزودن این بررسی و تنظیم مجدد سطح ایزولیشن به حالت پیشفرض `adXactReadCommitted`، اطمینان حاصل میکنید که IIS میتواند اتصالات را پس از اتمام کار، به درستی به استخر Connection Pooling بازگرداند. این راهکار ساده، پایداری و کارایی برنامههای ASP کلاسیک شما را در محیطهای پر ترافیک به طور چشمگیری بهبود میبخشد و از مشکلات پنهان Connection Pooling جلوگیری میکند. توسعهدهندگان SQL Server و ASP کلاسیک باید همواره به این نکته مهم توجه داشته باشند تا از عملکرد بهینه سیستمهای خود اطمینان حاصل کنند.