cache_manager module
מנהל Cache מתקדם עם Redis Advanced Cache Manager with Redis
- class cache_manager.DynamicTTL[מקור]
Bases:
objectניהול TTL דינמי לפי סוג תוכן וקונטקסט.
הערכים כאן מייצגים TTL בסיסי בשניות עבור סוגי תוכן שכיחים.
-
BASE_TTL:
Dict[str,int] = {'bookmarks': 120, 'collections_detail': 30, 'collections_items': 180, 'collections_list': 60, 'file_content': 3600, 'file_list': 300, 'markdown_render': 1800, 'public_stats': 600, 'search_results': 180, 'settings': 60, 'sticky_summary': 60, 'tags': 300, 'user_stats': 600}
-
BASE_TTL:
- cache_manager.build_cache_key(*parts)[מקור]
בניית מפתח cache יעיל ומובנה מהחלקים הנתונים.
מסנן חלקים ריקים
ממיר לתווים בטוחים (רווחים/סלאשים)
מגביל אורך ומוסיף hash קצר במידת הצורך
- class cache_manager.CacheManager[מקור]
Bases:
objectמנהל Cache מתקדם עם Redis
- enable_debug_for(seconds)[מקור]
הפעל/הארך חלון דיבאג זמני ללוגים של HIT/MISS/SET.
אם seconds <= 0: מכבה דיבאג (debug_until=0)
אחרת: מאריך (לא מקצר) את החלון כך שיסתיים לפחות בעוד seconds שניות מהעכשיו
מחזיר את timestamp החדש של debug_until.
- set_dynamic(key, value, content_type, context=None)[מקור]
שמירה ב-cache עם TTL דינמי ותיעוד מינימלי במטריקות/לוגים.
- get_with_refresh(key, refresh_func, *, content_type, context=None)[מקור]
קריאה מ-cache; אם חסר – מחשב, שומר דינמית ומחזיר.
- delete_pattern(pattern)[מקור]
מחיקת כל המפתחות שמתאימים לתבנית, ב-Redis ובפולבק המקומי גם יחד.
מקרה פרטי של
delete_patterns()עם דפוס אחד, וכל התיעוד שם חל גם כאן. נשארת כי היא ה-API שרוב הקוראים בריפו משתמשים בו.
- delete_patterns(patterns)[מקור]
מחיקת כל המפתחות שמתאימים ל**אחד** מהדפוסים, בקריאה אחת.
הפולבק המקומי מנוקה תמיד ולפני בדיקת
is_enabled: הוא מאוכלס דווקא כש-Redis אינו זמין, ולכן דילוג עליו כאן משאיר נתונים ישנים בזיכרון עד שה-TTL פג — גם אחרי כתיבה שהצליחה.סריקה נפרדת לכל דפוס, עם ``MATCH`` בצד השרת. גרסת ביניים של הפונקציה הזו הריצה סריקה אחת בלי
MATCHוסיננה בפייתון, ונמדדה מול Redis 7 עם 200,000 מפתחות (flask-limiter, סשנים וקאש חולקים אותו מסד):delete_patternעם דפוס יחיד משך 200,007 מפתחות לתהליך כדי למחוק אחד. יש בריפו עשרות קוראים כאלה, וב-MCP הקריאה רצה על ה-worker היחיד של_WRITE_POOL— כלומר עדכון תיאור אחד חסם כל כתיבה אחרת למשך המשיכה.MATCHאינו מקצר את המעבר של Redis על ה-keyspace, אבל הוא קובע מה חוצה את הרשת ומה מעובד כאן: עםmatchחוזרים רק המפתחות שמתאימים. מספר ה-roundtrips לתבנית הוא DBSIZE /_SCAN_COUNT, וזה — כפול ה-RTT — מה שקובע את זמן הקריאה בייצור; הנימוק למספר כתוב על הקבוע.מה כן מאוחד: תקציב הזמן, ה-batch, והספירה. הצורה המקורית קראה ל-
delete_patternבלולאה וכל קריאה קיבלהCACHE_DELETE_PATTERN_BUDGET_SECONDSמשלה — עשרה דפוסים יכלו לחסום עד 50 שניות. כאן הדדליין אחד לכל הקריאה.מפתח שהותאם לעולם אינו נזרק. בצורה הקודמת, כשהתקציב נגמר, ה-
batchהחלקי — עד 199 מפתחות שכבר עברו התאמה — נזרק בליDEL, והתוצאה הייתה בדיוק הקאש הישן שהניקוי בא להסיר. כאן ה-batch נשטף לפני כל יציאה, כולל מיצוי תקציב וחריגה; ומיצוי תקציב נרשם ב-WARNINGעם מספר הדפוסים שהושלמו, כי ניקוי חלקי שנראה כמו ניקוי מלא הוא הכשל השקט של הפונקציה הזו.שני סירובים שהם שגיאת קורא, ולכן זורקים ולא נבלעים:
מחרוזת במקום רשימה —
TypeError.strעומד ב-Sequence[str], ו-[str(p) for p in "file_content:*"]מתפרק לדפוס לכל תו; התו*לבדו מתאים ל**כל** מפתח. טעות של תו אחד ביןdelete_patternל-delete_patternsהייתה מוחקת את המסד כולו, כולל מוני ה-rate-limit.דפוס שמתאים לכל מפתח (
*,**,?*— בלי תו מילולי כלל) —ValueError. ניקוי של כל הקאש עובר דרךclear_all(), שעושה זאת במפורש ובמבוקר, ולא דרך כאן. הקריטריון הוא ”אין תו מילולי בכלל“ ולא ”אין תו מילולי לפני הכוכבית הראשונה“:*:user:{uid}:*ב-invalidate_user_cache()מתחיל בכוכבית ואינו מתאים לכל מפתח — הוא דורש:user:{uid}:— והצורה השנייה הייתה פוסלת אותו.
על הערך המוחזר: המספר סופר רק מה שנמחק בתהליך הזה. שתי מגבלות שהקורא חייב להכיר, כי אף אחת מהן לא משתקפת במספר:
הפולבק המקומי הוא פר-תהליך. עם יותר מ-worker אחד, ניקוי כאן לא נוגע בעותקים של ה-workers האחרים. נתון שאסור להגיש ישן — למשל מספר גרסה — לא יכול להסתמך על הקאש הזה בכלל, בשום מצב.
כש-Redis אינו זמין, 0 אינו מבחין בין ”לא היה מה למחוק“ לבין ”לא יכולתי לגשת“. לכן המצב נרשם ללוג פעם אחת במקום להיבלע.
- clear_all()[מקור]
ניקוי כל המטמון באופן מבוקר.
הפולבק המקומי מנוקה תמיד, גם כש-Redis מושבת. אחרת ”נקה הכל“ משאיר בדיוק את הקאש שפעיל כשאין Redis.
אם Redis פעיל – מוחק גם את כל המפתחות שלו באמצעות SCAN+DEL (best-effort).
- Return type:
ביטול קאש לפי קובץ: תוכן/רינדור/רשימות.
דפוסים נפוצים מעוגנים לאחור בהתאם למפתחות הקיימים בקוד.
אין כאן יציאה מוקדמת על
is_enabled:delete_patternמנקה גם את הפולבק המקומי, שקיים דווקא כש-Redis כבוי. דילוג כאן היה משאיר את הערכים הישנים בזיכרון.
- clear_stale(max_scan=1000, ttl_seconds_threshold=60)[מקור]
מחיקת מפתחות שכבר עומדים לפוג (”stale“) בצורה עדינה.
היגיון: - הפולבק המקומי מנוקה תמיד מפגי-תוקף, גם כש-Redis מושבת. - אם Redis מושבת – מחזיר את מה שנוקה מקומית בלבד. - סריקה מדורגת (SCAN) של עד max_scan מפתחות. - מחיקה רק למפתחות עם TTL חיובי קטן מ-ttl_seconds_threshold, או TTL שלילי המציין שאינו קיים. - לא מוחקים מפתחות ללא TTL (ttl == -1) כדי להימנע מפגיעה בקאש ארוך-חיים.
- cache_manager.dynamic_cache(content_type, key_prefix=None)[מקור]
דקורטור ל-caching דינמי ל-Flask endpoints.
בונה מפתח קאש יציב הכולל משתמש/נתיב/פרמטרים
שומר רק טיפוסים serializable; עבור Response עם JSON שומר את ה-data בלבד
Fail-open: לעולם לא מפיל endpoint על בעיות קאש