מנגנון ניהול Cache בסיסי
English: Simple Cache management function. how to make cache thinks easier.
The function below can check if something is in the cache, and also if other Web request, execute the heavy function using SyncLock.
לעיתים עולה הצורך לעבוד בתצורה שבה יש משאב (למשל שאילתת SQL כבדה) שיקר לייצר אותו. הפיתרון הפשוט - שומרים אותו בקאש.
Cache("name") = value
או שצריך לפי מגבלת תאריך ושעה:
Cache.Add(Key, Value, Nothing, Expire, Nothing, Priority, Nothing)
אבל כל זה מעצבן כל פעם מחדש.
אחד החידושים שיצאו בגירסא 3.5 של הסביבה, הינה האפשרות להעביר פונקציות אנונימיות (Lambda), כמשתנה לפונקציה.
ישבתי חמש דקות וכתבתי את הפונקציה הבאה:
Public Shared Function GetFromCache(Of T)(ByVal Name As String, ByVal Minutes As Integer, ByVal Func As Func(Of T), Optional ByVal Priority As Web.Caching.CacheItemPriority = CacheItemPriority.Default) As T
Dim s As T
If HttpContext.Current.Cache(Name) Is Nothing Then
s = Func()
HttpContext.Current.Cache.Add(Name, s, Now.AddMinutes(Minutes), Priority)
Return s
Else
Return HttpContext.Current.Cache(Name)
End If
End Function
בקצרה, נורא פשוט: מקבל שם (String כלשהו), תאריך תפוגה, פונקציה ודחיפות, ובודק בצורה הכי פשוטה
- אם המידע קיים ב-Cache - החזר אותו, אם לא - הרץ את הפונקציה.
כך למשל, בדף ASPX שמקבל String מאיפשהו:
sSpecial = MainFuncs.GetFromCache("Specials", 2, AddressOf Specials, CacheItemPriority.High)
עד כאן הכל טוב ויפה, עובד מצויין ומפשט את הדפים (חוסך כל פעם את הIF אם קיים בקאש או לא), אבל כאן יש בעיה.
נניח שיש לנו שאילתת SQL שזמן הביצוע שלה הוא 4 שניות, ולכן נאחסן אותה בקאש לשעתיים. בעת עליית האתר (למשל אחרי Recycle), יש לנו עשרות אלפי כניסות לדף הבית, שכל אחת מהן גוררת את הפונקציה שלנו.
הצרה - עד שהתוכן לא נמצא בקאש, ה-DB יוצף בעשרות אלפי בקשות לשאילתא כבדה ויתקשה להתאושש משך דקות ארוכות. ברגע שיקבל הלקוח הראשון את התוכן - העסק יתחיל להרגע.
גיגול קצר העלה שהפונקציה SyncLock (ב-#C היא נקראת lock), תומכת גם ב-String, אז שיניתי אותה קצת, על מנת לפתור את הבעיה הזו:
Public Shared Function GetFromCache(Of T)(ByVal Name As String, ByVal Minutes As Integer, ByVal Func As Func(Of T), Optional ByVal Priority As Web.Caching.CacheItemPriority = CacheItemPriority.Default) As T
Dim s As T
SyncLock ("GetFromCache" & Name)
If HttpContext.Current.Cache(Name) Is Nothing Then
s = Func()
HttpContext.Current.Cache.Add(Name, s, Now.AddMinutes(Minutes), Priority)
Return s
Else
Return HttpContext.Current.Cache(Name)
End If
End SyncLock
End Function
עכשיו, עד שהראשון יקבל את התשובה - כל שאר הבקשות יחכו בסבלנות בתור, כשלאחר מכן התוצאה היקרה תחכה להם בקאש מוכנה לביצוע.