DCSIMG
מנגנון ניהול Cache בסיסי - מאחורי המסך

מאחורי המסך

משה למפרט, על תכנות מתקדם וביצועים ב-Web.

על הבלוג

עוד חדשות

אתרים שיש לי בהם יד ורגל

מנגנון ניהול 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

עכשיו, עד שהראשון יקבל את התשובה - כל שאר הבקשות יחכו בסבלנות בתור, כשלאחר מכן התוצאה היקרה תחכה להם בקאש מוכנה לביצוע.