שאלה:
יש לי מערכת די גדולה, ואני משתמשת בה בSessions.
ה sessions שלי מאחסנים גם משתנים פשוטים (כמו int לID של המשתמש) וגם משתנים מורכבים (כמו DataSet של כל הפרטים שלו או של תוכן סל הקניות שלו וכדו').
הבעיה היא שה Session מתנהג לי בצורה מאוד לא יציבה, למרות הגדרת הtimeOut ב web.config הוא פג אחרי זמן קצר מאוד של חוסר תזוזה במערכת. וגם- אם אני עושה איזו שהיא פעולה מחוץ למערכת, כמו פעולה על FTP (העברת קבצים, למשל) - כשאני מסיימת את הפעולה הזאת ה sessions פשוט פגים.
תשובה:
Sessions מתאפסים מהרבה מאוד סיבות.
כמה לדוגמה:
1. הגעת לגבול העליון מבחינת זכרון שמוקצה ל-Sessions. פיזית ב-RAM יש מקום שמוקדש ל-Sessions. המקום הזה הוא חלק מאותו מקום שמוקצה ל-HTTP Cache. כברירת מחדל מדובר על 800 מגה (ולא ניתן ולא מומלץ להגדיל את המקס' זכרון הזה) פר מעבד בשרת. כלומר, אם יש יותר מ-800 מגה של נתונים ב-Cache ה-Session גם יתחיל להתנהג מוזר.
2. ה-AppDomain עצמו של השרת אותחל. זה יכול לקרות ממספר סיבות - אתחול של ה-IIS, שגיאת StackOverflow / OutOfMemoryException ועוד שגיאה כזו, שינוי של קבצי ה-config שגורמים ישר לאתחול של האתר וכך הלאה.
3. ה-Session עצמו פג תוקף ביחס לקריטריונים של השרת. הקריטריון הבסיסי הוא כמה מקסימום זמן להחזיק את ה-Session בחיים. אך אם השרת נאלץ לפנות זכרון ב-Cache למשל הוא ישתמש במדיניות LRU (מייצג: Last Recently Used) שאומר שהוא ינקה את ה-Sessionים הישנם ביותר. אז גם אם קבעת 20 דקות פג-תוקף ל-Session, יכול להיות שאחרי 5 דקות השרת יחליט להרוג את ה-Session הכי "זקן" של האתר.
ויש עוד הרבה סיבות.
בגדול, ה-Session לא אמור להחזיק מידע משמעותי מעבר לזיהוי של המשתמש וסטטוס שבאמת רלוונטי ל-"Session" (למפגש הנוכחי בין המשתמש לבין השרת).
כפתרון - עדיף לעבור לצורת אחסון שהיא לא InProc. מצ"ב קישור למאמר שמסביר על האפשרויות:
SessionState בקובץ web.config
שאלת המשך #1: (על פרגמנטציה ב-RAM של ה-Cache)
אתה בטוח שלא ניתן להגדיל (בשקל תשעים) את גבול ה-800 מגה ל-Session?
יותר יעיל לשמור מידע (גם אם הוא רב, וגם אם הוא עבור משתמשים רבים) ב Session במקום לרוץ בכל פעם ל DB.
אני בעד להוסיף זכרון למכונה (עולה גרושים).
תשובה המשך #1:
כן, אני בטוח.
פר מעבד על המכונה ה-RAM של ה-Cache לא יכול לעבור 800 מגה.
ניתן להגדיל את הכמות זכרון הזו, אבל מומלץ שרק מומחה יעשה את זה, וגם אז שמישהו כמוני יושב ומביט פיזית על מצב הפרגמנטציה על ה-RAM, אי-אפשר לעבור את גבול ה-900 מגה ו-950 במקרה הממש טוב שאתה רק שומר מספרים בין 0 ל-3.
זה לא משהו קל ומי שלא מבין מה שהוא עושה, זה תחום אחד שעדיף להימנע מחבלה בו.
בוא נסביר למה הגבול הזה קיים.
מערכת ההפעלה לא יודעת לנהל פרגמטציה על ה-RAM בצורה אופטימלית. (עוד רגע על למה ומה זה בכלל)
לשנות מודל פרגמנטציה בחלונות ישבור בצורה דרסטית Backward comptability לכל אפליקציה בעולם.
יש מערכות הפעלה עם ניהול RAM הרבה יותר טוב משל חלונות מהבחינה האקדמאית של "יותר טוב".
למשל יש מערכת הפעלה בשם VAX וספציפית VMS שיודעת לנהל זכרון מצויין. מערכת הפעלה מבערך שנות ה-80, שאפשר לעבור שנה בלי לאתחל. למה? כי ניהול הזכרון שלהם בשילוב עם GC (בשם Cics) אגרסיבי וסוציומט יודע איך למנוע פרגמנטציה על הדיסק. רק מה? בגלל המודל הזה אי-אפשר כמעט לפתח אפליקציות על המערכת הפעלה הזאת כי היא כל הזמן דופקת לך הפניות זכרון.
מה זה למעשה פרגמנטציה של ה-RAM?
נגיד ואני רוצה לכתוב עכשיו 4096 בתים על ה-RAM.
אז באתי וכתבתי "שלום עולם 1" על ה-RAM.
אז ישר באתי וכתבתי "שלום עולם 2", "שלום עולם 3" ו-"שלום עולם 4".
סה"כ תפסתי 4 פעמים 4KB סה"כ = 16KB.
עכשיו באתי ואמרתי, תפנה בבקשה את "שלום עולם 2".
אז עכשיו יש לי פיזית מקום על ה-RAM שנראה ככה:
4KB תפוס, 4KB לא תפוס, 4KB תפוס, 4KB תפוס.
ועכשיו אני רוצה 16KB על ה-RAM למשהו יחסית גדול.
אז אני אתפוס מקום ישר אחרי ה-"שלום עולם 4" שלי.
אבל רגע - יש לי 4KB פנוי במרכז שם! ובכל זאת שהייתי צריך 16KB ביקשתי את כל ה-16KB ולא 12KB!
למה זה? כי הפניות לזכרון הן פיזית הפניות לנקודה ספציפית בזכרון.
OS נורמלי לא יודע איך לכווץ שטח ב-RAM עם שטח פנוי בתוכו כך שיהיה רק השטח בשימוש.
אם תנסה למשל להעביר את "שלום עולם 3" למקום של "שלום עולם 2", כל ההפניות זכרון אליו ישברו.
מה שה-CICS הזה שהזכרתי עושה זה כן לבצע את ההעברה הזאת ב-RAM ובעקרון הגישה הייתה "אני מעביר, תשימו לב, מקסימום אתם קורסים".
עכשיו למה זה בכלל קשור ל-Cache של ASP.Net?
בוא נגיד ובמקום "שלום עולם X" שמרתי את רשומת לקוח X ב-Cache.
ואז באתי ואמרתי "רגע רגע, לקוח מספר 2 הוא Expire ב-1.1.2008 ולקוח מספר 50 Expire שהרשומה במסד משתנה.".
כנ"ל ה-Session גם עובר תהליך דומה של Expiration לפי פרמטרים של זמן או ניהול אחר.
אז מה קורה לנו? אנחנו מקבלים ב-ASP.Net Cache גם זכרון שיש בו הרבה פרגמנטציה.
כלומר, יש הרבה מקום פנוי שפשוט אי-אפשר לבקש מחדש בין מקום שכן בשימוש.
האחוזים בין סה"כ מקום למקום שלא בשימוש הוא אמפירית 60%.
כלומר 40% מה-ASP.Net Cache הוא לרוב בשימוש.
ולמה זה מעניין בכלל? כי ביחס למערכת ארכיטקטורת מעבדים יש גבול של 2GB על כל הזכרון של Process.
ככה אם יש לי גבול של 2GB שמתוכה ידוע ש-60% הוא זכרון ריק, אני אגביל את הזכרון בשימוש ל-800 מגה.
יש מקומות בקונפיוגרציה שאפשר לשנות את זה (ברמת ה-web.config או ה-app.config) ב-<processmodal memorylmit> ו-<cache privateBytesLimit>. אבל סה"כ, אבל לראות פיזית את הפרגמנטציה בזמן אמת, להבין על מה מביטים, אסור לגעת בזה. כיוון לא טוב של הנתון הזה (או אפליקציה שיוצרת יותר מדי פרגמנטציה על ה-RAM של ה-Cache) תגרום ל-OutOfMemoryException שתפיל הכל.
ככל שארכיטקטורת המעבדים מתקדמת, והגבול העליון של GB פר Process עולה וכמות המעבדים עולה - ככה אפשר לשחק יותר עם המספר הזה. אבל אין התנהגות ברירת מחדל לפי ידיעתי שונה.
עוד סיבה מעבר לפרגמנטציה של ה-RAM הוא זה שה-GC נכנס לחרדת נטישה שה-Process מתקרב לגבול העליון של ה-Virtual RAM Claim והופך להיות מאוד אגרסיבי. ארגסיביות של ה-GC גורמת לו לעשות הרבה מעברים על עץ האובייקטים שפיזית לוקח הרבה מאוד CPU וגם יכולה לדפוק את ה-Cache גם ככה בזה שהיא מוציאה ממנו אלמנטים מחוץ ל-Expiration Policy.
סה"כ הגבול הגיוני ואמור להיות שם.
שאלת המשך #2: (על שמירת נתונים InProc/OutProc/Sql)
לגבי מה שלא InProc, זה פיתרון נחמד להרבה מאוד דברים, אבל קח בחשבון שאין כמו לגשת לזיכרון הזמין ביותר בשרת, זה תמיד יותר מהיר מSQL או אפילו שרת יעודי לנושא.
תשובת המשך #2:
לא מדוייק.
כשה-RAM עובד פרגמנטציה פיזית הוא מתחיל גם להיות איטי, מעאפן ולא אמין.
למה באמת יש את ההגבלה הזאת של 800 מגה על ה-Cache פר מעבד?
ה-RAM הוא פיזית מכות חשמל של 0 ו-1 על חתיכת פלסטיק מתוחכם.
מסתבר שיש יותר מדי מידע שלנו שם, המידע מתחיל להיות מעורבב, לא ברור ואף במקרים קיצוניים לא אמין. אז הוא הופך לאיטי.
אז כאן יש גורם חזק של האטה על ה-RAM.
ה-OutProc יכול גם לשבת על ה-RAM של אותה מכונה, אבל לתת הקצאה נפרדת של RAM ספציפית רק ל-Session בלי קשר לדוט נט וגם לא להיות תלויה ב-IIS וב-AppDomain של האתר.
אז אתה מקבל כאן את הטוב של שתי העולמות.
בנושא ה-SQL שלכאורה איטי ביחס ל-RAM. אפשר תמיד לקנות לשרת RAM Disk (מה שנקרא: flash ram disk בעגה מיסחרית), להוסיף עליו Data file של סיקוול סרבר שכל מה שהוא עושה זה להחזיק את הטבלה של ה-Session ולסגור עניין.
ואז אתה גם עובד עם מהירות של RAM ומקבל את הנוחות של Persistent Memory.
אגב, זה בכלל גם הפתרון הנכון שיש לך כמה טבלאות ספציפיות ש"נטחנות".
אם יש לך כמה טבלאות של High Volume ולכאורה מסד נתונים עובד עליהן יותר מדי לאט, אפשר כמוצא אחרון להתקין RAM Disk על השרת, לבודד את הטבלאות ל-Data File שיושב על אותו דיסק ופתאום הכל מתחיל לטוס. מניסיון, זה פתרון אדיר שמומלץ בחום למצבים ספציפיים.
ומעבר לזה, ייתכן גם כאשר הנתונים יושבים ב-SQL לבצע ל-Custom Session Provider אופטימזציה שהוא ישתמש באיזה מנגנון Caching פנימי בסגנון MRU/LUR (קיצור ל: Most/Last Recently Used) שידע איזה מידע להחזיק בזכרון ואיזה מידע להחזיק בדיסק. זה אגב, בדיוק מה שמסדי נתונים מבצעים לעצמם והם יודעים לעשות לעצמם את האופטימיזציה הזאת.
וגם אם מדובר על OutProc/Sql שיושב על שרת מרוחק, תמיד אפשר לעבוד בפרוטוקול UDP שהופך את הגישה למאוד מאוד מהירה.
הכל תלוי כמה אתה מוכן להשקיע כדי לפתור את הבעיה, ויותר חשוב - כמה אתה צריך להשקיע.
כלומר, מה באמת מידת העומס והדרישות הטכניות לעומת מה הפתרון המתאים ביותר.
קישור: (המשך הדיון מומלץ בחום) http://www.tapuz.co.il/tapuzforum/main/viewmsg.asp?forum=831&msgid=105661782