מנגנון ניהול Cache בסיסי

11 ביולי 2009

תגובה אחת

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

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

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

תגובה אחת

  1. אליאור10 באוגוסט 2009 ב 16:47

    כמה דברים,

    א. תודה רבה על הרעיון, לא חשבתי להכין פונקציה שמקבלת פונקציה בתור פרמטר , אני עדיין דיי חדש ל 3.5 בעקבות מגבלות חומרה בארץ בסביבת WEB, כמה שזה נשמע עצוב..

    ב. לא נגעתי ב VB.NET כמה שנים טובות , אבל אני דיי בטוח שהפונקציה שלך נחתכה כי היא אמורה להחזיר משהו, היא מחזירה אובייקט ? מחזירה T?

    ג.הקטע של ה lock , לא הבנתי איך זה בדיוק עובד, כי ברגע שמתחילה בקשה של לקוח על השרת, כל בקשה רצה כאפליקציה נפרדת.. ה lock פועל ברמת ב DB? ברמת הפונקציה בגלל שהיא גלובלית? איך המערכת יודעת להקפיא את שאר הבקשות לבינתיים?

    להגיב